RascalTutor
Synopsis

Reference manual for the Rascal meta-programming language.

Description

Here we describe all features of the Rascal language in detail, for reference purposes.

  • Declarations: The entities that can be declared in Rascal programs.

  • Patterns: Patterns are a notation for pattern matching used to detect if a value has a certain shape,.

  • Expressions: The expressions available in Rascal.

  • Statements: All Rascal statements.

You can find more (accessible) information related to Rascal here:

  • Why Rascal: gives the motivation for the Rascal language and describes various usage scenarios.

  • Getting Started describes how to download, install and start Rascal.

  • Getting Help shows how to use the help facilties and search the documentation. See Further Reading for a reading guide.

  • Library functies are described in Rascal Libraries.

  • We do not maintain a list of frequently asked questions (faq) but use StackOverflow instead.

Examples
  • We show examples for each Rascal language construct described here.

  • You can find many simple code examples in Recipes.

  • The Rascal Libraries contain examples for most functions.

Benefits
  • Rascal has everything you need for any kind of (meta-)program you want to write. See Why Rascal.

Pitfalls
  • Rascal is a procedural/functional language with immutable data. Do not confuse this with object-oriented programming.

  • Rascal allows you to write highly imperative code, but it has declarative constructs that lead to shorter and more readable code.

1. Declarations

Synopsis

The entities that can be declared in Rascal programs.

Description

The following concepts are relevant for declarations:

1.1. Module Declaration

Synopsis

Declare a module.

Syntax
module Name
Imports;
Declaration1;
...
Declarationn;
Description

A module declaration consists of:

  • A module name.

  • Zero or more imports;

  • Zero or more declarations.

The module name Name will be used when the current module is imported in another module. A module name is in general a qualified name of the form:

Name1::_Name2:: ... ::_Namen

which corresponds to a path relative to the root of the current workspace.

The constituents of a module are shown in the figure below.

Module Parts

An Import declares other modules that are used by the current module. Following imports, a module may contain declarations (in arbitrary order, but a Syntax Definition can occur directly following the imports) for:

Each declaration may contain a private or public keyword that determines the visibility of the declared entity.

The entities that are visible inside a module are

  • The private or public entities declared in the module itself.

  • The public entities declared in any imported module.

The only entities that are visible outside the module, are the public entities declared in the module itself. If different imported modules declare the same visible name, it can be disambiguated by explicitly qualifying it with its module name:

Module :: Name

Each module resides in a separate file with extension .rsc.

Examples

Here is the Hello module:

module demo::basic::Hello

import IO;

void hello() {
   println("Hello world, this is my first Rascal program");
}

It defines a module with the name demo::basic::Hello and imports the IO library. Finally, it declares the hello function.

The actual source of this module can be found in library/demo/basic/Hello.rsc in the Rascal sources.

More ways to write this example are discussed in the Hello example in Recipes.

1.2. Import

Synopsis

Declare the import a module.

Syntax

import QualifiedName;

Description

An import has as effect that all public entities declared in module QualifiedName are made available to the importing module. Circular imports are allowed. All publicly visible entities in the imported module become available in the importing module.

Import is non-transitive, i.e., the visible entities from an imported module are not re-exported by the importing module.

Examples

Here, is how to import the IO library:

rascal>import IO;
ok
rascal>println("IO library was imported.");
IO library was imported.
ok

1.3. Program

Synopsis

A Rascal program consists of a number of Module Declarations.

Description

A Rascal program consists of a number of Module Declarations, each stored in a separate file with extension .rsc.

1.4. StaticTyping

Synopsis

The static type system of Rascal.

Description

Rascal is based on static typing, this means that as many errors and inconsistencies as possible are spotted before the program is executed.

1.4.1. The Type Lattice

The types are ordered in a so-called type lattice shown in the following figure.

type lattice
Figure 1. Type Lattice

The arrows describe a subtype-of relation between types. The type void is the smallest type and is included in all other types and the type value is the largest type that includes all other types. We also see that rel is a subtype of set and that each ADT is a subtype of node. A special role is played by the datatype Tree that is the generic type of syntax trees. Syntax trees for specific languages are all subtypes of Tree. As a result, syntax trees can be addressed at two levels:

  • in a generic fashion as Tree and,

  • in a specific fashion as a more precisely typed syntax tree. Finally, each alias is structurally equivalent to one or more specific other types.

The fact that the types are ordered in a lattice makes it possible to define a Least Upper Bound (lub) on types. Given two types T1 and T2, lub(T1, T2) is defined as the nearest common super type of T1 and T2 in the type lattice.

1.4.2. Advanced Features

The Rascal type system has various advanced features that are described separately:

  • Types may be be parameterized resulting in very general and reusable types, see [Type Parameters].

  • Declarations of [Function]s and [AlgebraicDataType]s may be parameterized and [Type Constraints] can be used to define constraints on the actual type to be used.

  • The formal arguments of functions are bound to values but in exceptional cases a function may need a type as argument value, Reified Types make this possible.

Examples

Here are some simple examples of correct and incorrect typing:

We can assign an integer value to an integer variable:

rascal>int i = 3;
int: 3

But assigning a string value gives an error:

rascal>int j = "abc";
|prompt:///|(4,9,<1,4>,<1,13>): Expected int, but got str
Advice: |http://tutor.rascal-mpl.org/Errors/Static/UnexpectedType/UnexpectedType.html|
 
ok

The num type accepts integer and real values:

rascal>num n = i;
num: 3
rascal>n = 3.14;
num: 3.14

A variable of type value accepts all possible values:

rascal>value v = true;
value: true
rascal>v = "abc";
value: "abc"
rascal>v = [1, 2, 3];
value: [1,2,3]

1.4.3. Reified Types

Synopsis

Reified types are types that can be used as values.

Syntax

# Name

Types

type

Description

Usually one declares functions that have arguments that have a type that corresponds to one of the many forms of values in Rascal. In exceptional circumstances it is desirable to define functions that have a type itself as argument.

To solve this problem in a more general manner something special has to be done. Types are not values and without an additional mechanism they cannot be passed as arguments to functions. To achieve this effect we introduce reified types that are denoted by the type type. In other words, reified types make it possible to use types as values.

Examples

The prototypical example is a parse function: how to write a type safe parse function that expresses the type of the result we expect? Suppose we want to parse a language that has the non-terminals EXP, STAT and PROGRAM. A first, naive, solution introduces a parse function for each non-terminal:

EXP parseEXP(str s){ ... }
STAT parsePROGRAM(str s) { ... }
PROGRAM parsePROGRAM(str s) { ... }

Unfortunately this solution does not scale well to large languages with many non-terminals and it breaks down completely when we do not know the non-terminals before hand.

Now we can write (see Type Parameters for a description of the &T notation):

&T parse(type[&T] start, str s) { ... }

and use the parse by giving it a type as argument:

parse(#EXP, "1+3");

1.4.4. Type Constraints

Synopsis

Type constraints restrict the acceptable type for parameters.

Syntax

& Name <: Type

Description

Constraints can be imposed on the actual types to which a type parameter may be bound. This is expressed by a subtype constraint which expresses that actual types bound to Name should be a subtype of Type.

Examples

Here is the definition of the absolute value function abs from the Number library:

public &T <: num abs(&T <: num N)
{
	return N >= 0 ? N : -N;
}

The argument N is constrained to be at most of type num.

rascal>import util::Math;
ok
rascal>abs(-3);
int: 3
rascal>abs(-3.5);
real: 3.5

Here is an example from the Node library:

&T <: node setAnnotations(&T <: node x, map[str, value] annotations);

(we don’t give the body of this function since it has been implemented in Java). setAnnotations takes a value of any type that is at most node and adds annotations to it. This makes it possible to set annotations on any Algebraic Data Type.

rascal>import Node;
ok
rascal>nd = "f"(10, "abc");
node: "f"(10,"abc")

First we apply setAnnotations to a node value:

rascal>setAnnotations(nd, ("color" : "red", "size" : "large"));
node: "f"(10,"abc",
  size="large",
  color="red")

Next, we introduce a new data type Suite:

rascal>data Suite = hearts() | diamonds() | clubs() | spades();
ok
rascal>st = diamonds();
Suite: diamonds()

And apply setAnnotations to a value of type Suite:

rascal>setAnnotations(st, ("player" : "Hossein", "gain" : "120"));
Suite: diamonds(player="Hossein",gain="120")

1.4.5. Type Parameters

Synopsis

Type parameters enable parameterized types.

Syntax

& Name

Description

A type parameter may occur at every syntactic position where a type is required and turns an ordinary type into a parameterized type. Parameterized types are used to define polymorphic functions and data types, i.e., functions and data types that are applicable for more than one type. Type parameters are bound to an actual type when the function or data type is applied and further uses of the type parameter are consistently replaced by the actual type.

The following syntactic positions are binding occurrences for type parameters:

  • Type parameters in the type declaration of a function are bound to the types of the actual parameters in the call of that function. Type parameters that occur in the body of the function are replaced by the corresponding actual types.

  • The left-hand side of an alias. The type parameters are bound when the alias is used and occurrences of type parameters in the right hand side are replaced by corresponding actual types.

  • The alternatives of a data type. Binding and replacement is identical to that of function declarations.

All other occurrences of type parameters are using occurrences. The following rules apply:

  • When the same type parameter is used at different binding occurrences it should be bound to the same actual type.

  • For every using occurrence of a type parameter there should be a binding occurrence of a type parameter with the same name.

Examples

Let's consider a small example of the use of function parameters in a function declaration, see Function Declaration for more details on function declarations. The following function swap returns a tuple in which its arguments are swapped and can be applied to arbitrary values in a type safe manner:

rascal>tuple[&B, &A] swap(&A a, &B b) { return <b, a>; }
tuple[&B,&A] (&A, &B): function(|prompt:///|(0,49,<1,0>,<1,49>))
rascal>swap(1,2);
tuple[int,int]: <2,1>
rascal>swap("abc", 3);
tuple[int,str]: <3,"abc">

Observe that the type parameters that are used in the return type should be defined in the declarations of the formal parameter of the function.

An [Alias] declaration may also be parameterized. So we can generalize graphs as follows:

alias Graph[&Node] = rel[&Node, &Node];
Graph[int] GI = {<1,2>, <3,4>, <4,1>};
Graph[str] GS = {<"a", "b">, <"c","d">, <"d", "a">};

The type parameters that are used in the type in the right part of the alias declaration should be defined in the left part of the alias definition.

1.5. Algebraic Data Type

Synopsis

Define a user-defined type (Algebraic Data Type).

Description

In ordinary programming languages record types or classes exist to introduce a new type name for a collection of related, named, values and to provide access to the elements of such a collection through their name.

In Rascal, algebraic data types provide this facility. They have to be declared, and then values can be declared using calls to the declared constructor functions, see Constructor.

Examples

The following data declaration defines the datatype Bool that contains various constants (tt() and ff() and constructor functions conj and disj.

rascal>data Bool = tt() | ff() | conj(Bool L, Bool R)  | disj(Bool L, Bool R);
ok

terms of type Bool can be constructed using the defined constructors:

rascal>conj(tt(),ff());
Bool: conj(
  tt(),
  ff())

1.6. Variable Declaration

Synopsis

Declare a variable.

Syntax
  • Type Name = Exp ;

  • Type Name;

Types

Type

Exp

Type

<: Type

Description

The effect of a variable declaration is to introduce a new variable Name and to assign the value of expression Exp to Name. A mention of Name later on in the same scope will be replaced by this value, provided that Name\'s value has not been changed by an intermediate assignment.

When a variable is declared, it has as scope the nearest enclosing block, or the module when declared at the module level.

The following rules apply:

  • Double declarations in the same scope are not allowed.

  • The type of Exp should be compatible with Type, i.e., it should be a subtype of Type.

As a convenience, also declarations without an initialization expression are permitted inside functions (but not at the module level) and have the form

Type Name;

and only introduce the variable Name.

Rascal provides local type inference, which allows the implicit declaration of variables that are used locally in functions. The following rules apply:

  • An implicitly declared variable is declared at the level of the current scope, this may the whole function body or a block nested in it.

  • An implicitly declared variable gets as type the type of the first value that is assignment to it.

  • If a variable is implicitly declared in different execution path of a function, all these implicit declarations should result in the same type.

  • All uses of an implicitly declared variable must be compatible with its implicit type.

Examples

Two explicit variable declarations:

rascal>int max = 100;
int: 100
rascal>min = 0;
int: 0

An implicit variable declaration

rascal>day = {<"mon", 1>, <"tue", 2>, <"wed",3>,
>>>>>>>       <"thu", 4>, <"fri", 5>, <"sat",6>, <"sun",7>};
rel[str,int]: {
  <"thu",4>,
  <"tue",2>,
  <"sat",6>,
  <"wed",3>,
  <"fri",5>,
  <"sun",7>,
  <"mon",1>
}

Variable declaration and assignment leading to type error

rascal>int month = 12;
int: 12
rascal>month ="December";
|prompt:///|(7,10,<1,7>,<1,17>): Expected int, but got str
Advice: |http://tutor.rascal-mpl.org/Errors/Static/UnexpectedType/UnexpectedType.html|
 
ok
Pitfalls
  • Local type inference for variables always uses the smallest possible scope for a variable; this implies that a variable introduced in an inner scope is not available outside that scope. Here is how things can go wrong:

rascal>if( 4 > 3){ x = "abc"; } else { x = "def";}
str: "abc"
rascal>x;
|prompt:///|(0,1,<1,0>,<1,1>): Undeclared variable: x
Advice: |http://tutor.rascal-mpl.org/Errors/Static/UndeclaredVariable/UndeclaredVariable.html|
 
ok

1.7. Function Declaration

Synopsis

Declare a function.

Syntax
  • Modifiers Type Name( Type1 Var1, …​, Typen Varn ) Body

  • Modifiers Type Name( Type1 Var1, …​, Typen Varn Type0 Name0…​ ) Body

  • Modifiers Type Name( Pattern1, …​, Patternn) Body

  • Modifiers Type Name( Pattern1, …​, Patternn, Type0 Name0…​) Body

where Body is one of:

  • { Statements }

  • throws Exception1, Exception2, …​ { Statements }

  • = Expression;

and where Modifiers may be:

  • ("public" | "private")? ("java" | "test" | "default")?

1.7.1. Variant 1

Description

A function declaration introduces a new function with name name, typed formal parameters Type1 Var1, a return type Type and a Statement that forms the function body. The type of Statement should be equal to Type.

The formal parameters may be used in Statement and get their value when function Name is invoked.

1.7.2. Variant 2

A function may have a variable list of arguments, this has as syntax variant 2 given above.

The last parameter of a function may be followed by …​ and this has as effect that all remaining actual parameters that occur in a call to this function are collected as list value of the last formal parameter. Inside the function body, the type of this parameter will therefore be list[Type0].

1.7.3. Variant 3 and 4

All formal parameter of a function can be Patterns. There are some restrictions however:

  • A Pattern in formal parameter positions may not refer to variables in the scope.

  • Patterns in formal parameter positions may not introduce fresh variables without an explicit type.

  • The last parameter, if followed by …​ can only be a normal typed parameters, not just any pattern.

1.7.4. Body types

  • Functions with list of statements as bodies must eventually use return or fail on every control flow path.

  • The declarations to throw an exception are documentation only

  • Single expressions can be bodies of functions, the return value is the value of the expression.

1.7.5. Parameterized types in function declaration

The types that occur in function declarations may also contain Type Parameters. In this way functions can be defined for arbitrary types. The type variable is bound (statically) at by the types of the parameters given at location of the call. The result type must be used at least once in any of the parameters.

1.7.6. Overloading

Function definitions may be overloaded, i.e. a function with the same name may be defined twice and a function may redefine a constructor of an Algebraic Data Type or a Syntax Definition.

There are some restrictions however:

  • Overloaded alternatives for the same function name but with different patterns must return the same type.

  • Overloaded alternatives for the same function name must have mutually exclusive patterns, unless one alternative is labeled default and the other is not. The patterns of formal parameters are mutually exclusive if for at least one parameter position:

    • They range over incomparable types, as in int f(int a) and int f(real a), or

    • They range over different alternatives of an Algebraic Data Type, as in int f(and(Bool a, Bool b)) and int f(or(Bool a, Bool b))

    • They range over different alternatives of a Syntax Definition

    • And note that deep matches using the / alternative are considered to be of type value and therefore overlap with all other patterns.

  • Overlapping patterns are allowed if the one alternative has the default modified while the other does not.

  • If a function is fallible, it uses the fail statement to back-track to a different alternative, then there must be a default alternative defined which can handle the general case. An [AlgebraicDataType] or a [SyntaxDefinition] with the same name and return type counts as a default alternative.

  • default functions may not fail.

1.7.7. Modifiers

The Modifiers affect visibility and special behaviour of functions:

  • Visibility: private declares that a function is only visible in the current module. public declares that it is visible outside the module as well. When visibility is not specified, private is assumed.

  • Special Behaviour:

    • java declares that the body of the function is implemented in Java. The function should have a javaClass annotation that determines where the Java implementation can be found.

    • test declares that this is a test function. A test function is a boolean function (currently) without arguments. It can be called as any other function. However, it can also be called automatically by the unit test framework, by typing :test at the command line, see [Help].

    • default declares an alternative for an overloaded function that will only be tried after all non-default alternatives have been tried. Note that Algebraic Data Types and Syntax Definitions implicitly define default functions that may be overloaded by normal Function Declarations.

Examples

Declare a function

rascal>rel[int, int] invert(rel[int,int] R){
>>>>>>>   return {<Y, X> | <int X, int Y> <- R };
>>>>>>>}
rel[int,int] (rel[int,int]): function(|prompt:///|(0,82,<1,0>,<3,1>))

Call it

rascal>invert({<1,10>, <2,20>});
rel[int,int]: {
  <10,1>,
  <20,2>
}

In the following example we illustrate the use of type variables in function declarations. Declare an inversion function that is applicable to any binary relation:

rascal>rel[&T2, &T1] invert2(rel[&T1,&T2] R){
>>>>>>>   return {<Y, X> | <&T1 X, &T2 Y> <- R };
>>>>>>>}
set[tuple[&T2,&T1]] (set[tuple[&T1,&T2]]): function(|prompt:///|(0,85,<1,0>,<3,1>))

Now apply it to relations with different types:

rascal>invert2({<1,10>, <2,20>});
rel[int,int]: {
  <10,1>,
  <20,2>
}
rascal>invert2({<"mon", 1>, <"tue", 2>});
rel[int,str]: {
  <1,"mon">,
  <2,"tue">
}

As another example declare a function that can be used to swap the elements of pairs of arbitrary types (also see Tuple Subscription):

rascal>tuple[&T2, &T1] swap(tuple[&T1, &T2] TP) { return <TP[1], TP[0]>;}
tuple[&T2,&T1] (tuple[&T1,&T2]): function(|prompt:///|(0,66,<1,0>,<1,66>))
rascal>swap(<1, 2>);
tuple[int,int]: <2,1>
rascal>swap(<"wed", 3>);
tuple[int,str]: <3,"wed">

Here we use an overloaded definition with incomparable patterns:

rascal>int f(int i) = 1;
int (int): function(|prompt:///|(0,17,<1,0>,<1,17>))
rascal>int f(real r) = 2;
int (real): function(|prompt:///|(0,18,<1,0>,<1,18>))
rascal>f(0);
int: 1
rascal>f(0.0);
int: 2

And we may use default, as in:

rascal>int f(0) = 1;
int (int): function(|prompt:///|(0,13,<1,0>,<1,13>))
rascal>default int f(int n) = n * f(n - 1);
int (int): function(|prompt:///|(0,36,<1,0>,<1,36>))
rascal>f(0);
int: 1
rascal>f(2);
int: 2

In combination with an Algebraic Data Type, which defines default functions implicitly for every alternative, we can define canonicalization functions. The same holds for Syntax Definitions, see Actions.

Pitfalls

In case of overlapping function definitions, the order in which the functions are tried is left undefined. The only exceptions are functions marked default, those will be tried after non-default functions.

1.8. Syntax Definition

Synopsis

Syntax Definitions allow the definition of parsers for programming languages, domain-specific languages and data formats.

Syntax
  • Start syntax Nonterminal = Alternatives;

  • lexical Nonterminal = Alternatives;

  • layout Nonterminal = Alternatives;

  • keyword Nonterminal = Alternatives;

where Start is either start or nothing, and Alternatives are one of:

  • Tags Associativity Symbols

  • Tags Associativity Name : Symbols

  • Associativity ( Alternatives )

  • Alternatives1 | Alternatives2

  • Alternatives1 > Alternatives2

where Associativity is nothing, or one of assoc, left, right or non-assoc, and Tags are a possibly empty list of tags.

Description

Rascal supports full context-free grammars for syntax definition. It generates scannerless parsers from these definitions. These parsers produce Parse Trees that can be further processed by Rascal using Concrete Syntax fragments in Patterns and Expressions, or they can be imploded to Algebraic Data Types.

There are four kinds of non-terminals that can be defined with slightly different characteristics.

  • Syntax non-terminals are general context-free non-terminals. This mean left-recursion, right-recursion, any of the regular expression Symbols and all kinds of Disambiguation can be used to define it. It is important to note that in between the Symbols that define a syntax non-terminal the locally defined layout non-terminal will be interleaved. For example, if you define layout ML = [\ ]*; and syntax A = "a" "a", Rascal will modify the definition of A to syntax A = "a" ML "a"; before generating a parser.

  • Lexical non-terminals are just like syntax non-terminals, very much like syntax non-terminals. However, the definition of a lexical is not modified with interleaved layout non-terminals. And, the structure of lexicals is not traversed by the Visit statement and equality is checked between lexicals by checking the characters (not its structure) for equality.

  • Layout non-terminals are just like syntax non-terminals as well. However, they are used to preprocess all syntax definitions in the same module scope (see above).

  • Keyword non-terminals are not like syntax non-terminals. These only allow definition of enumeration of literal symbols and single character classes. Keyword non-terminals play an important role in the semantics of Disambiguation where some disambiguation constructs require finite, non-empty enumeration of strings. The prime example is the definition of reserved keywords.

Each alternative of a syntax definition is defined by a list of Symbols. Each of the Symbols can be labeled or not. The alternative of a defined syntax type may be labeled or not as well. With the label additional operations are activated on the corresponding parse trees:

  • The is operator is defined for labeled alternatives (see Operators).

  • The has operator is defined for labeled Symbols in the right-hand side (see Operators).

  • Action functions can be written to override the construction of a parse tree, using the label of an alternative as the function name

  • [implode] uses labeled alternatives to map to an Algebraic Data Type

Alternatives can be combined in a single Syntax Definition using the |, > and associativity combinators. The latter two represent Disambiguation constructs that you should read more about. The | is a short-hand for not having to repeat syntax A = for every alternative of A.

Alternatives can be named or not. The names are essential only if:

  • you need to implode Parse Trees

  • you need to use the is expression, as in myStatement is ifThenElse instead of using concrete pattern matching.

  • you want to write Actions that triggers on the construction of the alternative.

However, it is generally a good idea to name your rules even if you do not need them. Note that a name may be reused for different alternatives for a single non-terminal, provided that the lists of symbols for these "overloaded" alternatives use different non-terminal symbols. This implies that alternatives for lexicals generally do not use overloaded names because they are often defined only by regular expressions over terminal Symbols (literals and character classes).

The start modifier identifies the start of a grammar. The effect of a start modifier is that Rascal will generate an extra syntax definition before generating a parser that allows layout to before and after the start non-terminal. For example:

layout L = [\ ]*; start Program = Statement*;`

will produce syntax start[Program] = L Program top L;. Note that the start[Program] type is now available in your program, and Parse Trees assigned to variable of that type will allow access to the top field.

Examples

The following example makes use of practically all of the Syntax Definition features, except parse actions.

// layout is lists of whitespace characters
layout MyLayout = [\t\n\ \r\f]*;

// identifiers are characters of lowercase alphabet letters,
// not immediately preceded or followed by those (longest match)
// and not any of the reserved keywords
lexical Identifier = [a-z] !<< [a-z]+ !>> [a-z] \ MyKeywords;

// this defines the reserved keywords used in the definition of Identifier
keyword MyKeywords = "if" | "then" | "else" | "fi";

// here is a recursive definition of expressions
// using priority and associativity groups.
syntax Expression
  = id: Identifier id
  | null: "null"
  | left multi: Expression l "*" Expression r
  > left ( add: Expression l "+" Expression r
         | sub: Expression l "-" Expression r
         )
  | bracket "(" Expression ")"
  ;
Benefits
  • Modular and compositional.

  • No grammar normalization or grammar factoring necessary.

  • Generate a parser for any context-free grammar.

  • Generate parsers are really fast (for general parsers).

  • Powerful disambiguation constructs for common programming language disambiguation patterns.

  • Data-dependent (context-sensitive) disambiguation via arbitrary functions.

  • Embedding of concrete syntax fragments in Rascal programs

  • Syntax Definitions follow the syntax and semantics of Algebraic Data Types quite closely.

Pitfalls

1.8.1. Action

Synopsis

Actions are functions that are called when parse trees are constructed (right after parsing).

Description

A so-called Action is a normal rascal Function Declaration that overloads a Syntax Definition. A Syntax Definition, very similar to Algebraic Data Type definitions, defines a constructor for a parse tree node. This constructor is the default function, and when it is overloaded by a non-default function this overloaded function will be tried first. You can overload any labeled Syntax Definition using the name of an alternative.

For example:

syntax A = a: B  C;

public A a(B b, C c) {
  return f(b, c);
}

In this example Action function the a is replaced by whatever A the f function returns.

Actions are executed every time a parse tree is constructed:

  • Right after parsing.

  • On the way back from a visit statement.

  • When a Concrete Syntax expression is executed.

  • When Parse Trees are constructed "manually".

They can be used as a Disambiguation method, using the filter statement, as in:

syntax E = id: Id i;
set[Id] types = {};

public E id(Id i) {
  if (i in types)
    filter; // remove this parse tree and all its parents up to the first amb node
  else
    fail; // just build the parse tree "E = id: Id i", by defaulting to the constructor
}

1.8.2. Ambiguity Detection

Synopsis

Ambiguity detection helps to find ambiguities in syntax definitions.

Description

AmbiDexter is a tool that analyzes Syntax Definitions, including their Disambiguations, to try and determine which ambiguities it contains. Static detection of ambiguity is not decidable, nevertheless AmbiDexter does a fine job at finding them.

Benefits
  • AmbiDexter can find ambiguity for you before testing the parser, after which you can use [AmbiguityDiagnosis] to explain it.

Pitfalls
  • AmbiDexter is now a separate command-line tool which still needs integration

  • AmbiDexter is not a silver bullet. It has a time-limit to stop after having searcher only so much of a language. After the time limit has expired, your [SyntaxDefinition] may still be ambiguous.

1.8.3. Ambiguity Diagnosis

Synopsis

Ambiguity diagnosis suggests changes to syntax definitions to make them non-ambiguous.

Description

The Ambiguity library, a.k.a. DrAmbiguity, contains a diagnosis tool that can help you find the causes of ambiguous parse trees and possible Disambiguations to solve them in a Syntax Definition. DrAmbiguity is a library that processes any parse forest produced by a parser generated from Rascal’s Syntax Definitions. Please read Disambiguation first.

Examples
import analysis::grammars::Ambiguity;
diagnose(t); // for any t of which you know it contains an ambiguity
Benefits
Pitfalls
  • DrAmbiguity does not scale to large parse trees, so please first make your example smaller.

  • DrAmbiguity proposes several [Disambiguation]s for each ambiguity, only few of which make sense from a language design point of view!

  • DrAmbiguity is now only a library function, while it should be integrated into the Rascal IDE

1.8.4. Disambiguation

Synopsis

Disambiguation is the definition of filters on the parse trees that Syntax Definitions define. There are several ways of defining Disambiguation in Rascal.

Description

There are generally three ways of removing ambiguity from parse forests that are produced by parsers generated from Syntax Definitions.

Associativity Declaration
Synopsis

Define associativity of operators

Syntax
  • syntax Exp = Assoc Label Symbol1 Symbol2 …​

  • syntax Exp = Assoc ( Alt1 | Alt2 | …​ )

  • syntax Exp = Assoc Symbol1 Symbol2 …​

Here Assoc is one of: left, right, assoc or non-assoc. See Syntax Definitions on how to define alternatives and Symbols.

Description

Using Associativity declarations we may disambiguate binary recursive operators.

The semantics are that an associativity modifier will instruct the parser to disallow certain productions to nest at particular argument positions:

  • left and assoc will disallow productions to directly nest in their right-most position.

  • right will disallow productions to directly nest in their left-most position.

  • non-assoc will disallow productions to directly nest in either their left-most or their right-most position.

When associativity is declared for a group of productions, e.g. left ( Alt1 | Alt 2 | Alt3), then each alternative will be mutually associative to each other alternative and itself. If an alternative of a group defines its own local associativity, as in left ( right Alt1 | Alt2 | Alt3), then Alt1 is right associative with respect to itself and left associative with respect to all others in the group.

A finer point is that associativity has no effect on any other position than the left-most and right-most position (see also Priority Declaration). This is to guarantee that associativity does not introduce parse errors. The following tables explain when an assocativity declaration filters given two productions father and child that share an associativity group.

If `left (Parent Child)` Parent None: E = "[" E "]" Parent Left-most: E = E "*" Parent Right-most: E = "*" E Parent Both: E = E "*" E

Child None: E = "{" E "}"

No filter

No filter

No filter

No filter

Child Left-most: E = E "+"

No filter

No filter

Filter under right

Filter under right

Child Right-most: E = "+" E

No filter

No filter

No filter

No filter

Child Both: E = E "+" E

No filter

No filter

If `right (Parent Child)` Parent None: E = "[" E "]" Parent Left-most: E = E "*" Parent Right-most: E = "*" E Parent Both: E = E "*" E

Child None: E = "{" E "}"

No filter

No filter

No filter

No filter

Child Left-most: E = E "+"

No filter

No filter

No filter

No filter

Child Right-most: E = "+" E

No filter

Filter under left

No filter

Filter under left

Child Both: E = E "+" E

No filter

Filter under left

Benefits
  • Short notation for common constructs in programming languages.

  • Removes ambiguity but can not introduce parse errors.

  • Allows the use of less non-terminals for the same expression grammar (typically only one), which makes parse trees simpler as well as the mapping to an abstract syntax tree more direct.

Pitfalls
  • Please do not assume that Rascal’s associativity declarations have the same semantics as SDF’s associativity declarations.

  • Use of productions that are not both left and right recursive in an associativity group, although safe, is not very meaningful. We would advise to use the Priority Declaration relation such a case. For example:

Original associativity Better written as priority

`E = left ( "+" E

E "+" E );`

E = E "" E > "" E;

`E = right ( "+" E

E "+" E );`

E = "" E > E "" E;

`E = left ( E "+"

E "+" E);`

E = E "" > E "" E;

`E = right ( E "+"

E "+" E);`

E = E "" E > E "" ;

Follow Declaration
Synopsis

A conditional Symbol, constraining the characters that can immediately follow a symbol in the input source text.

Syntax
  • Symbol >> constraint

  • Symbol !>> constraint

where a constraint is any character class, a literal or a keyword non-terminal Symbol.

Description

Using !>>, the parser will not accept the Symbol if it is immediately followed by the terminal in the input string. If the end of the symbol coincides with end-of-file, the constraint will always succeed and the symbol is accepted.

Precede Declaration
Synopsis

A conditional Symbol, constraining the characters that can immediately precede a symbol in the input source text.

Syntax
  • constraint << Symbol

  • constraint !<< Symbol

where a constraint is any character class, a literal or a keyword non-terminal Symbol.

Description

Using !<<, the parser will not accept the Symbol if it is immediately preceded by the terminal in the input string. If the start of the symbol coincides with start of the inout, the constraint will always succeed and the symbol is accepted.

Priority Declaration
Synopsis

Declare the priority of operators.

Syntax
  • syntax Exp = alt1 > alt2 > alt3 is the basic syntax for priorities.

  • syntax Exp = alt1 | alt2 > alt3 | alt4, where the | signifies groups of equal priority

  • syntax Exp = associativity ( _alt1 | …​ ) > _alt2, where an associativity group denotes a group of equal priority

Description

Priority declarations define a partial ordering between the productions within a single non-terminal. The feature is specifically designed to fit with the semantics of expression sub-languages embedded in programming languages. There exist other mechanisms for Disambiguation, if Priority Declaration does not work for you.

The semantics of a priority relation A > B is that B will not be nested under A in the left-most or right-most position. Any other position of A will allow B fine. Note that the priority relation you define is transitively closed, so if A > B and B > C then A > C.

A finer point is that Rascal restricts the filtering of priority such that it is guaranteed that no parse errors occur at the cause of a priority. The following table defines when and where Rascal forbids a direct nesting between two productions parent > child, depending on at which left-most or right-most positions the parent and the child are recursive.

If Parent > Child Parent None: E = "[" E "]" Parent Left-most: E = E "*" Parent Right-most: E = "*" E Parent Both: E = E "*" E

Child None: E = "{" E "}"

No filter

No filter

No filter

No filter

Child Left-most: E = E "+"

No filter

No filter

Filter under right

Filter under right

Child Right-most: E = "+" E

No filter

Filter under left

No filter

Filter under left

Child Both: E = E "+" E

No filter

Filter under left

Filter under right

Filter under left and right

Examples

The following snippet uses all Priority Declaration features:

syntax Exp
  = A: Id
  | B: Number
  > C: Exp "[" Exp "]"
  | D: Exp "!"
  > E: Exp "*" Exp
  > F: Exp "+" Exp;
  | bracket G: "(" Exp ")"
  ;

A short explanation:

  • C and D share a group of equal priority. They are incomparable in the partial ordering. That’s fine because 1![2] is not ambiguous.

  • Similarly A and B share a group; yet they are not recursive and so do not play any role in the priority ordering.

  • C and D both have higher priority then E and F, which means that E and F may not be directly nested under C or D.

  • However: E and F will be allowed under the second argument of C because it is not an outermost position. That’s fine because 1 [2 + 3] is not ambiguous.

Here a number of strings for this language, with brackets to show how they will be parsed:

  • "1 + 2 * 3" will be parsed as "1 + (2 * 3)" because E > F.

  • "1 + 2 [ 3 ]" will be parsed as "1 + (2\[3\])" because C > F.

  • "1 * 3!" will be parsed as "1 + (3!)" because D > E.

  • "1 + [2 * 3]" will be parsed as "1 + ([2 * 3])" because priority is only defined for outermost positions.

Benefits
  • Short notation for common expression grammars

  • Removes ambiguity but can not introduce parse errors

  • Allows the use of less non-terminals for the same expression grammar (typically only one), which makes parse trees simpler as well as the mapping to an abstract syntax tree more direct.

Pitfalls
  • Please do not assume that Rascal’s priorities have the same semantics as SDF’s priorities.

  • When a priority does not have a filtering effect, such as in E = E "+" > E "" it is usually better to use normal alternative composition: E = E "+" | E "". There is no difference in the semantics of parsing, but the latter expression is more intentional.

  • You should not hide right or left recursion behind a nullable non-terminal, since the system will not filter the ambiguity then. Example: E = left "a"? E "*" E > E "" E will remain ambiguous. This should be written as: E = left ("a" E "*" E | E "*" E ) > E "" E; (unfolding the optional such that E becomes explicitly left-most).

Reserve Declaration
Synopsis

Reserve is a conditional Symbol, constraining the set of strings that a symbol may produce.

Syntax
  • Symbol \ constraint

where a constraint is any character class, a literal or a keyword non-terminal Symbol.

1.8.5. Parse Trees

Synopsis

An algebraic data-type for parse trees; produced by all parsers generated from syntax definitions.

Description

Below is the full definition of Tree and Production and Symbol. A parse tree is a nested tree structure of type Tree.

  • Most internal nodes are applications (appl) of a Production to a list of children Tree nodes. Production is the abstract representation of a [SyntaxDefinition] rule, which consists of a definition of an alternative for a Symbol by a list of Symbols.

  • The leaves of a parse tree are always characters (char), which have an integer index in the UTF8 table.

  • Some internal nodes encode ambiguity (amb) by pointing to a set of alternative Tree nodes.

The Production and Symbol types are an abstract notation for rules in Syntax Definitions, while the Tree type is the actual notation for parse trees.

Parse trees are called parse forests when they contain amb nodes.

You can analyze and manipulate parse trees in three ways:

The type of a parse tree is the symbol that it’s production produces, i.e. appl(prod(sort("A"),[],{}),[]) has type A. Ambiguity nodes Each such a non-terminal type has Tree as its immediate super-type.

Examples
// the following definition
syntax A = "a";
// would make the following [Test] succeed:
test a() = parse(#A,"a") ==
appl(prod(
    sort("A"),
    [lit("a")],
    {}),
  [appl(
      prod(
        lit("a"),
        [\char-class([range(97,97)])],
        {}),
      [char(97)])]);
// you see that the defined non-terminal A ends up as the production for the outermost node. As the only child is the tree for recognizing the literal a, which is defined to be a single a from the character-class [ a ].
// when we use labels in the definitions, they also end up in the trees:
// the following definition
lexical A = myA:"a" B bLabel;
lexical B = myB:"b";
// would make the following [Test] succeed:
test a() = parse(#A,"ab") == appl(prod(label("myA",lex("A")),[lit("a"),sort("bLabel",lex("B"))],{}),[appl(prod(lit("a"),[\char-class([range(97,97)]),[char(97)]),appl(prod(label("myB", lex("B"),[lit("b")],{}),[appl(prod(lit("b"),[\char-class([range(98,98)]),[char(98)])]) ]);
// here you see that the alternative name is a label around the first argument of `prod` while argument labels become labels in the list of children of a `prod`.

1.8.6. Symbol

Synopsis

The symbols that can occur in a syntax definition.

Syntax

Nonterminal symbols are identifier names that start with an uppercase letter.

Symbol Description

Symbol fieldName

Any symbol can be labeled with a field name that starts with a lowercase letter

The following literal symbols and character classes are defined:

Symbol Description

"stringliteral"

Literal string

'stringliteral'

Case-insensitive literal string

[range1 range2 …​ ]

Character class

The following operations on character classes can be composed arbitrarily:

Class Description

!Class

Complement of Class with respect to the UTF8 universe of characters

Class1 - Class2

Difference of character classes Class1 and Class2

Class1 || Class2

Union of character classes Class1 and Class2

Class1 && Class2

Intersection of character classes Class1 and Class2

(Class)

Brackets for defining application order of class operators

The following regular expressions can be constructed over Symbols:

Symbol Description

Symbol?

Optional Symbol

Symbol+

Non-empty list of _Symbol_s

Symbol*

Possibly empty list of _Symbol_s.

{Symbol1 Symbol2}+

Non-empty list of Symbol1 separated by Symbol2

{Symbol1 Symbol2}*

Possibly empty list of Symbol1 separated by Symbol2.

(Symbol1 Symbol2 …​ )

Embedded sequence of symbols

(Symbol1 | Symbol2 | …​ )

Embedded choice of alternative symbols

()

The anonymous non-terminal for the language with the empty string

Inline conditions (Disambiguations) can be added to symbols to constrain their acceptability:

Disambiguation Description

Symbol _

Symbol ends at end of line or end of file

^Symbol

Symbol starts at begin of line

Symbol @ ColumnIndex

Symbol starts at certain column index.

Symbol1 >> Symbol2

Symbol1 must be (directly) followed by Symbol2

Symbol1 !>> Symbol2

Symbol1 must not be (directly) followed by Symbol2

Symbol1 << Symbol2

Symbol2 must be (directly) preceded by Symbol1

Symbol1 !<< Symbol2

Symbol2 must not be (directly) preceded by Symbol1

Symbol1 \ Symbol2

Symbol1 must not be in the language defined by Symbol2

Symbols can be composed arbitrarily.

Types

Every non-terminal symbol is a type.

Description

The basic symbols are the non-terminal name and the labeled non-terminal name. These refer to the names defined by Syntax Definition. You can use any defined non-terminal name in any other definition (lexical in syntax, syntax in lexical, etc).

Then we have literals and character classes to define the terminals of a grammar. When you use a literal such as "begin", Rascal will produce a definition for it down to the character level before generating a parser: syntax "begin" = [b][e][g][i][n];. This effect will be visible in the Parse Trees produced by the parser. For case insensitive literals you will see a similar effect; the use of 'begin' produces syntax 'begin' = [bB][eE][gG][iI][nN].

Character classes have the same escaping conventions as characters in a String literal, but spaces and newlines are meaningless and have to be escaped and the [ and ] brackets as well as the dash - need escaping. For example, one writes [\[ \] \ \n\-] for a class that includes the open and close square brackets and a space, a newline and a dash. Character classes support ranges as in [a-zA-Z0-9]. Please note about character classes that:

  • the operations on character classes are executed before parser generation time. You will not find explicit representation of these operations in [ParseTrees], but rather their net effect as resulting character classes.

  • Character classes are also ordered by Rascal and overlapping ranges are merged before parsers are generated. Equality between character classes is checked after this canonicalization.

  • Although all Symbols are type constructors, the character class operators are not allowed in types.

The other symbols either generate for you parts of the construction of a grammar, or they constrain the rules of the grammar to generate a smaller set of trees as Disambiguations.

The generative symbols are referred to as the regular symbols. These are like named non-terminals, except that they are defined implicitly and interpreted by the parser generator to produce a parser that can recognize a symbol optionally, iteratively, alternatively, sequentially, etc. You also need to know this about the regular symbols:

  • In Parse Trees you will find special nodes for the regular expression symbols that hide how these were recognized.

  • Patterns using Concrete Syntax have special semantics for the regular symbols (list matching, separator handling, ignoring layout, etc.).

  • Regular symbols are not allowed in keyword Syntax Definitions

  • Depending on their occurrence in a lexical, syntax or layout Syntax Definition the semantics of regular symbols changes. In the syntax context, layout non-terminals will be woven into the regular symbol, but not in the lexical and layout contexts. For example, a Symbol* in a syntax definition such as syntax X = A*; will be processed to syntax X = `{A Layout}*. Similarly, syntax X = {A B}+; will be processed to syntax X = {A (Layout B Layout)}+;.

The constraint symbols are specially there to deal with the fact that Rascal does not generate a scanner. There are no a priori disambiguation rules such as prefer keywords or longest match. Instead, you should use the constraint symbols to define the effect of keyword reservation and longest match.

  • It is important to note that these constraints work on a character-by-character level in the input stream. So, a follow constraint such as A >> [a-z] means that the character immediately following a recognized A must be in the range [a-z].

  • Read more on the constraint symbols via Disambiguations.

Examples

A character class that defines all alphanumeric characters:

lexical AlphaNumeric = [a-zA-Z0-9];

A character class that defines anything except quotes:

lexical AnythingExceptQuote = ![\"];

An identifier class with longest match (can not be followed immediately by [a-z]):

lexical Id = [a-z]+ !>> [a-z];

An identifier class with longest match and first match (can not be preceded or followed by [a-z]):

rascal>lexical Id = [a-z] !<< [a-z]+ !>> [a-z];
ok

An identifier class with some reserved keywords and longest match:

lexical Id = [a-z]+ !>> [a-z] \ "if" \ "else" \ "fi";

An optional else branch coded using sequence and optional symbols:

syntax Statement = "if" Expression "then" Statement ("else" Statement)? "fi";

A block of statements separated by semicolons:

syntax Statement = "{" {Statement ";"}* "}";

A declaration with an embedded list of alternative modifiers and a list of typed parameters:

syntax Declaration = ("public" | "private" | "static" | "final")* Type Id "(" {(Type Id) ","}* ")" Statement;
Benefits
  • The symbol language is very expressive and can lead to short definitions of complex syntactic constructs.

  • There is no built-in longest match for iterators, which makes syntax definitions open to languages that do not have longest match.

  • There is no built-in keyword preference or reservation, which makes syntax definitions open to language composition and legacy languages.

Pitfalls
  • By nesting too many symbols definitions can be become hard to understand.

  • By nesting too many symbols pattern matching and term construction becomes more complex. Extra non-terminals and rules with meaningful names can make a language specification more manageable.

  • The lack of automatic longest match and prefer keyword heuristics (you have to define it yourself), sometimes leads to unexpected ambiguity. See [Disambiguation].

1.9. Alias Declaration

Synopsis

Declare an alias for a type.

Syntax

alias Name = Type;

Description

Everything can be expressed using the elementary types and values that are provided by Rascal. However, for the purpose of documentation and readability it is sometimes better to use a descriptive name as type indication, rather than an elementary type. The use of aliases is a good way to document your intentions.

An alias declaration states that Name can be used everywhere instead of the already defined type Type. Both types are thus structurally equivalent.

Examples

Introduce two aliases ModuleId and Frequency for the type str.

alias ModuleId = str;
alias Frequency = int;

Another example is an alias definition for a graph containing integer nodes:

alias IntGraph = rel[int,int];

Note that the Rascal Standard Library provides a graph data type that is defined as follows:

alias Graph[&T] = rel[&T, &T];

In other words the standard graph datatype can be parameterized with any element type.

See Type Parameters for other examples parameterized alias declarations.

1.10. Annotation Declaration

Synopsis

Declare an annotation type for nodes. This feature is deprecated; please use [Keyword Fields] instead.

Syntax

anno AnnoType OnType @ Name

Description

An annotation may be associated with any node value, be it a pure node or some Algebraic Data Type derived from it.

Annotations are intended to attach application data to values, like adding position information or control flow information to source code or adding visualization information to a graph.

An annotation declaration defines:

  • AnnoType, the type of the annotation values,

  • OnType, the type of the values that are being annotated,

  • Name, the name of the annotation.

Any value of any named type can be annotated and the type of these annotations can be declared precisely.

The following constructs are provided for handling annotations:

  • Val @ Anno: is an expression that retrieves the value of annotation Anno of value Val (may be undefined!). See [Selection].

  • Val1[@Anno = Val2]: is an expression that sets the value of annotation Anno of the value Val1 to Val2 and returns Val1 with the new annotation value as result. See [Replacement].

  • Var @ Anno = Val: is an assignment statement that sets the value of annotation Anno of the value of variable Var to Val.

Examples

Examples have been removed since this feature is deprecated.

Pitfalls
  • Annotations are cumbersome since they change the structure of Values without changing the semantics of the identity of a value. This is why they are deprecated.

1.11. Tag Declaration

Synopsis

Tag declarations are not implemented (yet).

Description

Tag declarations explain which type the expected value of a tag should have.

Benefits
  • They help the type checker to find common errors and they can help the parser to parse the contents of a string tag using a context-free grammar declaration.

Pitfalls
  • Not yet implemented, so basically tags are not checked

2. Patterns

Synopsis

Patterns are a notation for pattern matching used to detect if a value has a certain shape, and then to bind variables to parts of the matched value.

Syntax

For most of the Values, there is a corresponding pattern matching operator. Then there are some "higher-order" matching operators which make complex patterns out of simpler ones. This is the complete list:

Pattern Syntax

Literal

Boolean, Integer, Real, Number, String, Location, or DateTime

Regular Expression

/<Regular Expression>/

Variable declaration

Type Var

Multi-variable

*Var, *Type Var

Variable

Var

List

[ Pat1, Pat2, …​, Patn ]

Set

{ Pat1, Pat2, …​, Patn }

Tuple

< Pat1, Pat2, …​, Patn >

Node

Name ( Pat1, Pat2, …​, Patn )

Descendant

/ Pat

Labelled

Var : Pat

TypedLabelled

Type Var : Pat

TypeConstrained

[Type] Pat

Concrete

(Symbol) ` Token1 Token2 …​ Tokenn `

Description

Patterns are used to dispatch functions and conditional control flow, to extract information from values and to conditionally filter values. The pattern following pattern kinds can be arbitrarily nested, following the above syntax:

All these patterns may be used in:

Each pattern binds variables in a conditional scope:

  • in further patterns to the right of the name which is bound in the same pattern

  • in the body of case statement (either a replacement or a statement body)

  • in the conditions and bodies of <If>, <For>, and <While> control flow statements

  • in the yielding expressions of comprehensions and in furter conditions of the comprehensions

Pitfalls
  • If a pattern does not match, then it may be hard to find out why. A small test case is the best thing to create. Often a default alternative which <Throw>s an exception with the value which is not matched can be used to find out why this is happening.

  • If a variable is bound in the scope of a pattern, then it acts as an == test, so make sure to use fresh variables to avoid such accidental collisions.

2.1. Concrete Patterns

Synopsis

Concrete patterns.

Syntax
Concrete pattern with expected symbol type: (Symbol) ` Token1 Token2 ... Tokenn `
Typed variable inside a concrete pattern: <Type Var>
Description

A concrete pattern is a pattern for matching a [Parse Tree]. The notation of a concrete pattern is the object language itself, the language that the parse tree describes. In other words, you can use a code example to match parsed code using a concrete pattern. These concrete code examples can contain Variable Patterns like the other Patterns.

The mechanism of concete patterns gives a good notation for matching complex structures such as a [Parse Tree], and it works in a simple manner:

  • the input code is parsed using a parser generated from a Syntax Definition; this generates parse trees.

  • the pattern example code is parsed using the same parser; this generates parse trees with Variable Patterns.

  • the parse tree with the Variable Patterns is matches against the parse tree of the input code, similarly to the way Node patterns work.

So, you could say that Concrete Patterns are a short notation for otherwise highly complex Node patterns on [Parse Tree]. Note that the [Typed Variable]s in a concrete pattern can only occur in the pattern at the location where the code for a full non-terminal of the Syntax Definition would be. The structure of a concrete pattern follows the structure of the grammar in the Syntax Definition and the types of the Variable Patterns are the syntax non-terminals of the Syntax Definition.

Inside concrete syntax patterns, layout is ignored while pattern matching. So parse trees which have different whitespace and comments but are otherwise the same will match anyway.

Examples

Examples (in a context where an appropriate concrete syntax has been defined):

  • Quoted syntax pattern with two pattern variable declarations:

rascal>import ParseTree;
ok
rascal>syntax Id = [a-z]&#43;;
ok
rascal>syntax Num = [0-9]&#43;;
ok
rascal>syntax Exp = left Exp &#34;&#42;&#34; Exp &#62; Exp &#34;&#43;&#34; Exp |  Id | Num;
ok
rascal>layout WS = [&#92; &#92;n&#92;r&#92;t]&#42;;
ok
rascal>visit (parse(&#35;Exp, &#34;x &#43; x&#34;)) {
>>>>>>>   case (Exp) &#96;&#60;Id a&#62; &#43; &#60;Id b&#62;&#96; =&#62; (Exp) &#96;2 &#42; &#60;Id a&#62;&#96; when a == b
>>>>>>>}
warning, ambiguity predicted: Exp  &#34;&#43;&#34;  Exp  lacks left or right associativity
warning, ambiguity predicted: Exp  &#34;&#42;&#34;  Exp  lacks left or right associativity
Exp: (Exp) &#96;x &#43; x&#96;

WARNING: unexpected errors in the above SHELL example. Documentation author please fix! Some observations about this example:

  • Notice how the non-terminals Exp and Id from the Syntax Definition become types for the pattern.

  • When this example pattern actually matches the variable a is bound and can be used again like any other Variable Pattern.

A full example of concrete patterns can be found in WithLayout.

2.2. Descendant Pattern

Synopsis

Deep match in an abstract pattern.

Description

A descendant pattern performs a deep match of the pattern Pat. In other words, it matches when any element of the subject at any depth that matches Pat and is used to match, for instance, tree nodes at an arbitrary distance from the root.

Examples
rascal>import IO;
ok
rascal>data ColoredTree = leaf(int N)
>>>>>>>                 | red(ColoredTree left, ColoredTree right)
>>>>>>>                 | black(ColoredTree left, ColoredTree right);
ok
rascal>T = red(red(black(leaf(1), leaf(2)), black(leaf(3), leaf(4))), black(leaf(5), leaf(4)));
ColoredTree: red(
  red(
    black(
      leaf(1),
      leaf(2)),
    black(
      leaf(3),
      leaf(4))),
  black(
    leaf(5),
    leaf(4)))

Now we match for black nodes with leaf(4) as second argument:

rascal>for(/black(_,leaf(4)) := T)
>>>>>>>    println("Match!");
Match!
Match!
list[void]: []

We use an anonymous variable _ at a position where we don’t care about the actual value that is matched. In order to print the actual values of the matches, we would need an [Abstract/Labelled] pattern.

Here we match all leaves that occur as second argument of black:

rascal>for(/black(_,leaf(int N)) := T)
>>>>>>>    println("Match <N>");
Match 2
Match 4
Match 4
list[void]: []

Here we list all integers that occur in any leaf:

rascal>for(/int N := T)
>>>>>>>    println("Match <N>");
Match 1
Match 2
Match 3
Match 4
Match 5
Match 4
list[void]: []

Rather than printing, we can also collect them in a list using [$Statements/Append]:

rascal>for(/int N := T)
>>>>>>>    append N;
list[int]: [1,2,3,4,5,4]

2.3. Labelled Pattern

Synopsis

Labelled abstract pattern.

Description

A labelled pattern matches the same values as Pat, but has as side-effect that the matched value is assigned to Var.

Examples
rascal>import IO;
ok
rascal>data ColoredTree = leaf(int N)
>>>>>>>                 | red(ColoredTree left, ColoredTree right)
>>>>>>>                 | black(ColoredTree left, ColoredTree right);
ok
rascal>T = red(red(black(leaf(1), leaf(2)), black(leaf(3), leaf(4))), black(leaf(5), leaf(4)));
ColoredTree: red(
  red(
    black(
      leaf(1),
      leaf(2)),
    black(
      leaf(3),
      leaf(4))),
  black(
    leaf(5),
    leaf(4)))
rascal>for(/M:black(_,leaf(4)) := T)
>>>>>>>    println("Match <M>");
Match black(leaf(3),leaf(4))
Match black(leaf(5),leaf(4))
list[void]: []

We use an anonymous variable _ at a position where we don’t care about the actual value that is matched.

2.4. List Pattern

Synopsis

List in abstract pattern.

Description

A list pattern matches a list value (the subject), provided that Pat1, Pat2, …​, Patn match the elements of that list in order. Special cases exist when one of the patterns Pati is

  • a Variable Pattern with a type that is identical to the element type of the subject list: the variable is matched with the value at the corresponding position in the subject list.

  • a MultiVariable Pattern, with an optional element type that is identical to the element type of the subject list: list matching is applied and the variable can match an arbitrary number of elements of the subject list.

  • a Variable Pattern, where the variable has been declared with a list type, but not initialized, outside the pattern: list matching is applied and the variable can match an arbitrary number of elements of the subject list.

  • a [Variable] Pattern], where the variable has been declared with a type equal to the element type of the subject, but not initialized, outside the pattern: the variable is matched with the value at the corresponding position in the subject list.

Examples
rascal>import IO;
ok
  • A single variable

rascal>if([10, int N, 30, 40, 50] := [10, 20, 30, 40, 50])
>>>>>>>   println(&#34;Match succeeded, N = &#60;N&#62;&#34;);
Match succeeded, N = 20
ok
  • An untyped multi-variable:

rascal>if([10, &#42;L, 50] := [10, 20, 30, 40, 50])
>>>>>>>   println(&#34;Match succeeded, L = &#60;L&#62;&#34;);
Match succeeded, L = [20,30,40]
ok
  • A typed multi-variable:

rascal>if([10, &#42;int L, 50] := [10, 20, 30, 40, 50])
>>>>>>>   println(&#34;Match succeeded, L = &#60;L&#62;&#34;);
Match succeeded, L = [20,30,40]
ok

A list pattern may also be non-linear, i.e., it may contain uses of variables that were bound earlier in the pattern (here, the second occurence of L):

rascal>if([10, *L, 40, *L, 50] := [10, 20, 30, 40, 20, 30, 50])
>>>>>>>   println("Match succeeded, L = <L>");
Match succeeded, L = [20,30]
ok

Here we see an example, where all pairs of equal elements in a list are printed:

rascal>for([*L1, int N, *L2, N, *L3] := [ 5, 10, 20, 30, 40, 30, 15, 20, 10])
>>>>>>>    println("N = <N>");
N = 10
N = 20
N = 30
list[void]: []

Here we print all ways in which a given list can be partitioned in two lists:

rascal>for([*L1, *L2] := [10, 20, 30, 40, 50])
>>>>>>>    println("<L1> and <L2>");
[] and [10,20,30,40,50]
[10] and [20,30,40,50]
[10,20] and [30,40,50]
[10,20,30] and [40,50]
[10,20,30,40] and [50]
[10,20,30,40,50] and []
list[void]: []
  • Already declared list variable:

rascal>list[int] L;
ok
rascal>if([10, L, 50] := [10, 20, 30, 40, 50])
>>>>>>>   println(&#34;Match succeeded, L = &#60;L&#62;&#34;);
Match succeeded, L = [20,30,40]
ok
  • Already declared element variable:

rascal>int N;
ok
rascal>if([10, N, 30, 40, 50] := [10, 20, 30, 40, 50])
>>>>>>>   println(&#34;Match succeeded, N = &#60;N&#62;&#34;);
Match succeeded, N = 20
ok

2.5. Literal Pattern

Synopsis

Literal in abstract pattern.

Description

A literal of one of the basic types Boolean, Integer, Real, Number, String, Location, or DateTime can be used as abstract pattern. A literal pattern matches with a value that is identical to the literal.

Examples

A literal pattern matches with a value that is equal to it:

rascal>123 := 123
bool: true
rascal>"abc" := "abc"
bool: true

A literal pattern does not match with a value that is not equal to it:

rascal>123 := 456
bool: false
rascal>"abc" := "def"
bool: false

If the type of the literal pattern is incomparable to the subject’s type, a static type error is produced to announce that the match is guaranteed to fail:

rascal>123 := "abc";
|prompt:///|(7,5,<1,7>,<1,12>): Expected int, but got str
Advice: |http://tutor.rascal-mpl.org/Errors/Static/UnexpectedType/UnexpectedType.html|
 
ok

However, a literal pattern can be used to filter among other values:

rascal>value x = "abc";
value: "abc"
rascal>123 := x;
bool: false
rascal>x = 123;
value: 123
rascal>123 := x;
bool: true

2.6. MultiVariable Pattern

Synopsis

Multi-variable (also known as Splice-variable) in abstract pattern.

Syntax
  • *Var

  • *Type Var

Description

A multi-variable is an abbreviation for a variable declaration pattern. It can occur in a list pattern or set pattern and can match zero or more list or set elements. Optionally the element type of the multi-variable may be specified.

Examples
rascal>import IO;
ok

Using lists:

rascal>if([10, *N, 50] := [10, 20, 30, 40, 50])
>>>>>>>   println("Match succeeds, N == <N>");
Match succeeds, N == [20,30,40]
ok

the above is equivalent with:

rascal>if([10, *int N, 50] := [10, 20, 30, 40, 50])
>>>>>>>   println("Match succeeds, N == <N>");
Match succeeds, N == [20,30,40]
ok

Using sets:

rascal>if({10, *S, 50} := {50, 40, 30, 30, 10})
>>>>>>>   println("Match succeeds, S == <S>");
Match succeeds, S == {40,30}
ok

the above is equivalent with:

rascal>if({10, *int S, 50} := {50, 40, 30, 30, 10})
>>>>>>>   println("Match succeeds, S == <S>");
Match succeeds, S == {40,30}
ok
Pitfalls

In older versions of Rascal the type of a multi-variable had to be a list or set type.

2.7. Node pattern

Synopsis

Node in abstract pattern.

Description

A node pattern matches a node value or a datatype value, provided that Name matches with the constructor symbol of that value and Pat1, Pat2, …​, Patn match the children of that value in order.

Examples

Match on node values (recall that the function symbol of a node has to be quoted, see [Values/Node]):

rascal>import IO;
ok
rascal>if("f"(A,13,B) := "f"("abc", 13, false))
>>>>>>>   println("A = <A>, B = <B>");
A = abc, B = false
ok

Define a data type and use it to match:

rascal>data Color = red(int N) | black(int N);
ok
rascal>if(red(K) := red(13))
>>>>>>>   println("K = <K>");
K = 13
ok

2.8. Regular Expression Pattern

Synopsis

Regular expression patterns.

Description

Regular expressions are used to match a string value and to decompose it in parts and also to compose new strings. Regular expression patterns bind variables of type str when the match succeeds, otherwise they do not bind anything. They can occur in cases of visit and switch statements, on the left-hand side of the match operator (:= or !:=) and as declarator in enumerators.

We use a regular expression language that slightly extends/modifies the Java Regex language:

  • Regular expression are delimited by / and / optionally followed by modifiers (see below).

  • We allow variable introductions, syntax <_Name_:_Regex_>, which introduce a variable of type str named Name. A variable introduction corresponds to a group in a Java regexp. Each variable that is introduced should be unique, but may be referenced more than once later in the regular expression.

  • Regular expressions may also contain references to variables, syntax <_Name_>, the string value of variable Name is used at the position of the variable reference. This can be used to define so-called non-linear patterns.

  • Java regular expressions allow optional groups, which may introduce null bindings. Since uninitialized variables are not allowed in Rascal, we limit the kinds of expressions one can write here by not allowing nesting of variable introductions.

  • We allow variable references in a regular expression of the form: <_Name_> which inserts the string value of Name in the pattern. $Name$ should have been introduced in the regular expression itself or in the context in which the regular expression occurs.

  • In Perl matching options follow the regular expression, but Java uses the notation (?Option) at the beginning of the regular expression to set matching options. We support both styles. The following modifiers are supported:

    • multi-line matching: (?m) at the start of the regular expression or the modifier m at the end of the regular expression. The anchors ^ and $ usually only match at the beginning and end of the subject string. When this option is set they also match any begin or end of line that is embedded in the subject string. Examples:

    • case-insensitive matching: (?i) or modifier i. Match characters irrespective of their case.

    • single-line mode: (?s) or modifier s. The . expression does usually not match line terminators. When single-line mode is set, it will match any character including line terminators.

    • unix lines: (?d) or modifier d. Usually newlines (\n), carriage return (\r) and new line carriage return (\n\r) sequences are all considered line terminators. When this option is set, only newline is considered to be a line terminator.

For convenience, we summarize the most frequently used constructs in regular expressions in the following table.

Frequently used elements of Regular Expression Syntax
Operator Description

x

The single character x as long as it is not a punctuation character with a special meaning in the regular expression syntax

\p

The punctuation character p, this includes !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, {, |, }, and ~.

\\

The backslash character

\n

Newline character

\t

Tab character

[…​]

One of the characters between the brackets (also known as character class). Character ranges and set operations on character classes may be used.

[^…​]

Any one character not between the brackets.

[a-z0-9]

Character range: character between a and z or 0 and 9.

.

Any character except a line terminator. If single-line mode is set (using (?s) or modifier s), then it matches any character including line terminators.

\d

Digit: [0-9]

\D

Non-digit:` [^0-9]`

\s

Whitespace

\S

Anything but whitespace.

\w

A word: [a-zA-Z0-9_]

\W

A non-word:` [^\w]`

xy

Match x followed by y

`x

y`

Match x or y

x?

Optional occurrence of x

x*

Zero or more occurrences of x

x+

One or more occurrences of x

x{n}

Exactly n occurrences of x

x{n,}

n or more occurrences of x

x{n,m}

At least n, at most m occurrences of x

^

The beginning of the subject string

$

The end of the input string

\b

Word boundary: position between a word and a non-word character

\B

Examples

Here are some examples of regular expression patterns.

/\brascal\b/i

does a case-insensitive match (i) of the word rascal between word boundaries (\b). And

/^.*?<word:\w+><rest:.*$>/m

does a multi-line match (m), matches the first consecutive word characters (\w) and assigns them to the variable word. The remainder of the string is assigned to the variable rest.

A variable reference used to make a non-linear pattern:

/<x:[a-z]+>---<x>/

matches strings like abc---abc that consist of two identical sequences of letters separated by three dashes. Variables that are referenced in a regular expression may also come from the context in which the regular expression occurs. For instance,

/<x><n>/

will use the current values of x and n as regular expression. For values "abc", respectively, 3 this would be equivalent to the regular expression:

/abc3/

Observe that context variables may be of arbitrary type and that their value is first converted to a string before it is inserted in the regular expression. This can be used in many ways. For instance, regular expressions may contain restrictions on the number of repetitions of an element: /a{3}/ will match exactly three letters a. Also minimum and maximum number of occurrences can be defined. Here is how the repetition count can be inserted by a variable reference (where n is assumed to have an integer value):

/a{<n>}/

Taking this example one step further, we can even write

/<x:a{<n>}>/

in other words, we introduce variable x and its defining regular expression contains a reference to a context variable.

Multi-line matching:

rascal>/XX$/ := "lineoneXX\nlinetwo";
bool: false
rascal>/XX$/m := "lineoneXX\nlinetwo";
bool: true
rascal>/(?m)XX$/ := "lineoneXX\nlinetwo";
bool: true

Case-insensitive matching:

rascal>/XX/ := "some xx";
bool: false
rascal>/XX/i := "some xx";
bool: true
rascal>/(?i)XX/ := "some xx";
bool: true

Single-line mode:

rascal>/a.c/ := "abc";
bool: true
rascal>/a.c/ := "a\nc";
bool: false
rascal>/a.c/s := "a\nc";
bool: true
rascal>/(?s)a.c/ := "a\nc";
bool: true

Here are examples, how to escape punctuation characters in regular expressions:

rascal>/a\/b/ := "a/b";
bool: true
rascal>/a\+b/ := "a+b";
bool: true

2.9. Set Pattern

Synopsis

Set in abstract pattern.

Description

A set pattern matches a set value (the subject), provided that Pat1, Pat2, …​, Patn match the elements of that set in any order (recall that the elements of a set are unordered and do not contain duplicates). Completely analogous to list patterns, there are special cases when one of the patterns Pati is

  • a Variable Declaration Pattern with a type that is identical to the element type of the subject set: the variable is matched with one value in the subject set.

  • a MultiVariable Pattern, with an optional element type that is identical to the element type of the subject set: set matching is applied and the variable can match an arbitrary number (in arbitrary order) of elements of the subject set.

  • a Variable Pattern, where the variable has been declared with a set type, but not initialized, outside the pattern: set matching is applied and the variable can match an arbitrary number (in arbitrary order) of elements of the subject set.

  • a Variable Pattern, where the variable has been declared with a type equal to the element type of the subject, but not initialized, outside the pattern: the variable is matched with one value in the subject set.

Examples
rascal>import IO;
ok
  • A single variable

rascal>if({10, 30, 40, 50, int N} := {10, 20, 30, 40, 50})
>>>>>>>   println(&#34;Match succeeded, N = &#60;N&#62;&#34;);
Match succeeded, N = 20
ok
  • An untyped multi-variable:

rascal>if({10, &#42;S, 50} := {50, 40, 30, 20, 10})
>>>>>>>   println(&#34;Match succeeded, S = &#60;S&#62;&#34;);
Match succeeded, S = {40,20,30}
ok
  • A typed multi-variable:

rascal>if({10, &#42;int S, 50} := {50, 40, 30, 20, 10})
>>>>>>>   println(&#34;Match succeeded, S = &#60;S&#62;&#34;);
Match succeeded, S = {40,20,30}
ok

Here we see an example, where all possible splits of a set in two subsets are printed:

rascal>for({*S1, *S2} :={30, 20, 10})
>>>>>>>    println("<S1> and <S2>");
{10,20,30} and {}
{10,20} and {30}
{10,30} and {20}
{10} and {20,30}
{20,30} and {10}
{20} and {10,30}
{30} and {10,20}
{} and {10,20,30}
list[void]: []
  • Already declared set variable:

rascal>set[int] S;
ok
rascal>if({10, &#42;S, 50} := {10, 20, 30, 40, 50})
>>>>>>>   println(&#34;Match succeeded, S = &#60;S&#62;&#34;);
Match succeeded, S = {40,20,30}
ok
  • Already declared element variable:

rascal>int N;
ok
rascal>if({10, N, 30, 40, 50} := {50, 40, 30, 20, 10})
>>>>>>>   println(&#34;Match succeeded, N = &#60;N&#62;&#34;);
Match succeeded, N = 20
ok

2.10. Tuple Pattern

Synopsis

Tuple in abstract pattern.

Description

A tuple pattern matches a tuple value, provided that Pat1, Pat2, …​, Patn match the elements of that tuple in order.

Examples
rascal>import IO;
ok
rascal>if(<A, B, C> := <13, false, "abc">)
>>>>>>>   println("A = <A>, B = <B>, C = <C>");
A = 13, B = false, C = abc
ok

2.11. Type Constrained Pattern

Synopsis

Type constrained abstract pattern.

Description

A type constrained pattern matches provided that the subject has type Type and Pat matches. This can be handy in case of ambiguity (say more than one constructor with the same name), or in case the pattern is completely general. See an example below:

Warning: This does not seem to work properly. There is a bug.

Examples
rascal>import IO;
ok

Some example data type which contains generic values as well as specific expressions:

rascal>data Exp = val(value v) | add(Exp l, Exp r) | sub(Exp l, Exp r);
ok
rascal>ex = add(add(val("hello"(1,2)),val("bye")), sub(val(1),val(2)));
Exp: add(
  add(
    val("hello"(1,2)),
    val("bye")),
  sub(
    val(1),
    val(2)))

Here we constrain the match to find only Exps:

rascal>visit (ex) {
>>>>>>>  case [Exp] str name(,) : println("node name is <name>");
>>>>>>>}
node name is hello
node name is add
node name is sub
node name is add
Exp: add(
  add(
    val("hello"(1,2)),
    val("bye")),
  sub(
    val(1),
    val(2)))

Here we do not constrain the same pattern:

rascal>visit (ex) {
>>>>>>>  case str name(,) : println("node name is <name>");
>>>>>>>}
node name is hello
node name is add
node name is sub
node name is add
Exp: add(
  add(
    val("hello"(1,2)),
    val("bye")),
  sub(
    val(1),
    val(2)))

2.12. Typed and Labelled Pattern

Synopsis

Typed, labelled, abstract pattern.

Description

A typed, labelled, pattern matches when the subject value has type Type and Pat matches. The matched value is assigned to Var.

This construct is used for:

  • binding the whole pattern to a variable while also matching some stuff out of it: MyType t : someComplexPattern(f(int a), int b)). This is similar to Labelled Patterns but with an extra type

  • to assert that the pattern has a certain type. This can be useful in disambiguating a constructor name, as in the example below.

Examples
rascal>import IO;
ok
rascal>data Lang = add(Lang l, Lang r) | number(int i);
ok
rascal>data Exp = id(str n) | add(Exp l, Exp r) | subtract(Exp l, Exp r) | otherLang(Lang a);
ok
rascal>ex = add(id("x"), add(id("y"), otherLang(add(number(1),number(2)))));
Exp: add(
  id("x"),
  add(
    id("y"),
    otherLang(add(
        number(1),
        number(2)))))
rascal>visit (ex) {
>>>>>>>  case Lang l:add(,) : println("I found a Lang <l>");
>>>>>>>  case Exp e:add(,)  : println("And I found an Exp <e>");
>>>>>>>}
I found a Lang add(number(1),number(2))
And I found an Exp add(id("y"),otherLang(add(number(1),number(2))))
And I found an Exp add(id("x"),add(id("y"),otherLang(add(number(1),number(2)))))
Exp: add(
  id("x"),
  add(
    id("y"),
    otherLang(add(
        number(1),
        number(2)))))

2.13. Variable Pattern

Synopsis

Variable in abstract pattern.

Syntax

Var

Description

A variable pattern can act in two roles:

  • If Var has already a defined value then it matches with that value.

  • If Var has not been defined before (or it has been declared but not initialized) then it matches any value. That value is assigned to Var. The scope of this variable is the outermost expression in which the pattern occurs or the enclosing If, While, or Do if the pattern occurs in the test expression of those statements.

Examples

Initialize variable N

rascal>N = 10;
int: 10

and use N in a pattern; its value is used as value to match with:

rascal>N := 10;
bool: true
rascal>N := 20;
bool: false

Use a non-existing variable in a pattern, it is bound when the match succeeds:

rascal>import IO;
ok
rascal>if(M := 10)
>>>>>>>   println("Match succeeded, M == <M>");
Match succeeded, M == 10
ok

2.14. Variable Declaration Pattern

Synopsis

Variable declaration in abstract pattern.

Description

A variable declaration

Type Var

can be used as abstract pattern. A variable declaration introduces a new variable Var that matches any value of the given type Type. That value is assigned to Var when the whole match succeeds.

The scope of this variable is the outermost expression in which the pattern occurs or the enclosing If, While, or Do if the pattern occurs in the test expression of those statements.

Examples

Let’s first perform a match that succeeds:

rascal>str S := "abc";
bool: true

and now we attempt to inspect the value of S:

rascal>S;
|prompt:///|(0,1,<1,0>,<1,1>): Undeclared variable: S
Advice: |http://tutor.rascal-mpl.org/Errors/Static/UndeclaredVariable/UndeclaredVariable.html|
 
ok

As mentioned above: S is only bound in the scope of the match expression! Let’s explore how bindings work in an if statement:

rascal>import IO;
ok
rascal>if(str S := "abc")
>>>>>>>   println("Match succeeds, S == \"<S>\"");
Match succeeds, S == "abc"
ok

3. Expressions

Synopsis

The expressions available in Rascal.

Description

The expression is the basic unit of evaluation and may consist of the ingredients shown in the figure.

  • An elementary literal value, e.g. constants of the types Boolean, Integer, Real, Number, String, Location or DateTime.

  • A structured value for List, Set, Map, Tuple or Relation. The elements are first evaluated before the structured value is built.

  • A variable that evaluates to its current value.

  • A call to a function or constructor:

    • A function call. First the arguments are evaluated and the corresponding function is called. The value returned by the function is used as value of the function call. See Call.

    • A constructor. First the arguments are evaluated and then a data value is constructed for the corresponding type. This data value is used as value of the constructor. Constructors are functions that can be used in all contexts where functions can be used. See Constructor.

  • An operator expression. The operator is applied to the arguments; the evaluation order of the arguments depends on the operator. The result returned by the operator is used as value of the operator expression. See Operators.

  • Comprehensions.

  • A Visit expression.

  • A Boolean Any expression.

  • An Boolean All expression.

  • Some statements like If, For, While and Do can also be used in expressions, see Statement as Expression.

3.1. Values

Synopsis

The different types of values.

Description

3.1.1. Boolean

Synopsis

Boolean values.

Syntax

true, false

Types

bool

Description

The Booleans are represented by the type bool which has two values: true and false.

The Boolean operators (to be more precise: operators with a value of type Boolean as result) have short-circuit semantics. This means that the operands are evaluated until the outcome of the operator is known.

Most operators are self-explanatory except the match (:=) and no match (!:=) operators that are also the main reason to treat Boolean operator expressions separately. Although we describe patterns in full detail in Patterns, a preview is useful here. A pattern can

  • match (or not match) any arbitrary value (that we will call the subject value);

  • during the match variables may be bound to subvalues of the subject value.

The match operator

Pat := Exp

is evaluated as follows:

  • Exp is evaluated, the result is a subject value;

  • the subject value is matched against the pattern Pat;

  • if the match succeeds, any variables in the pattern are bound to subvalues of the subject value and the match expression yields true;

  • if the match fails, no variables are bound and the match expression yields false.

This looks and is nice and dandy, so why all this fuss about Boolean operators? The catch is that—​as we will see in Patterns--a match need not be unique. This means that there may be more than one way of matching the subject value resulting in different variable bindings.

This behaviour is applicable in the context of all Rascal constructs where a pattern match determines the flow of control of the program, in particular:

  • Boolean expressions: when a pattern match fails that is part of a Boolean expression, further solutions are tried in order to try to make the Boolean expression true.

  • Tests in For, While, Do statements.

  • Tests in Boolean Any and Boolean All expressions.

  • Tests and Enumerators in comprehensions.

  • Pattern matches in cases of a Visit.

  • Pattern matches in cases of a Switch.

The following operators are provided for Boolean:

There are also library functions available for Booleans.

Examples

Consider the following match of a list

rascal>[1, *int L, 2, *int M] := [1,2,3,2,4]
bool: true

By definition list[int] L and list[int] M match list elements that are part of the enclosing list in which they occur. If they should match a nested list each should be enclosed in list brackets.

There are two solutions for the above match:

  • L = [] and M =` [2, 3, 2, 4]`; and

  • L = [2,3] and M =` [4]`.

rascal>import IO;
ok
rascal>for ([1, *int L, 2, *int M] := [1,2,3,2,4])
>>>>>>>  println("L: <L>, M: <M>");
L: [], M: [3,2,4]
L: [2,3], M: [4]
list[void]: []

Depending on the context, only the first solution of a match expression is used, respectively all solutions are used. If a match expression occurs in a larger Boolean expression, a subsequent subexpression may yield false and — depending on the actual operator — evaluation backtracks to a previously evaluated match operator to try a next solution. Let’s illustrate this by extending the above example:

[1, *int L, 2, *int M] := [1,2,3,2,4] && size(L) > 0

where we are looking for a solution in which L has a non-empty list as value. Evaluation proceeds as follows:

  • The left argument of the && operator is evaluated: the match expression is evaluated resulting in the bindings L = [] and M = [2, 3, 2, 4];

  • The right argument of the && operator is evaluated: size(L) > 0 yields false;

  • Backtrack to the left argument of the && operator to check for more solutions: indeed there are more solutions resulting in the bindings L = [2,3] and M = [4];

  • Proceed to the right operator of &&: this time size(L) > 0 yields true;

  • The result of evaluating the complete expression is true.

rascal>import IO;
ok
rascal>import List;
ok

for prints them all:

rascal>for ([1, *int L, 2, *int M] := [1,2,3,2,4] && size(L) > 0)
>>>>>>>  println("L: <L>, M: <M>");
L: [2,3], M: [4]
list[void]: []

if prints the first

rascal>if ([1, *int L, 2, *int M] := [1,2,3,2,4] && size(L) > 0)
>>>>>>>  println("L: <L>, M: <M>");
L: [2,3], M: [4]
ok
Boolean All
Synopsis

All argument expressions are true.

Syntax

all ( Exp1, Exp2, …​ )

Types
Exp1 Exp2 …​ all ( Exp1, Exp2, …​ )

bool

bool

…​

bool

Description

Yields true when all combinations of values of Expi are true.

Examples

Are all integers 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 even?

rascal>all(int n <- [1 .. 10], n % 2 == 0);
bool: false

Are all integers 0, 2, 4, 6, 8, 10 even?

rascal>all(int n <- [0, 2 .. 10], n % 2 == 0);
bool: true

When one of the Expi enumerates the elements of an empty list, all always returns true:

rascal>all(int n <- [], n > 0);
bool: false
Pitfalls
The Rascal interpreter and compiler give different results on an empty list. The interpreter returns fals for the abo eexample.
Boolean And
Synopsis

Boolean and operator.

Syntax

Exp1 && Exp2

Types
Exp1 Exp2 Exp1 && Exp2

bool

bool

bool

Description

The and operator on Boolean values defined as follows:

Exp1 Exp2 Exp1 && Exp2

true

true

true

true

false

false

false

true

false

false

false

false

Boolean operators have short circuit semantics: only those operands are evaluated that are needed to compute the result. In the case of the && operator, the result is false if Exp1 evaluates to false, otherwise Exp2 is evaluated to determine the result.

Note that && backtracks over its argument expressions until it can find an evaluation that yields true unless there is none. This may happen if the left or right expression is a non-deterministic pattern match or a value generator.

Variable assignments as a result of matching or generator expressions under a && are visible outside the context of the operator, but only if the context is conditional, such as an if-then-else or a for loop. Note that if one of the argument expressions evaluates to false, then no binding is done either.

Examples
rascal>true && false;
bool: false
rascal>i <- [1,2,3] && (i % 2 == 0)
bool: true
rascal>import IO;
ok
rascal>if (i <- [1,2,3] && (i % 2 == 0))
>>>>>>>  println("<i> % 2 == 0");
2 % 2 == 0
ok
rascal>for (i <- [1,2,3,4] && (i % 2 == 0))
>>>>>>>  println("<i> % 2 == 0");
2 % 2 == 0
4 % 2 == 0
list[void]: []
Benefits
  • The backtracking && allows one to express searching for a computational solution in concise manner.

Pitfalls
  • Side effects to global variables or IO in the context of a backtracking && can lead to more effects than you bargained for.

rascal>import IO;
ok
rascal>int i = 0;
int: 0
rascal>bool incr() { i += 1; return true; }
bool (): function(|prompt:///|(0,36,<1,0>,<1,36>))
rascal>for (int j <- [1,2,3] && incr() && (i % 2 == 0))
>>>>>>>  println("once true for <j>");
once true for 2
list[void]: []
rascal>i;
int: 3
Boolean Any
Synopsis

Any combination of argument values is true.

Syntax

any ( Exp1, Exp2, …​ )

Types
Exp1 Exp2 …​ any ( Exp1, Exp2, …​ )

bool

bool

…​

bool

Description

Yields true when at least one combination of values of Expi is true.

Examples
rascal>any(int n <- [1 .. 10], n % 2 == 0);
bool: true
Boolean Equivalence
Synopsis

The equivalence operator on Boolean values.

Syntax

Exp1 ⇐⇒ Exp2

Types
Exp1 Exp2 Exp1 ⇐⇒ Exp2

bool

bool

bool

Description

The equivalence operator on Boolean values defined as follows:

Exp1 Exp2 Exp1 ⇐⇒ Exp2

true

true

true

true

false

false

false

true

false

false

false

true

Boolean operators have short circuit semantics: only those operands are evaluated that are needed to compute the result. However, in the case of the ⇐⇒ operator both operands have to be evaluated to determine the result.

Note that the ⇐⇒ operator backtracks over its arguments until it finds an evaluation that is true, unless there is none. Variable bindings that are the effect of matching operators in its arguments are not visible outside the scope of the ⇐⇒.

Examples
rascal>import IO;
ok
rascal>false <==> false;
bool: true
rascal>false <==> true;
bool: false
We should add a more meaningful example of backtracking over ⇐⇒ than this old one: (i ← [1,2]) ⇐⇒ (j ← [1,2,3]); for i ← [1,2]) ⇐⇒ (j ← [1,2,3] println("true!"); (i ← [1,2] && (i % 2 == 0)) ⇐⇒ (j ← [1,2,3] && (j % 3 == 0)) for i ← [1,2] && (i % 2 == 0 ⇐⇒ (j ← [1,2,3] && (j % 3 == 0))) println("true!");
Boolean IfDefinedElse
Synopsis

Test whether expression has a defined value, otherwise provide alternative.

Syntax

Exp1 ? Exp2

Types
Exp1 Exp2 Exp1 ? Exp2

T1

T2

T2 <: T1

Description

If no exception is generated during the evaluation of Exp1, the result of Exp1 ? Exp2 is the value of Exp1. Otherwise, it is the value of Exp2.

Examples

This test can, for instance, be used to handle the case that a certain key value is not in a map:

rascal>T = ("a" : 1, "b" : 2);
map[str, int]: ("a":1,"b":2)

Trying to access the key "c" will result in an error:

rascal>T["c"];
|prompt:///|(2,3,<1,2>,<1,5>): NoSuchKey("c")
	at $shell$(|prompt:///|(0,7,<1,0>,<1,7>))
 
ok

Using the ? operator, we can write:

rascal>T["c"] ? 0;
int: 0

This is very useful, if we want to modify the associated value, but are not sure whether it exists:

rascal>T["c"] ? 0 += 1;
map[str, int]: ("a":1,"b":2,"c":1)

Another example using a list:

rascal>L = [10, 20, 30];
list[int]: [10,20,30]
rascal>L[4] ? 0;
int: 0

It is, however, not possible to assign to index positions outside the list.

Boolean Implication
Synopsis

The implication operator on Boolean values.

Syntax

Exp1 =⇒ Exp2

Types
Exp1 Exp2 Exp1 =⇒ Exp2

bool

bool

bool

Description

The implication operator on Boolean values defined as follows:

Exp1 Exp2 Exp1 =⇒ Exp2

true

true

true

true

false

false

false

true

true

false

false

true

Boolean operators have short circuit semantics: only those operands are evaluated that are needed to compute the result. In the case of the =⇒ operator, the result is true if Exp1 evaluates to false, otherwise Exp2 is evaluated to determine the result.

Examples
rascal>false ==> true;
bool: true
Boolean IsDefined
Synopsis

Test whether the value of an expression is defined.

Syntax

Exp ?

Types
Exp Exp ?

T

bool

Description

If no exception is generated during the evaluation of Exp, the result is true. Otherwise, it is false.

Examples
rascal>T = ("a" : 1, "b" : 2);
map[str, int]: ("a":1,"b":2)
rascal>T["b"]?
bool: true
rascal>T["c"]?
bool: false
rascal>L = [10, 20, 30];
list[int]: [10,20,30]
rascal>L[1]?
bool: true
rascal>L[5]?
bool: false
Boolean Match
Synopsis

Match a pattern against an expression.

Syntax

Pat := Exp

Types
Pat Exp Pat := Exp

Patterns

value

bool

Description

See [Pattern Matching] for an introduction to pattern matching and Patterns for a complete description.

Examples
rascal>123 := 456;
bool: false
rascal>[10, *n, 50] := [10, 20, 30, 40, 50];
bool: true
rascal>{10, *int n, 50} := {50, 40, 30, 30, 10};
bool: true
Boolean Negation
Synopsis

The not operator on Boolean values.

Syntax

! Exp

Types
Exp ! Exp

bool

bool

Description

The not operator on Boolean values defined as follows:

Exp ! Exp

true

false

false

true

Examples
rascal>!true;
bool: false
Boolean NoMatch
Synopsis

Negated [Boolean Match] operator.

Syntax

Pat !:= Exp

Types
Pat Exp Pat !:= Exp

[Patterns]

value

bool

Description

See [Pattern Matching] for an introduction to pattern matching and Patterns for a complete description.

Examples
rascal>123 !:= 456;
bool: true
rascal>[10, *n, 50] !:= [10, 20, 30, 40];
bool: true
rascal>{10, *n, 50} !:= {40, 30, 30, 10};
bool: true
Boolean Or
Synopsis

The or operator on Boolean values.

Syntax

Exp1 || Exp2

Types
Exp1 Exp2 Exp1 || Exp2

bool

bool

bool

Description

The or operator on Boolean values defined as follows:

Exp1 Exp2 Exp1 || Exp2

true

true

true

true

false

true

false

true

true

false

false

false

Boolean operators have short circuit semantics: only those operands are evaluated that are needed to compute the result. In the case of the || operator, the result is true if Exp1 evaluates to true, otherwise Exp2 is evaluated to determine the result.

Note that || will backtrack over its argument expressions until it can find an evaluation that is true, unless there is none.

Variable assignments as a result of matching or generator expressions under a || are visible outside the context of the operator, but only if the context is conditional, such as an if-then-else or a for loop. Note that it is statically required that both sides of an || introduce the same variable names of the same type.

Examples
rascal>import IO;
ok
rascal>false || true;
bool: true
rascal>(i <- [1,2,3,4] && i % 2 == 0) || false
bool: true
rascal>for ((i <- [1,2,3,4] && i % 2 == 0) || false)
>>>>>>>  println("true for <i>");
true for 2
true for 4
list[void]: []

3.1.2. Constructor

Synopsis

Constructors create values for user-defined datatypes (Algebraic Datatypes).

Syntax

Name ( Exp1, Exp2, …​ )

Types
Exp1 Exp2 …​ Name ( Exp1, Exp2, …​ )

T1

T2

…​

Depends on ADT declaration

Description

In ordinary programming languages record types or classes exist to introduce a new type name for a collection of related, named, values and to provide access to the elements of such a collection through their name.

In Rascal, algebraic data types provide this facility. They have to be declared, see Algebraic Data Type, and then values can be created using calls to the declared constructor functions. The constructor Name should correspond (regarding name, arity and argument types) to one of the alternatives in the ADT declaration.

First, the actual parameter expressions Expi are evaluated resulting in values Vi. Next, a data value is constructed in accordance with the declared data type using the values Vi as arguments for the constructor. This data value is used as value of the constructor. Constructors are functions that can be used in all contexts where functions can be used.

Observe that the syntax of a constructor is identical to the syntax of an function Call.

Examples

First, define a datatype WF for word frequencies:

rascal>data WF = wf(str word, int freq);
ok

Then construct a new WF value by calling the constructor wf with appropriate arguments:

rascal>wf("Rascal", 10000);
WF: wf("Rascal",10000)

3.1.3. DateTime

Synopsis

Date and time values.

Syntax
  • $ Date $

  • $ Time $

  • $ DateTime $

Types

datetime

Description

Date, time, and datetime values are represented by the datetime type. datetime literals start with a $ and are made up of either a date, given in year, month, day of month order; a time, preceded by T and given in hour, minute, second, millisecond, (optional) timezone offset order; or a datetime, which is a date and a time, in the orders given above, and separated by a T.

The following fields provide access to information about the value, but cannot be set:

  • isDate: returns true if the value is a date value, false if the value is a datetime or time value.

  • isTime: returns true if the value is a time value, false if the value is a date or datetime value.

  • isDateTime: returns true if the value is a datetime value, false if the value is a date or time value.

  • justTime: returns the date component of a date or datetime value.

  • justDate: returns the time component of a time or datetime value.

  • century: returns the century component of a year for date or datetime values.

The following fields provide access to the individual components of date, time and datetime values, and can be accessed using DateTime Field Selection and be assigned using DateTime Field Selection:

  • year

  • month

  • day

  • hour

  • minute

  • second

  • millisecond

  • timezoneOffsetHours

  • timezoneOffsetMinutes

Not all fields are available on all values as indicated by the following table:

Field date datetime time

year

x

x

month

x

x

day

x

x

hour

x

x

minute

x

x

second

x

x

millisecond

x

x

timezoneOffsetHours

x

x

timezoneOffsetMinutes

x

x

The isDate, isTime, and isDateTime fields can be checked in advance to determine what kind of value is stored in a variable of type datetime.

The following operators are defined for DateTime:

The following functions are defined for DateTime:

There are also library functions available for DateTime.

Examples

Examples of datetime values are:

rascal>$2010-07-15$
datetime: $2010-07-15$
rascal>$T07:15:23.123+0100$;
|prompt:///|(0,20,<1,0>,<1,20>): Invalid datetime input: Error reading time, expected ':', found: 49
Advice: |http://tutor.rascal-mpl.org/Errors/Static/DateTimeSyntax/DateTimeSyntax.html|
ok

WARNING: unexpected errors in the above SHELL example. Documentation author please fix! Now introduce a datetime value and assign it to DT.

rascal>DT = $2010-07-15T09:15:23.123+03:00$;
datetime: $2010-07-15T09:15:23.123+03:00$

Here are examples of some datatime fields:

rascal>DT.isDateTime;
bool: true
rascal>DT.justDate;
datetime: $2010-07-15$
rascal>DT.justTime;
datetime: $T09:15:23.123+03:00$
rascal>DT.century;
int: 20
Pitfalls

In normal parlance, the year 2010 is in the 21th century. The century field, however, just returns the century component of a given year, e.g., for 2010 this is 20.

DateTime Equal
Synopsis

Equality on datetime values.

Syntax

Exp1 == Exp2

Types
Exp1 Exp2 Exp1 == Exp2

datetime

datetime

bool

Description

Yields true if both arguments are identical datetime values and false otherwise.

Examples
rascal>$2010-07-15$ == $2010-07-15$;
bool: true
rascal>$2010-07-15$ == $2010-07-14$;
bool: false
DateTime Field Selection
Synopsis

Select a field from a datetime value.

Syntax

Exp . Name

Types
Exp Name Exp . Name

datetime

depends on field

Description

Field selection applies to datetime values. Name should be one of the supported fields listed in DateTime and returns the value of that field. Name stands for itself and is not evaluated.

DateTime GreaterThan
Synopsis

Greater than operator on datetime values.

Syntax

Exp1 > Exp2

Types
Exp1 Exp2 Exp1 > Exp2

datetime

datetime

bool

Description

Yields true if the datetime value of Exp1 is later in time than the datetime value of Exp2, and false otherwise.

Examples
rascal>$2010-07-15$ > $2010-07-14$;
bool: true
rascal>$2011-07-15$ > $2010-07-15$;
bool: true
DateTime GreaterThanOrEqual
Synopsis

Greater than or equal operator on datetime values.

Syntax

Exp1 >= Exp2

Types
Exp1 Exp2 Exp1 >= Exp2

datetime

datetime

bool

Description

Yields true if the datetime value of Exp1 is later in time than the datetime value of Exp2 or if both values are equal, and false otherwise.

Examples
rascal>$2011-07-15$ >= $2010-07-15$;
bool: true
rascal>$2010-07-15$ >= $2010-07-14$;
bool: true
DateTime LessThan
Synopsis

Less than operator on datetime values.

Syntax

Exp1 < Exp2

Types
Exp1 Exp2 Exp1 < Exp2

datetime

datetime

bool

Description

Yields true if the datetime value of Exp1 is earlier in time than the datetime value of Exp2, and false otherwise.

Examples
rascal>$2010-07-14$ < $2010-07-15$;
bool: true
rascal>$2011-07-15$ < $2010-07-14$;
bool: false
DateTime LessThanOrEqual
Synopsis

Less than or equal operator on datetime values.

Syntax

Exp1Exp2

Types
Exp1 Exp2 Exp1Exp2

datetime

datetime

bool

Description

Yields true if the datetime value of Exp1 is earlier in time than the datetime value of Exp2 or if the values of Exp1 and Exp2 are equal, and false otherwise.

Examples
rascal>$2010-07-15$ <= $2010-07-15$;
bool: true
rascal>$2011-07-15$ <= $2010-07-14$;
bool: false
DateTime NotEqual
Synopsis

Not equal operator on datetime values.

Syntax

Exp1 != Exp2

Types
Exp1 Exp2 Exp1 != Exp2

datetime

datetime

bool

Description

Yields true if both arguments are different datetime values and false otherwise.

Examples
rascal>$2010-07-15$ != $2010-07-14$;
bool: true
rascal>$2010-07-15$ != $2010-07-15$;
bool: false

3.1.4. Integer

Synopsis

Integer values.

Syntax

sequence of digits of arbitrary length.

Types

int

Description

The integer values are represented by the type int and are written as usual. They can be arbitrarily large.

See Number for all operations and functions on integers, reals and numbers.

Examples
  • 12

  • 0

  • -123456789

3.1.5. List

Synopsis

List values.

Syntax

[ Exp1, Exp2, …​ ]

Types
Exp1 Exp2 …​ [ Exp1, Exp2, …​ ]

T1

T2

…​

list[lub(T1, T2, …​ )]

Description

A list is an ordered sequence of values and has the following properties:

  • All elements have the same static type.

  • The order of the elements matters.

  • A list may contain an element more than once.

The type of a list has the form list[T], where T is an arbitrary type.

When a value or variable of type list occurs inside a list, that list value is inserted as list element. To achieve splicing of these elements, i.e., the insertion of the elements of the list value rather than the whole list, it has to be prefixed by the splice operator *.

The following operators are provided on list:

There are also library functions available for List.

Examples
rascal>[1, 2, 3];
list[int]: [1,2,3]
rascal>[<1,10>, <2,20>, <3,30>];
lrel[int,int]: [
  <1,10>,
  <2,20>,
  <3,30>
]
rascal>[1, "b", 3];
list[value]: [1,"b",3]
rascal>[<"a",10>, <"b",20>, <"c",30>];
lrel[str,int]: [
  <"a",10>,
  <"b",20>,
  <"c",30>
]
rascal>[["a", "b"], ["c", "d", "e"]];
list[list[str]]: [
  ["a","b"],
  ["c","d","e"]
]

List splicing works as follows: by prefixing L by the splice operator, its elements are included as elements in the enclosing list:

rascal>L = [1, 2, 3];
list[int]: [1,2,3]
rascal>[10, L, 20];
list[value]: [
  10,
  [1,2,3],
  20
]
rascal>[10, *L, 20];
list[int]: [10,1,2,3,20]
List Append
Synopsis

Append an element at the end of a list

Types
Exp1 Exp2 Exp1 + Exp2

list[T1]

T2

list[lub(T1,T2)]

Description

The operator + appends an element at the end of a list. The + is one of those Operators which are overloaded. It can also mean List Insert or List Concatenation for example.

Examples
rascal>[] + 1;
list[int]: [1]
rascal>[1] + 2;
list[int]: [1,2]
Pitfalls:

This is concatenation:

rascal>[1] + [2]
list[int]: [1,2]

To append a list to a list, use extra brackets:

rascal>[1] + [[2]]
list[value]: [
  1,
  [2]
]
List Comprehension
Synopsis

A list comprehension generates a list value.

Syntax

[ Exp1, Exp2, …​ | Gen1, Gen2, …​ ]

Types
Exp1 Exp2 …​ [ Exp1, Exp2, …​ | Gen1, Gen2, …​ ]

T1

T2

…​

list[ lub( T1, T2, …​ ) ]

Description

A list comprehension consists of a number of contributing expressions Exp1, Exp2, …​ and a number of generators Gen1, Gen2, Gen3, …​ that are evaluated as described in Comprehensions.

Examples

Computing a list of squares of the numbers from 0 to 10 that are divisible by 3:

rascal>[n * n | int n <- [0 .. 10], n % 3 == 0];
list[int]: [0,9,36,81]

But we can also include the relevant n in the resulting list:

rascal>[n, n * n | int n <- [0 .. 10], n % 3 == 0];
list[int]: [0,0,3,9,6,36,9,81]
List Concatenation
Synopsis

Concatenate two lists.

Syntax

Exp1 + Exp2

Types
Exp1 Exp2 Exp1 + Exp2

list[T1]

list[T2]

list[lub(T1,T2)]

Description

The + operator concatenates the elements of the two lists in order of appearance.

Note that the same operator is overloaded for List Insert and List Append.

Examples
rascal>[1, 2, 3] + [4, 5, 6];
list[int]: [1,2,3,4,5,6]
rascal>[] + [1]
list[int]: [1]
rascal>[1] + []
list[int]: [1]
rascal>[1] + [2] + [3]
list[int]: [1,2,3]

And overloaded usage for insert and append looks like:

rascal>1 + []
list[int]: [1]
rascal>[] + 1
list[int]: [1]
List Difference
Synopsis

The difference between two lists.

Syntax

Exp1 - Exp2

Types
Exp1 Exp2 Exp1 - Exp2

list[T1]

list[T2]

list[lub(T1,T2)]

list[T1]

T2

list[lub(T1,T2)]

Description

If both Exp1 and Exp2 have a list as value, the result is the difference of these two list values. If Exp2 does not have a list as value, it is first converted to a list before the difference is computed. The difference is computed by taking the successive elements of the second list and removing the first occurrence of that element in the first list.

Examples
rascal>[1, 2, 3, 4] - [1, 2, 3];
list[int]: [4]
rascal>[1, 2, 3, 4] - [3];
list[int]: [1,2,4]
rascal>[1, 2, 3, 4] - 3;
list[int]: [1,2,4]
rascal>[1, 2, 3, 4] - [5, 6, 7];
list[int]: [1,2,3,4]
rascal>[1, 2, 3, 1, 2, 3] - [1];
list[int]: [2,3,1,2,3]
rascal>[1, 2, 3, 1, 2, 3] - [1, 2];
list[int]: [3,1,2,3]
List Equal
Synopsis

Equality on lists.

Syntax

Exp1 == Exp2

Types
Exp1 Exp2 Exp1 == Exp2

list[T1]

list[T2]

bool

Description

Yields true if both arguments are equal lists and false otherwise.

Examples
rascal>[1, 2, 3] == [1, 2, 3];
bool: true
rascal>[1, 2, 3] == [3, 2, 1];
bool: false
List Insert
Synopsis

add an element in front of a list

Types
Exp1 Exp2 Exp1 + Exp2

T1

list[T2]

list[lub(T1,T2)]

Description

The + operator can insert an element in front of a list. Note that + is one of the Operators that is overloaded, it is also List Concatenation and List Append for example.

Examples
rascal>1 + []
list[int]: [1]
rascal>1 + [2]
list[int]: [1,2]
rascal>1 + [2,3]
list[int]: [1,2,3]
Pitfalls

This is concatenation:

rascal>[1] + [2]
list[int]: [1,2]

To insert a list as an element, use extra brackets:

rascal>[[1]] + [2]
list[value]: [
  [1],
  2
]
List Intersection
Synopsis

Intersection of two lists.

Syntax

Exp1 & Exp2

Types
Exp1 Exp2 Exp1 & Exp2

list[T1]

list[T2]

list[lub(T1,T2)]

Description

Returns the intersection of the two list values of Exp1 and Exp2, i.e., the list value of Exp1 with all elements removed that do not occur in the list value of Exp2.

Examples
rascal>[1, 2, 3, 4, 5] & [4, 5, 6];
list[int]: [4,5]
List NotEqual
Synopsis

Not equal operator on lists.

Syntax

Exp1 != Exp2

Types
Exp1 Exp2 Exp1 != Exp2

list[T1]

list[T2]

bool

Description

Yields true if both arguments are unequal lists and false otherwise.

Examples
rascal>[1, 2, 3] != [3, 2, 1];
bool: true
rascal>[1, 2, 3] != [1, 2, 3];
bool: false
List Product
Synopsis

Compute the product of two lists.

Syntax

Exp1 * Exp2

Types
Exp1 Exp2 Exp1 * Exp2

list[T1]

list[T2]

list[tuple[T1,T2]]

Description

Yields a list of tuples resulting from the product of the values of Exp1 and Exp2. It contains a tuple for each combination of values from both arguments.

Examples
rascal>[1, 2, 3] * [4, 5, 6];
lrel[int,int]: [
  <1,4>,
  <1,5>,
  <1,6>,
  <2,4>,
  <2,5>,
  <2,6>,
  <3,4>,
  <3,5>,
  <3,6>
]

Here is a concise way to create a deck of cards:

rascal>["clubs", "hearts", "diamonds", "spades"] * [1 .. 13];
lrel[str,int]: [
  <"clubs",1>,
  <"clubs",2>,
  <"clubs",3>,
  <"clubs",4>,
  <"clubs",5>,
  <"clubs",6>,
  <"clubs",7>,
  <"clubs",8>,
  <"clubs",9>,
  <"clubs",10>,
  <"clubs",11>,
  <"clubs",12>,
  <"hearts",1>,
  <"hearts",2>,
  <"hearts",3>,
  <"hearts",4>,
  <"hearts",5>,
  <"hearts",6>,
  <"hearts",7>,
  <"hearts",8>,
  <"hearts",9>,
  <"hearts",10>,
  <"hearts",11>,
  <"hearts",12>,
  <"diamonds",1>,
  <"diamonds",2>,
  <"diamonds",3>,
  <"diamonds",4>,
  <"diamonds",5>,
  <"diamonds",6>,
  <"diamonds",7>,
  <"diamonds",8>,
  <"diamonds",9>,
  <"diamonds",10>,
  <"diamonds",11>,
  <"diamonds",12>,
  <"spades",1>,
  <"spades",2>,
  <"spades",3>,
  <"spades",4>,
  <"spades",5>,
  <"spades",6>,
  <"spades",7>,
  <"spades",8>,
  <"spades",9>,
  <"spades",10>,
  <"spades",11>,
  <"spades",12>
]
List Slice
Synopsis

Retrieve a slice of a list.

Syntax
  • Exp1 [ Exp2 .. Exp4]

  • Exp1 [ Exp2 , Exp3 .. Exp4]

where Exp2 and Exp4 are optional.

Types
Exp1 Exp2 Exp3 Exp4

Exp1 [ Exp2 .. Exp4 ] or Exp1 [ Exp2 , Exp3 .. Exp4]

list[T1]

int

int

Description

List slicing uses the integer values of Exp2 and Exp4 to determine the begin (inclusive) and end (exclusive) of a slice from the list value L of Exp1. Negative indices count from the end of the list backwards. Using the second form, an extra index Exp3 is given that determines the index of the second element in the slice and establishes the step between successive elements in the slice. The default step is 1. If end is smaller than begin, the slice is constructed backwards.

Let Len be the length of L and let N2, N3 and N4 be the respective values of the expressions Exp2, Exp2 and Exp2 when they are present.

The slice parameters begin, end, and step are determined as follows:

  • Exp2:

    • If Exp2 is absent, then begin = 0.

    • Otherwise, if N2 >= 0 then begin = N2 else begin = N2 + Len.

  • Exp4:

    • If Exp4 is absent, then end = Len.

    • Otherwise, if N4 >= 0, then end = N4 else end = N4 + Len.

  • Exp3:

    • If Exp3 is absent, then if begin < end then step = 1 else step = -1.

    • Otherwise, if begin < end, then step = N3 - begin else step = begin - N3.

Now, the constraints 0 ⇐ begin < Len and 0 < end < Len should hold, otherwise the exception IndexOutOfBounds is thrown.

The slice consists of the elements L[begin], L[begin+step], L[end - step]. When begin >= end, the elements are listed in reverse order.

Examples

Consider the list L = [0, 10, 20, 30, 40, 50, 60, 70, 80]; as running example.

Here is a view on L that will help to correlate positive and negative indices:

i 0 1 2 3 4 5 6 7 8

L[i]

0

10

20

30

40

50

60

70

80

-i

-9

-8

-7

-6

-5

-4

-3

-2

-1

Some common use cases (with beginend):

Slice Means:

L[begin..end]

elements with indices begin through end-1

L[begin..]

elements with indices begin through the rest of the list

L[..end]

elements with indices from the beginning through end-1

L[..]

the whole list

L[-1]

last element of the list

L[-2..]

the last two elements of the list

L[..-2]

all elements except the last two.

Let’s put this into practice now.

rascal>L = [0, 10, 20, 30, 40, 50, 60, 70, 80];
list[int]: [0,10,20,30,40,50,60,70,80]

Slices with begin < end

rascal>L[1..3];
list[int]: [10,20]
rascal>L[1..];       // empty end => end of list
list[int]: [10,20,30,40,50,60,70,80]
rascal>L[..3];       // empty begin => first element of list
list[int]: [0,10,20]
rascal>L[..];        // both empty => whole list
list[int]: [0,10,20,30,40,50,60,70,80]

Slices with begin >= end

rascal>L[3..1];      // slice contains elements with indices 3 and 2 (in that order)
list[int]: [30,20]
rascal>L[3..3];      // empty slice when begin == end
list[int]: []

Slices with negative begin or end:

rascal>L[2..-2];     // equivalent to L[2..7]
list[int]: [20,30,40,50,60]
rascal>L[2..7];
list[int]: [20,30,40,50,60]
rascal>L[-4..-2];    // equivalent to L[5..7]
list[int]: [50,60]
rascal>L[5..7];
list[int]: [50,60]

Slices with an explicit second index:

rascal>L[1,3..6];
list[int]: [10,30,50]
rascal>L[5,3..];
list[int]: [50,30,10]

Explore error cases:

rascal>L[..10];
list[int]: [0,10,20,30,40,50,60,70,80]
rascal>L[1..20];
list[int]: [10,20,30,40,50,60,70,80]
List Splice
Synopsis

Splice the elements of a list in an enclosing list.

Types
Exp Exp1 Expn [Exp1, …​, Exp, …​, Expn]

T

T1

Tn

list[lub(T1, …​, T, …​,Tn)]

Description

The operator * splices the elements of a list in an enclosing list.

Examples

Consider the following list in which the list [10, 20, 30] occurs as list element. It has as type list[value]:

rascal>[1, 2, [10, 20, 30], 3, 4];
list[value]: [
  1,
  2,
  [10,20,30],
  3,
  4
]

The effect of splicing the same list element in the enclosing list gives a flat list of type list[int]:

rascal>[1, 2, *[10, 20, 30], 3, 4];
list[int]: [1,2,10,20,30,3,4]

The same example can be written as:

rascal>L = [10, 20, 30];
list[int]: [10,20,30]
rascal>[1, 2, *L, 3, 4];
list[int]: [1,2,10,20,30,3,4]
Benefits

in which nested lists are handled.

List StrictSubList
Synopsis

The strict sublist operator on lists.

Syntax

Exp1 < Exp2

Types
Exp1 Exp2 Exp1 < Exp2

list[T1]

list[T2]

bool

Description

Yields true if the value of Exp1 is a strict sublist of the value of Exp2, and false otherwise.

Examples
rascal>[1, 2, 3] < [1, 2, 3, 4];
bool: true
rascal>[1, 2, 3, 4] < [1, 2, 3, 4];
bool: false
rascal>[1, 3, 5] < [1, 2, 3, 4, 5]
bool: true
List StrictSuperList
Synopsis

The strict super list operator on lists.

Syntax

Exp1 > Exp2

Types
Exp1 Exp2 Exp1 > Exp2

list[T1]

list[T2]

bool

Description

Yields true if the value of Exp2 is a strict sublist of the value of Exp1, and false otherwise.

Examples
rascal>[1, 2, 3, 4] > [1, 2, 3];
bool: true
rascal>[1, 2, 3, 4] > [1, 2, 3, 4];
bool: false
rascal>[1, 2, 3, 4] > [1, 2, 3];
bool: true
rascal>[1, 2, 3, 4, 5] > [1, 3, 5]
bool: true
List SubList
Synopsis

The sublist operator on lists.

Syntax

Exp1Exp2

Types
Exp1 Exp2 Exp1Exp2

list[T1]

list[T2]

bool

Description

Yields true if the value of Exp1 is equal to or a sublist of the value of Exp2, and false otherwise.

Examples
rascal>[1, 2, 3] <= [1, 2, 3, 4];
bool: true
rascal>[1, 2, 3] <= [1, 2, 3];
bool: true
rascal>[1, 3, 5] <= [1, 2, 3, 4, 5];
bool: true
List Subscription
Synopsis

Retrieve a list element via its index.

Syntax

Exp1 [ Exp2 ]

Types
Exp1 Exp2 Exp1 [ Exp2 ]

list[T1]

int

T1

Description

List subscription uses the integer value of Exp2 as index in the list value of Exp1. The value of Exp2 should be greater or equal 0 and less than the number of elements in the list. If this is not the case, the exception IndexOutOfBounds is thrown.

Examples

Introduce a list, assign it to L and retrieve the element with index 1:

rascal>L = [10, 20, 30];
list[int]: [10,20,30]
rascal>L[1];
int: 20

Explore an error case:

rascal>L[5];
|prompt:///|(2,1,<1,2>,<1,3>): IndexOutOfBounds(5)
	at $shell$(|prompt:///|(0,5,<1,0>,<1,5>))
 
ok
List SuperList
Synopsis

The super list operator on lists.

Syntax

Exp1 >= Exp2

Types
Exp1 Exp2 Exp1 >= Exp2

list[T1]

list[T2]

bool

Description

Yields true if the value of Exp2 is equal to or a sublist of the value of Exp1, and false otherwise.

Examples
rascal>[1, 2, 3, 4] >= [1, 2, 3];
bool: true
rascal>[1, 2, 3, 4] >= [1, 2, 3, 4];
bool: true
rascal>[1, 2, 3, 4] >= [1, 2, 3];
bool: true
rascal>[1, 2, 3, 4, 5] >= [1, 3, 5]
bool: true
List in
Synopsis

Membership test on list elements.

Syntax

Exp1 in Exp2

Types
Exp1 Exp2 Exp1 in Exp2

T1 <: T2

list[T2]

bool

Description

Yields true if the value of Exp1 occurs as element in the value of Exp2 and false otherwise. The type of Exp1 should be compatible with the element type of Exp2.

Examples
rascal>2 in [1, 2, 3];
bool: true
rascal>4 in [1, 2, 3];
bool: false
List notin
Synopsis

Negated membership test on lists.

Syntax

Exp1 notin Exp2

Types
Exp1 Exp2 Exp1 notin Exp2

T1 <: T2

list[T2]

bool

Description

Yields true if the value of Exp1 does not occur as element in the value of Exp2 and false otherwise. The type of Exp1 should be compatible with the element type of Exp2.

Examples
rascal>4 notin [1, 2, 3];
bool: true
rascal>2 notin [1, 2, 3];
bool: false

3.1.6. ListRelation

Synopsis

ListRelation values.

Syntax

[ < Exp11, Exp12, …​ > , < Exp21, Exp22, …​ > , …​ ]

Types
Exp11 Exp12 …​ { < Exp11, Exp12, …​ > , …​ }

T1

T2

…​

lrel[T1, T2, …​ ]

Description

A list relation is a list of elements with the following property:

  • All elements have the same static tuple type.

ListRelations are thus nothing more than lists of tuples, but since they are used so often we provide a shorthand notation for them. ListRelations are represented by the type lrel[T1 L1, T2 L2, …​ ], where T1, T2, …​ are arbitrary types and L1, L2, …​ are optional labels. It is a shorthand for list[tuple[T1 L1, T2 L2, …​ ]].

An n-ary list relation with m tuples is denoted by [< E11, E12, …​, E1n>,< E21, E22, …​, E2n>, …​, < Em1, Em2, …​, Emn>], where the Eij are expressions that yield the desired element type Ti.

Since list relations are a form of list all operations (see List) and functions (see [Prelude-List]) are also applicable to relations.

The following additional operators are provided for list relations:

There are also library functions available for ListRelation.

Examples
rascal>[<1,10>, <2,20>, <3,30>]
lrel[int,int]: [
  <1,10>,
  <2,20>,
  <3,30>
]

instead of lrel[int,int] we can also give list[tuple[int,int]] as type of the above expression remember that these types are interchangeable.

rascal>[<"a",10>, <"b",20>, <"c",30>]
lrel[str,int]: [
  <"a",10>,
  <"b",20>,
  <"c",30>
]
rascal>[<"a", 1, "b">, <"c", 2, "d">]
lrel[str,int,str]: [
  <"a",1,"b">,
  <"c",2,"d">
]
ListRelation CartesianProduct
Synopsis

Cartesian product of two list relation values.

Syntax

Exp1 * Exp2

Types
Exp1 Exp2 Exp1 * Exp2

list[T1]

list[T2]

lrel[T1, T2]

Description

Returns a binary relation that is the Cartesian product of two lists.

Examples
rascal>[1, 2, 3] * [9];
lrel[int,int]: [
  <1,9>,
  <2,9>,
  <3,9>
]
rascal>[1, 2, 3] * [10, 11];
lrel[int,int]: [
  <1,10>,
  <1,11>,
  <2,10>,
  <2,11>,
  <3,10>,
  <3,11>
]
ListRelation Composition
Synopsis

Composition of two list relation values.

Syntax

Exp1 o Exp2

Types
Exp1 Exp2 Exp1 o Exp2

lrel[T1, T2]

lrel[T2, T3]

lrel[T1, T3]

Description

Returns the composition of two binary list relations.

Examples
rascal>[<1,10>, <2,20>, <3,15>] o [<10,100>, <20,200>];
lrel[int,int]: [
  <1,100>,
  <2,200>
]
Pitfalls

We use the letter o as operator and this may conflict other defined names.

ListRelation FieldSelection
Synopsis

Select a field (column) from a list relation value.

Syntax

Exp . Name

Types

Exp

Exp . Name

lrel[T1 L1, T2 L2, …​ ]

Description

Exp should evaluate to a list relation that has an i-th field label Li that is identical to Name. Return a list with all values of that field. Name stands for itself and is not evaluated.

Examples
rascal>lrel[str street, int nm] R = [<"abc", 1>, <"abc", 2>, <"def", 4>, <"def", 5>];
lrel[str street,int nm]: [
  <"abc",1>,
  <"abc",2>,
  <"def",4>,
  <"def",5>
]
rascal>R.street;
list[str]: ["abc","abc","def","def"]
ListRelation Join
Synopsis

Join two list relation values.

Syntax

Exp1 join Exp2

Types
Exp1 Exp2 Exp1 join Exp2

lrel[ T11, T12, T13, …​ ]

lrel[ T21, T22, T23, …​ ]

lrel[ T11, T12, T13, …​, T21, T22, T23, …​ ]

Description

ListRelation resulting from the natural join of the list relation values of the two arguments. This list relation contains tuples that are the result from concatenating the elements from both arguments.

Examples
rascal>[<1,2>, <10,20>] join [<2,3>];
lrel[int,int,int,int]: [
  <1,2,2,3>,
  <10,20,2,3>
]
rascal>[<1,2>] join [3, 4];
lrel[int,int,int]: [
  <1,2,3>,
  <1,2,4>
]
rascal>[<1,2>, <10,20>] join [<2,3>, <20,30>];
lrel[int,int,int,int]: [
  <1,2,2,3>,
  <1,2,20,30>,
  <10,20,2,3>,
  <10,20,20,30>
]
ListRelation Reflexive Transitive Closure
Synopsis

The reflexive transitive closure of a binary list relation.

Syntax

Exp *

Types
Exp Exp *

lrel[T1, T2]

lrel[T1, T2]

Description

Reflexive transitive closure is defined by repeated composition of a list relation. If we define for a given list relation R:

  • R0 = identity relation = [<a, a>, <b, b> | <a, b> ← R]

  • R1 = R

  • R2 = R o R

  • R3 = R o R2

  • …​

then the reflexive transitive closure R* can be defined in two ways: (also see ListRelation Transitive Closure):

  • R* = R0 + R1 + R2 + R3 + …​

  • R* = R0 + R+

Examples
rascal>[<1,2>, <2,3>, <3,4>]*;
lrel[int,int]: [
  <1,2>,
  <2,3>,
  <3,4>,
  <1,3>,
  <2,4>,
  <1,4>,
  <4,4>,
  <3,3>,
  <2,2>,
  <1,1>
]
ListRelation Subscription
Synopsis

Indexing of a list relation via tuple values.

Syntax
  • Exp0 [ Exp1, Exp2, …​ Expn]

  • Exp0 [ Exp1]

Variant 1
Types
Exp0 Exp1 Exp2 …​ Exp0 [ Exp1, Exp2, …​ ]

lrel[T1, T2, …​ Tm]

int

int

…​

lrel[Tn, Tn+1, …​ Tm]

Variant 2

Exp0

Exp1

Exp0 [ Exp1 ]

lrel[T1, T2, …​ Tm]

list[T1]

Description

ListRelation resulting from subscription of a ListRelation Exp0.

Variant 1

Subscription with the index values of Exp1, Exp2, …​. The result is a ListRelation with all tuples that have these index values as first elements with the index values removed from the tuple. If the resulting tuple has only a single element, a list is returned instead of a relation. A wildcard _ as index value matches all possible values at that index position.

Variant 2

Subscription with a set of the index values of Exp1. The result is a ListRelation with all tuples that have these index values as first element with the index values removed from the tuple.

Examples
rascal>R = [<1,10>, <2,20>, <1,11>, <3,30>, <2,21>];
lrel[int,int]: [
  <1,10>,
  <2,20>,
  <1,11>,
  <3,30>,
  <2,21>
]
rascal>R[1];
tuple[int,int]: <2,20>
rascal>R[{1}];
list[int]: [10,11]
rascal>R[{1, 2}];
list[int]: [10,20,11,21]
rascal>RR = [<1,10,100>,<1,11,101>,<2,20,200>,<2,22,202>,
>>>>>>>              <3,30,300>];
lrel[int,int,int]: [
  <1,10,100>,
  <1,11,101>,
  <2,20,200>,
  <2,22,202>,
  <3,30,300>
]
rascal>RR[1];
tuple[int,int,int]: <1,11,101>
rascal>RR[1,_];
list[int]: [100,101]

Introduce a relation with economic data and assign it to GDP:

rascal>lrel[str country, int year, int amount] GDP =
>>>>>>>[<"US", 2008, 14264600>, <"EU", 2008, 18394115>,
>>>>>>> <"Japan", 2008, 4923761>, <"US", 2007, 13811200>,
>>>>>>> <"EU", 2007, 13811200>, <"Japan", 2007, 4376705>];
lrel[str country,int year,int amount]: [
  <"US",2008,14264600>,
  <"EU",2008,18394115>,
  <"Japan",2008,4923761>,
  <"US",2007,13811200>,
  <"EU",2007,13811200>,
  <"Japan",2007,4376705>
]

and then retrieve the information for the index "Japan":

rascal>GDP["Japan"];
lrel[int,int]: [
  <2008,4923761>,
  <2007,4376705>
]

or rather for the indices "Japan" and 2008:

rascal>GDP["Japan", 2008];
list[int]: [4923761]
ListRelation Transitive Closure
Synopsis

Transitive closure on binary list relation values.

Syntax

Exp +

Types
Exp Exp +

lrel[T1, T2]

lrel[T1, T2]

Description

Returns the transitive closure of a binary listrelation. Transitive closure is defined by repeated composition of a relation. If we define for a given relation R:

  • R1 = R

  • R2 = R o R

  • R3 = R o R2

  • …​

then the transitive closure R+ can be defined as

  • R+ = R1 + R2 + R3 + …​

Examples
rascal>[<1,2>, <2,3>, <3,4>]+;
lrel[int,int]: [
  <1,2>,
  <2,3>,
  <3,4>,
  <1,3>,
  <2,4>,
  <1,4>
]

3.1.7. Location

Synopsis

(Source code) location values.

Syntax

| Uri | ( O, L, < BL, BC > , < EL,EC > ) where:

  • Uri is an arbitrary Uniform Resource Identifier (URI).

  • O and L are integer expressions giving the offset of this location to the begin of file, respectively, its length.

  • BL and BC are integers expressions giving the begin line and begin column.

  • EL and EC are integers expressions giving the end line and end column.

The part following the second pipe symbol (|) is optional.

Types

loc

Description

Location values are represented by the type loc and serve the following purposes:

  • Providing a uniform mechanism for accessing local or remote files. This is used in all IO-related library functions.

  • If the optional part is present they serve as text coordinates in a specific local or remote source file. This is very handy to associate a source code location which extracted facts.

URIs are explained in Uniform Resource Identifier. From their original definition in RFC3986 we cite the following useful overview of an URI:

         foo://example.com:8042/over/there?name=ferret#nose
         \_/   \______________/\_________/ \_________/ \__/
          |           |            |            |        |
       scheme     authority       path        query   fragment
          |   _____________________|__
         / \ /                        \
         urn:example:animal:ferret:nose

The elements of a location value can be accessed and modified using the standard mechanism of field selection and field assignment. The corresponding field names are:

  • top: the URI of the location without precise positioning information (offset, length, begin, end).

  • uri: the URI of the location as a string. Also subfields of the URI can be accessed:

    • scheme: the scheme (or protocol) to be used;

    • authority: the domain where the data are located, as a str;

    • host: the host where the URI is hosted (part of authority), as a str;

    • port: port on host (part of authority), as a int;

    • path: path name of file on host, as a str;

    • extension: file name extension, as a str;

    • query: query data, as a str;

    • fragment: the fragment name following the path name and query data, as a str;

    • user: user info (only present in schemes like mailto), as a str;

    • parent : removes the last segment from the path component, if any, as a loc;

    • file : the last segment of the path, as a str;

    • ls : the contents of a directory, if the loc is a directory, as a list[loc].

  • offset: start of text area.

  • length: length of text area.

  • begin.line, begin.column: begin line and column of text area.

  • end.line, end.column end line and column of text area.

Supported protocols are:

Scheme name and pattern Description

http://host:_port_/path?query#fragment

access a remote file via the web.

file:///path

access a local file on the file system.

cwd:///path

access the current working directory (the directory from which Rascal was started).

home:///path

access the home directory of the user.

std:///path

access the Rascal standard library.

tmp:///path

access the temporay file directory.

jar:_url_!/[entry]

access any entry in a zip file (or a jar)

rascal://qualifiedModulename

access the source code of a Rascal module name

project://projectName/projectRelativePath

access a project in the current instance of Eclipse.

bundleresource://bundleId/bundleRelativePath

access OSGI bundles. Only active in Eclipse context

Examples

Locations with specific position information should always be generated automatically but for the curious here is an example:

rascal>|file:///home/paulk/pico.trm|(0,1,<2,3>,<4,5>)
loc: |file:///home/paulk/pico.trm|(0,1,<2,3>,<4,5>)

Note that this is equivalent to using the home scheme:

rascal>|home://pico.trm|(0,1,<2,3>,<4,5>)
loc: |home://pico.trm|(0,1,<2,3>,<4,5>)

You could read a webpage:

rascal>import IO;
ok
rascal>println(readFile(|http://www.example.org|))
<!doctype html>
<html>
<head>
    <title>Example Domain</title>

    <meta charset="utf-8" />
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style type="text/css">
    body {
        background-color: #f0f0f2;
        margin: 0;
        padding: 0;
        font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;

    }
    div {
        width: 600px;
        margin: 5em auto;
        padding: 2em;
        background-color: #fdfdff;
        border-radius: 0.5em;
        box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
    }
    a:link, a:visited {
        color: #38488f;
        text-decoration: none;
    }
    @media (max-width: 700px) {
        div {
            margin: 0 auto;
            width: auto;
        }
    }
    </style>
</head>

<body>
<div>
    <h1>Example Domain</h1>
    <p>This domain is for use in illustrative examples in documents. You may use this
    domain in literature without prior coordination or asking for permission.</p>
    <p><a href="https://www.iana.org/domains/example">More information…​</a></p>
</div>
</body>
</html>

ok

Addition on locations creates longer paths:

rascal>x = |tmp://myTempDirectory|;
loc: |tmp://myTempDirectory|
rascal>x += "myTempFile.txt";
loc: |tmp://myTempDirectory/myTempFile.txt|
Location AddSegment
Synopsis

Locations can be concatenated with strings to add segments to the path component

Syntax

Loc + Str

Types
Loc Str Loc + Str

loc

str

loc

Description

Adds a segment to the path component of a location. This concatenation introduces a path separator (/) automatically.

Examples
rascal>|tmp:///myDir| + "myFile";
loc: |tmp:///myDir/myFile|

To get the original back, you can use the parent field:

rascal>(|tmp:///myDir| + "myFile").parent
loc: |tmp:///myDir|
Location Equal
Synopsis

Equality operator on locations.

Syntax

Exp1 == Exp2

Types
Exp1 Exp2 Exp1 == Exp2

loc

loc

bool

Description

Yields true if both arguments are identical and false otherwise.

Location FieldSelection
Synopsis

Field selection on locations.

Syntax

Exp . Name

Types
Exp Name Exp . Name

loc

Depends on field

Description

Field selection applies to locations. Name should be one of the supported fields listed in Location and returns the value of that field. Name stands for itself and is not evaluated.

Location GreaterThan
Synopsis

The greater than operator on location values.

Syntax

Exp1 > Exp2

Types
Exp1 Exp2 Exp1 > Exp2

loc

loc

bool

Description

Yields true if the location value of Exp1 strictly textually encloses the location value of Exp2, and false otherwise.

Location GreaterThanOrEqual
Synopsis

The greater than or equal operator on location values.

Syntax

Exp1 >= Exp2

Types
Exp1 Exp2 Exp1 >= Exp2

loc

loc

bool

Description

Yields true if the location value of Exp1 textually encloses the location value of Exp2, and false otherwise.

Location LessThan
Synopsis

The less than operator on location values.

Syntax

Exp1 < Exp2

Types
Exp1 Exp2 Exp1 < Exp2

loc

loc

bool

Description

Yields true if the location value of Exp1 is strictly textually contained in the location value of Exp2, and false otherwise.

Location LessThanOrEqual
Synopsis

The less than or equal operator on location values.

Syntax

Exp1Exp2

Types
Exp1 Exp2 Exp1Exp2

loc

loc

bool

Description

Yields true if the location value of Exp1 is textually contained in the location value of Exp2, and false otherwise.

NotEqual
Synopsis

The not equal operator on location values.

Syntax

Exp1 != Exp2

Types
Exp1 Exp2 Exp1 != Exp2

loc

loc

bool

Description

Yields true if both arguments are not identical and false otherwise.

3.1.8. Map

Synopsis

Map values.

Syntax

( KeyExp1 : ValExp1, KeyExp2 : ValExp2, …​ )

Types
KeyExp1 ValExp1 KeyExp2 ValExp2 …​ ( KeyExp1 : ValExp1, KeyExp2 : ValExp2, …​ )

TK1

TV1

TK2

TV2

…​

map[lub(TK1, TK2, …​ ) , lub(TV1, TV2, …​ )]

Description

A map is a set of key/value pairs and has the following properties:

  • Key and value may have different static types.

  • A key can only occur once.

Maps resemble functions rather than relations in the sense that only a single value can be associated with each key.

The following functions are provided for maps:

Examples
rascal>("pear" : 1, "apple" : 3, "banana" : 0);
map[str, int]: ("banana":0,"pear":1,"apple":3)
Map Composition
Synopsis

Composition of two map values.

Syntax

Exp1 o Exp2

Types
Exp1 Exp2 Exp1 o Exp2

map[T1, T2]

map[T2, T3]

map[T1, T3]

Description

Returns the composition of two maps.

Examples
rascal>import Map;
ok
rascal>("one" : 1, "two" : 2) o (1 : 10, 2 : 20);
map[str, int]: ("one":10,"two":20)
Pitfalls

We use the letter o as operator and this may conflict other defined names.

Map Comprehension
Synopsis

A map comprehension generates a map value.

Syntax

( Exp1 : Exp2 | Gen1, Gen2, …​ )

Types
Exp1 Exp2 ( Exp1 : Exp2 | Gen1, Gen2, …​ )

T1

T2

map[T1, T2]

Description

A map comprehension consists of a number of two contributing expressions Exp1 (for key values), and Exp2 (the values associated with those key values) and a number of generators Gen1, Gen2, Gen3, …​ that are evaluated as described in Comprehensions.

Examples

Introduce a map of fruits:

rascal>fruits = ("pear" : 1, "apple" : 3, "banana" : 0, "berry" : 25, "orange": 35);
map[str, int]: ("banana":0,"pear":1,"orange":35,"berry":25,"apple":3)
rascal>import String;
ok

Use a map comprehension to filter fruits with a name of at most 5 characters:

rascal>(fruit : fruits[fruit] | fruit <- fruits, size(fruit) <= 5);
map[str, int]: ("pear":1,"berry":25,"apple":3)

Use a map comprehension to filter fruits with an associated value larger than 10:

rascal>(fruit : fruits[fruit] | fruit <- fruits, fruits[fruit] > 10);
map[str, int]: ("orange":35,"berry":25)
Map Difference
Synopsis

The difference between two maps.

Syntax

Exp1 - Exp2

Types
Exp1 Exp2 Exp1 - Exp2

map[TK1, TV1]

map[TK2, TV2]

map[lub(TK1,TK2),lub(TK1,TK2)]

Description

The result is the difference of the two map values of Exp1 and Exp2, i.e. a map with all pairs in Exp1 that do have a key that does not occur in Exp2.

Examples
rascal>("apple": 1, "pear": 2) - ("banana": 3, "apple": 4);
map[str, int]: ("pear":2)
Map Equal
Synopsis

Equality operator on maps.

Syntax

Exp1 == Exp2

Types
Exp1 Exp2 Exp1 == Exp2

map[TK1,TV2]

map[TK2, TV2]

bool

Description

Yields true if both arguments contain the same key/value pairs, and false otherwise.

Examples
rascal>("apple": 1, "pear": 2) == ("pear": 2, "apple": 1);
bool: true
rascal>("apple": 1, "pear": 2) == ("apple": 1, "banana": 3)
bool: false
Map Intersection
Synopsis

Intersection of two maps.

Syntax

Exp1 & Exp2

Types
Exp1 Exp2 Exp1 & Exp2

map[T11, T12]

set[T2]

set[lub(T1,T2)]

Description

Returns the intersection of the two map values of Exp1 and Exp2, i.e., a map that contains the key/value pairs that occur in both maps.

Examples
rascal>("apple": 1, "pear": 2) & ("banana": 3, "apple": 1);
map[str, int]: ("apple":1)
rascal>("apple": 1, "pear": 2) & ("banana": 3, "apple": 4);
map[str, int]: ()
Map NotEqual
Synopsis

Not equal operator on map values.

Syntax

Exp1 != Exp2

Types
Exp1 Exp2 Exp1 != Exp2

map[TK1,TV2]

map[TK2, TV2]

bool

Description

Yields true if both arguments contain different key/value pairs, and false otherwise.

Examples
rascal>("apple": 1, "pear": 2) != ("apple": 1, "banana": 3);
bool: true
rascal>("apple": 1, "pear": 2) != ("pear": 2, "apple": 1);
bool: false
Map StrictSubMap
Synopsis

Strict submap operator on map values.

Syntax

Exp1 < Exp2

Types
Exp1 Exp2 Exp1 < Exp2

map[TK1,TV2]

map[TK2, TV2]

bool

Description

Yields true if all key/value pairs in the map value of Exp1 occur in the map value Exp2 and the values of Exp1 and EXp2 are not equal, and false otherwise.

Examples
rascal>("apple": 1, "pear": 2) < ("pear": 2, "apple": 1, "banana" : 3);
bool: true
rascal>("apple": 1, "pear": 2) < ("apple": 1, "banana" : 3);
bool: false
Map StrictSuperMap
Synopsis

Strict supermap operator on map values.

Syntax

Exp1 > Exp2

Types
Exp1 Exp2 Exp1 > Exp2

map[TK1,TV2]

map[TK2, TV2]

bool

Description

Yields true if all key/value pairs in the map value of Exp2 occur in the map value Exp1 and the values of Exp1 and EXp2 are not equal, and false otherwise.

Examples
rascal>("pear": 2, "apple": 1, "banana" : 3) > ("apple": 1, "pear": 2);
bool: true
rascal>("apple": 1, "banana" : 3) > ("apple": 1, "pear": 2);
bool: false
Map SubMap
Synopsis

Submap operator on map values.

Syntax

Exp1Exp2

Types
Exp1 Exp2 Exp1Exp2

map[TK1,TV2]

map[TK2, TV2]

bool

Description

Yields true if all key/value pairs in the map value of Exp1 occur in the map value Exp2 or the values of Exp1 and Exp2 are equal, and false otherwise.

Examples
rascal>("apple": 1, "pear": 2) <= ("pear": 2, "apple": 1);
bool: true
rascal>("apple": 1, "pear": 2) <= ("pear": 2, "apple": 1, "banana" : 3);
bool: true
rascal>("apple": 1, "pear": 2) <= ("apple": 1, "banana" : 3);
bool: false
Map Subscription
Synopsis

Retrieve a value by its key in map.

Syntax

Exp1 [ Exp2 ]

Types
Exp1 Exp2 Exp1 [ Exp2 ]

map[T1, T2]

T1

T2

Description

Map subscription uses the value of Exp2 as key in the map value of Exp1 and returns the associated value. If this key does not occur in the map, the exception NoSuchKey is thrown.

Examples

Introduce a map, assign it to colors, and retrieve the element with index "trumps":

rascal>colors = ("hearts":"red", "clover":"black",
>>>>>>>          "trumps":"black", "clubs":"red");
map[str, str]: ("hearts":"red","trumps":"black","clover":"black","clubs":"red")
rascal>colors["trumps"];
str: "black"

Explore some erroneous subscription expressions:

rascal>colors[0];
|prompt:///|(7,1,<1,7>,<1,8>): Expected str, but got int
Advice: |http://tutor.rascal-mpl.org/Errors/Static/UnexpectedType/UnexpectedType.html|
 
ok
rascal>colors["square"];
|prompt:///|(7,8,<1,7>,<1,15>): NoSuchKey("square")
	at $shell$(|prompt:///|(0,17,<1,0>,<1,17>))
 
ok
Map SuperMap
Synopsis

Supermap operator on map values.

Syntax

Exp1 >= Exp2

Types
Exp1 Exp2 Exp1 >= Exp2

map[TK1,TV2]

map[TK2, TV2]

bool

Description

Yields true if all key/value pairs in the map value of Exp2 occur in the map value Exp1 or the values of Exp1 and Exp2 are equal, and false otherwise.

Examples
rascal>("pear": 2, "apple": 1) >= ("apple": 1, "pear": 2);
bool: true
rascal>("pear": 2, "apple": 1, "banana" : 3) >= ("apple": 1, "pear": 2);
bool: true
rascal>("apple": 1, "banana" : 3) >= ("apple": 1, "pear": 2);
bool: false
Map Union
Synopsis

Union of two maps.

Syntax

Exp1 + Exp2

Types
Exp1 Exp2 Exp1 + Exp2

map[TK1, TV1]

map[TK2, TV2]

map[lub(TK1,TK2),lub(TK1,TK2) ]

Description

The result is the union of the two map values of Exp1 and Exp2. If they have a pair with the same key in common, that key will be associated in the union with the value associated with that key in Exp2.

Examples
rascal>("apple": 1, "pear": 2) + ("banana": 3, "kiwi": 4);
map[str, int]: ("banana":3,"pear":2,"kiwi":4,"apple":1)
rascal>("apple": 1, "pear": 2) + ("banana": 3, "apple": 4);
map[str, int]: ("banana":3,"pear":2,"apple":4)
Benefits

Map union is very suited for representing environment composition in interpreters.

Map in
Synopsis

Membership test on the keys of a map.

Syntax

Exp1 in Exp2

Types
Exp1 Exp2 Exp1 in Exp2

T1 <: TK

map[TK, TV]

bool

Description

Yields true if the value of Exp1 occurs as key in the map value of Exp2 and false otherwise. The type of Exp1 should be compatible with the key type TK of Exp2.

Examples
rascal>"pear" in ("apple": 1, "pear": 2);
bool: true
rascal>"pineapple" in ("apple": 1, "pear": 2);
bool: false
Map notin
Synopsis

Negated membership test on the keys of a map.

Syntax

Exp1 notin Exp2

Types
Exp1 Exp2 Exp1 notin Exp2

T1 <: TK

map[TK, TV]

bool

Description

Yields true if the value of Exp1 does not occur as key in the map value of Exp2 and false otherwise. The type of Exp1 should be compatible with the key type TK of Exp2.

Examples
rascal>"pineapple" notin ("apple": 1, "pear": 2);
bool: true
rascal>"pear" notin ("apple": 1, "pear": 2);
bool: false

3.1.9. Node

Synopsis

Node values.

Syntax

Exp0 ( Exp1, Exp2, …​, FieldName1 = Expr~1, FieldName2 = _Expr~2, …​ )

Types
Exp0 Exp1 Exp2 …​ Exp0 ( Exp1, Exp2, …​ )

str

value

value

…​

node

Description

Values of type node represent untyped trees and are constructed as follows:

  • the string value of Exp0 is the node name;

  • zero or more expressions of type value are the node's children.

  • optionally, unordered named fields can be added as well.

The following are provided for nodes:

Examples

A node with name "my_node" and three arguments:

rascal>"my_node"(1, true, "abc");
node: "my_node"(1,true,"abc")

A nested node structure:

rascal>"my_node1"(1, "my_node2"(3.5, ["a", "b", "c"]), true);
node: "my_node1"(
  1,
  "my_node2"(
    3.5,
    ["a","b","c"]),
  true)

A node with named fields:

rascal>"my_node2"(1,2,size=2,age=24);
node: "my_node2"(1,2,
  size=2,
  age=24)
Benefits
  • nodes are untyped and can be used to quickly import untyped data into Rascal

  • pattern matching on nodes is quite expressive

Pitfalls
  • the lack of types at run-time makes pattern matching on node possibly inaccurate (you might match more than you think)

Node Equal
Synopsis

Equal operator on node values.

Syntax

Exp1 == Exp2

Types
Exp1 Exp2 Exp1 == Exp2

node

node

bool

Description

Yields true if the node names of the values of Exp1 and Exp2 are equal and the children of each node are pairwise equal, otherwise false.

Examples
rascal>"f"(1, "abc", true) == "f"(1, "abc", true);
bool: true
rascal>"f"(1, "abc", true) == "f"(1, "def", true);
bool: false
Node GreaterThan
Synopsis

Greater than operator on node values.

Syntax

Exp1 > Exp2

Types
Exp1 Exp2 Exp1 > Exp2

node

node

bool

Description

Comparison on nodes is defined by a lexicographic ordering. Node N = F(N1, …​, Nn) is greater than node N = G(M1, …​, Mm) when: * N is not equal to M, and * F is lexicographically greater than G, or F is equal to G and n > m.

Examples
rascal>"g"(3) > "f"(10, "abc");
bool: true
rascal>"f"(10, "abc") > "f"(10);
bool: true
Node GreaterThanOrEqual
Synopsis

Greater than or equal operator on node values.

Syntax

Exp1 >= Exp2

Types
Exp1 Exp2 Exp1 >= Exp2

node

node

bool

Description

Comparison on nodes is defined by a lexicographic ordering. Node N = F(N1, …​, Nn) is greater than or equal node N = G(M1, …​, Mm) when: * N is equal to M, or * F is lexicographically greater than G, or F is equal to G and n > m.

Examples
rascal>"g"(3) >= "f"(10, "abc");
bool: true
rascal>"f"(10, "abc") >= "f"(10);
bool: true
rascal>"f"(10, "abc") >= "f"(10, "abc");
bool: true
Node LessThan
Synopsis

Less than operator on node values.

Syntax

Exp1 < Exp2

Types
Exp1 Exp2 Exp1 < Exp2

node

node

bool

Description

Comparison on nodes is defined by a lexicographic ordering. Node N = F(N1, …​, Nn) is less than node N = G(M1, …​, Mm) when: * N is not equal to M, and * F is lexicographically less than G, or F is equal to G and n < m.

Examples
rascal>"f"(10, "abc") < "g"(3);
bool: true
rascal>"f"(10) < "f"(10, "abc");
bool: true
Node LessThanOrEqual
Synopsis

Less than or equal operator on node values.

Syntax

Exp1Exp2

Types
Exp1 Exp2 Exp1Exp2

node

node

bool

Description

Comparison on nodes is defined by a lexicographic ordering. Node N = F(N1, …​, Nn) is less than or equal node N = G(M1, …​, Mm) when: * N is equal to M, or * F is lexicographically less than G, or F is equal to G and n < m.

Examples
rascal>"f"(10, "abc") <= "f"(10, "abc");
bool: true
rascal>"f"(10) <= "f"(10, "abc");
bool: true
Node NotEqual
Synopsis

Not equal operator on node values.

Syntax

Exp1 != Exp2

Types
Exp1 Exp2 Exp1 != Exp2

node

node

bool

Description

Yields true if the node names of the values of Exp1 and Exp2 are unequal or any of the children of each node is pairwise unequal, otherwise true.

Examples
rascal>"f"(1, "abc", true) != "g"(1, "abc", true);
bool: true
rascal>"f"(1, "abc", true) != "f"(1, "abc", true);
bool: false
Node Slice
Synopsis

Retrieve a slice of a node’s argument list.

Syntax
  • Exp1 [ Exp2 .. Exp4]

  • Exp1 [ Exp2 , Exp3 .. Exp4]

where Exp2 and Exp4 are optional.

Types
Exp1 Exp2 Exp3 Exp4 Exp1 [ Exp2 .. Exp4 ] or Exp1 [ Exp2 , Exp3 .. Exp4]

node

int

int

int

list[value]

Description

A Node slice is similar to a list List Slice and uses the integer values of Exp2 and Exp4 to determine the begin (inclusive) and end (exclusive) of a slice from the children of the node value ND of Exp1. Negative indices count from the end of the list of children backwards. Using the second form, an extra index Exp3 is given that determines the index of the second element in the slice and establishes the step between successive elements in the slice. The default step is 1. If end is smaller than begin, the slice is constructed backwards.

Let Len be the number of children of ND and let N2, N3 and N4 be the respective values of the expressions Exp2, Exp2 and Exp2 when they are present.

The slice parameters begin, end, and step are determined as follows:

  • Exp2:

    • If Exp2 is absent, then begin = 0.

    • Otherwise, if N2 >= 0 then begin = N2 else begin = N2 + Len.

  • Exp4:

    • If Exp4 is absent, then end = Len.

    • Otherwise, if N4 >= 0, then end = N4 else end = N4 + Len.

  • Exp3:

    • If Exp3 is absent, then if begin < end then step = 1 else step = -1.

    • Otherwise, if begin < end, then step = N3 - begin else step = begin - N3.

Now, the constraints 0 ⇐ begin < Len and 0 < end < Len should hold, otherwise the exception IndexOutOfBounds is thrown.

The slice consists of the children ND[begin], ND[begin+step], ND[end - step]. When begin >= end, the elements are listed in reverse order.

Examples

Consider the list ND = "f"(0, "abc", 20, false, 40, [3,4,5], 60, {"a", "b"}, 80); as running example.

Here is a view on the children of ND that will help to correlate positive and negative indices:

i 0 1 2 3 4 5 6 7 8

ND[i]

0

"abc"

20

false

40

[3,4,5]

60

{"a", "b"}

80

-i

-9

-8

-7

-6

-5

-4

-3

-2

-1

Some common use cases (with beginend):

Slice Means:

ND[begin..end]

children with indices begin through end-1

ND[begin..]

children with indices begin through the rest of the list of children

ND[..end]

children with indices from the beginning through end-1

ND[..]

the whole list of children

ND[-1]

last child of the list of children

ND[-2..]

the last two children of the list of children

ND[..-2]

all children except the last two.

Let’s put this into practice now.

rascal>ND = "f"(0, "abc", 20, false, 40, [3,4,5], 60, {"a", "b"}, 80);
node: "f"(
  0,
  "abc",
  20,
  false,
  40,
  [3,4,5],
  60,
  {"a","b"},
  80)

Slices with begin < end

rascal>ND[1..3];
list[value]: ["abc",20]
rascal>ND[1..];       // empty end => end of list of children
list[value]: [
  "abc",
  20,
  false,
  40,
  [3,4,5],
  60,
  {"a","b"},
  80
]
rascal>ND[..3];       // empty begin => first child of list
list[value]: [0,"abc",20]
rascal>ND[..];        // both empty => whole list of children
list[value]: [
  0,
  "abc",
  20,
  false,
  40,
  [3,4,5],
  60,
  {"a","b"},
  80
]

Slices with begin >= end

rascal>ND[3..1];      // slice contains children with indices 3 and 2 (in that order)
list[value]: [false,20]
rascal>ND[3..3];      // empty slice when begin == end
list[value]: []

Slices with negative begin or end:

rascal>ND[2..-2];     // equivalent to ND[2..7]
list[value]: [
  20,
  false,
  40,
  [3,4,5],
  60
]
rascal>ND[2..7];
list[value]: [
  20,
  false,
  40,
  [3,4,5],
  60
]
rascal>ND[-4..-2];    // equivalent to ND[5..7]
list[value]: [
  [3,4,5],
  60
]
rascal>ND[5..7];
list[value]: [
  [3,4,5],
  60
]

Slices with an explicit second index:

rascal>ND[1,3..6];
list[value]: [
  "abc",
  false,
  [3,4,5]
]
rascal>ND[5,3..];
list[value]: [
  [3,4,5],
  false,
  "abc"
]

Explore error cases:

rascal>ND[..10];
list[value]: [
  0,
  "abc",
  20,
  false,
  40,
  [3,4,5],
  60,
  {"a","b"},
  80
]
rascal>ND[1..20];
list[value]: [
  "abc",
  20,
  false,
  40,
  [3,4,5],
  60,
  {"a","b"},
  80
]
Node Subscription
Synopsis

Retrieve an argument of a node via its index.

Syntax

Exp1 [ Exp2 ]

Types
Exp1 Exp2 Exp1 [ Exp2 ]

node

int

value

Description

Node subscription uses the integer value of Exp2 as index in the argument list of the node value of Exp1. The value of Exp2 should be greater or equal 0 and less than the number of arguments of the node. If this is not the case, the exception IndexOutOfBounds is thrown.

Examples

Introduce a node, assign it to F and retrieve the various arguments:

rascal>F = "f"(1, "abc", false);
node: "f"(1,"abc",false)
rascal>F[0]
value: 1
rascal>F[1]
value: "abc"
rascal>F[2]
value: false

Explore an error case:

rascal>F[3];
|prompt:///|(2,1,<1,2>,<1,3>): IndexOutOfBounds(3)
	at $shell$(|prompt:///|(0,5,<1,0>,<1,5>))
 
ok

3.1.10. Number

Synopsis

Numeric values.

Types

int, real, num

Description

Numbers include integers (values of type int) and reals (values of type real). If both operands have the same type (int or real) then the operator is the corresponding operator on integers or reals. Otherwise, integer arguments are first converted to real and the real operator is applied.

The following operations are provided on numbers:

Number Addition
Synopsis

Addition on numeric values.

Syntax

Exp1 + Exp2

Types
Exp1 Exp2 Exp1 + Exp2

int

int

int

int

real

real

real

real

real

Description

Yields the numerical sum of the values of Exp1 and Exp2.

Examples
rascal>12 + 13
int: 25
rascal>12 + 13.5
real: 25.5
Number Conditional
Synopsis

Conditional expression for numeric values.

Syntax

Exp1 ? Exp2 : Exp3

Types
Exp1 Exp2 Exp3 Exp1 ? Exp2 : Exp3

bool

int

int

int

bool

int

real

real

bool

real

real

real

Description

If the value of Exp is true then the value of Exp1 else the value of Exp2.

Examples
rascal>(3 > 2) ? 10 : 20
int: 10
rascal>(3 > 20) ? 10 : 20
int: 20
Number Division
Synopsis

Division on numeric values.

Syntax

Exp1 / Exp2

Types
Exp1 Exp2 Exp1 / Exp2

int

int

int

int

real

real

real

real

real

Description

Yields the result of dividing the value of Exp1 by the value of Exp2.

Examples
rascal>12 / 3
int: 4
rascal>10 / 3
int: 3
rascal>12 / 3.0
real: 4.
rascal>10 / 3.0
real: 3.333333333
rascal>12 / 0
|prompt:///|(5,1,<1,5>,<1,6>): ArithmeticException("/ by zero")
 
ok
Number Equal
Synopsis

Equality operator on numeric values.

Syntax

Exp1 == Exp2

Types
Exp1 Exp2 Exp1 == Exp2

int

int

bool

int

real

bool

real

real

bool

Description

Yields true if the value of both arguments is numerically equal, and false otherwise.

Examples
rascal>12 == 12
bool: true
rascal>12 == 12.0
bool: true
rascal>12 == 13
bool: false
rascal>12 == 13.0
bool: false
rascal>3.14 == 3.14
bool: true
rascal>3.14 == 3
bool: false
Number GreaterThan
Synopsis

Greater than operator on numeric values.

Syntax

Exp1 > Exp2

Types
Exp1 Exp2 Exp1 > Exp2

int

int

bool

int

real

bool

real

real

bool

Description

Yields true if the value of Exp1 is numerically greater than the value of Exp2, and false otherwise.

Examples
rascal>13 > 12
bool: true
rascal>12 > 13
bool: false
rascal>13.5 > 12
bool: true
rascal>12.5 > 13
bool: false
Number GreaterThanOrEqual
Synopsis

Greater than or equal operator on numeric values.

Syntax

Exp1 >= Exp2

Types
Exp1 Exp2 Exp1 >= Exp2

int

int

bool

int

real

bool

real

real

bool

Description

Yields true if the value of Exp1 is numerically greater than or equal to the value of Exp2, and false otherwise.

Examples
rascal>13 >= 12
bool: true
rascal>12 >= 13
bool: false
rascal>13.5 >= 12
bool: true
rascal>12.5 >= 13
bool: false
Number LessThan
Synopsis

Less than operator on numeric values.

Syntax

Exp1 < Exp2

Types
Exp1 Exp2 Exp1 < Exp2

int

int

bool

int

real

bool

real

real

bool

Description

Yields true if the value of Exp1 is numerically less than the value of Exp2, and false otherwise.

Examples
rascal>13 < 12
bool: false
rascal>12 < 13
bool: true
rascal>13.5 < 12
bool: false
rascal>12.5 < 13
bool: true
Number LessThanOrEqual
Synopsis

Less than or equal operator on numeric values.

Syntax

Exp1Exp2

Types
Exp1 Exp2 Exp1Exp2

int

int

bool

int

real

bool

real

real

bool

Description

Yields true if the value of Exp1 is numerically less than or equal to the value of Exp2, and false otherwise.

Examples
rascal>13 <= 12
bool: false
rascal>12 <= 13
bool: true
rascal>13.5 <= 12
bool: false
rascal>12.5 <= 13
bool: true
Number Multiplication
Synopsis

Multiply two numeric values.

Syntax

Exp1 * Exp2

Types
Exp1 Exp2 Exp1 * Exp2

int

int

int

int

real

real

real

real

real

Description

Yields the result of multiplying the values of Exp1 and Exp2.

Examples
rascal>12 * 13
int: 156
rascal>12 * 13.5
real: 162.0
rascal>-12*13
int: -156
Number Negation
Synopsis

Negate a numeric value.

Syntax

- Exp

Types
Exp - Exp

int

int

real

real

Description

Yields the negated values of Exp.

Examples
rascal>-12
int: -12
rascal>-13.5
real: -13.5
rascal>- -12
int: 12
Number NotEqual
Synopsis

Not equal operator on numeric values.

Syntax

Exp1 != Exp2

Types
Exp1 Exp2 Exp1 != Exp2

int

int

bool

int

real

bool

real

real

bool

Description

Yields true if the value of both arguments is numerically unequal, and false otherwise.

Examples
rascal>12 != 13
bool: true
rascal>12 != 12
bool: false
rascal>12 != 13.0
bool: true
rascal>12.0 != 13
bool: true
rascal>3.14 != 3
bool: true
rascal>3.14 != 3.14
bool: false
Number Remainder
Synopsis

Remainder of two integer values.

Syntax

Exp1 % Exp2

Types
Exp1 Exp2 Exp1 % Exp2

int

int

int

Description

Yields the remainder when dividing the of Exp1 by the value of Exp2.

Examples
rascal>12 % 5
int: 2
rascal>12 % 6
int: 0
Pitfalls

Remainder is only defined on integers:

rascal>13.5 % 6
|prompt:///|(7,1,<1,7>,<1,8>): remainder not supported on real and int
Advice: |http://tutor.rascal-mpl.org/Errors/Static/UnsupportedOperation/UnsupportedOperation.html|
 
ok
Number Subtraction
Synopsis

Subtract two numeric values.

Syntax

Exp1 - Exp2

Types
Exp1 Exp2 Exp1 - Exp2

int

int

int

int

real

real

real

real

real

Description

Yields the numerical result of subtracting the value of Exp2 from the value of Exp1.

Examples
rascal>13 - 12
int: 1
rascal>13.5 - 12
real: 1.5
rascal>12 - 13
int: -1
rascal>12 - 13.5
real: -1.5

3.1.11. Range

Synopsis

Numeric range of values.

Syntax
  • [ Exp1 .. Exp3 ]

  • [ Exp1, Exp2 .. Exp3 ]

Description

Ranges are a shorthand for describing lists of integers from Exp1 up to (exclusive) Exp3 with increments of 1. When Exp2 is present it is taken as the second element of the list and Exp2 - Exp1 is used as increment for the subsequent list elements.

A range with integer expressions is identical to a list List Slice. However, a range may also contain numeric expressions that are not integers.

Examples
rascal>[1 .. 10];
list[int]: [1,2,3,4,5,6,7,8,9]
rascal>[1, 3 .. 10];
list[int]: [1,3,5,7,9]
rascal>[0.5, 3.2 .. 10];
list[real]: [0.5,3.2,5.9,8.6]
rascal>[1, -2 .. -10];
list[int]: [1,-2,-5,-8]
Benefits

Ranges are mostly used to loop over ranges of integers.

Pitfalls

In some cases ranges are empty where one could have expected at least one element:

rascal>[1, 3 .. -10];
list[int]: []

3.1.12. Real

Synopsis

Real values.

Types

real

Description

The real values are represented by the type real and are written as usual in most programming languages. They can have arbitrary size and precision.

See Number for all operations on integers, reals and numbers.

Examples
  • 1.5

  • 3.14e-123

3.1.13. ReifiedTypes

Synopsis

Types can be represented by values

Description

The type reify expression operator has two functions in one go:

  • it transforms type literals into values that represent them (an isomorphic relation)

  • it reifies the declarations necessary to build values of the types as well

As a result a reified type can be used to reconstruct a type and the abstract (Algebraic Data Type) or concrete (Syntax Definition) grammar that produced it.

Type literals have a nice interaction with Type Parameters, since they can be used to bind a type parameter without having to provide a value of the type. An example is the [parse] function in [ParseTree] (see below for an example usage).

The values that are used to represent types are declared in the [Libraries-Prelude-Type] module and [Libraries-Prelude-ParseTree] modules, namely Symbol is the data-type to represent types symbolically and Production is the data-type for representing grammatical constructs.

A type literal wraps a Symbol and a map of `Production`s.

Examples

First import the module Type:

rascal>import Type;
ok

Builtin types can be constructed without definitions, so the reified type representation is simple:

rascal>#int
type[int]: type(
  int(),
  ())
rascal>#list[int]
type[list[int]]: type(
  list(int()),
  ())
rascal>#rel[int from, int to]
type[rel[int from,int to]]: type(
  set(tuple([
        label(
          "from",
          int()),
        label(
          "to",
          int())
      ])),
  ())

to get the symbol from the reified type:

rascal>#int.symbol
Symbol: int()

or we can use some definitions and reify the defined type to see a different behavior:

rascal>data Nat = zero() | succ(Nat prev) | add(Nat l, Nat r) | mul(Nat l, Nat r);
ok
rascal>#Nat
type[Nat]: type(
  adt(
    "Nat",
    []),
  (adt(
      "Nat",
      []):choice(
      adt(
        "Nat",
        []),
      {
        cons(
          label(
            "add",
            adt(
              "Nat",
              [])),
          [
            label(
              "l",
              adt(
                "Nat",
                [])),
            label(
              "r",
              adt(
                "Nat",
                []))
          ],
          [],
          {}),
        cons(
          label(
            "mul",
            adt(
              "Nat",
              [])),
          [
            label(
              "l",
              adt(
                "Nat",
                [])),
            label(
              "r",
              adt(
                "Nat",
                []))
          ],
          [],
          {}),
        cons(
          label(
            "zero",
            adt(
              "Nat",
              [])),
          [],
          [],
          {}),
        cons(
          label(
            "succ",
            adt(
              "Nat",
              [])),
          [label(
              "prev",
              adt(
                "Nat",
                []))],
          [],
          {})
      })))

and we can get an abstract definition of the constructors of the [AlgebraicDataType]:

rascal>import Type;
ok
rascal>#Nat.definitions[adt("Nat",[])]
Production: choice(
  adt(
    "Nat",
    []),
  {
    cons(
      label(
        "add",
        adt(
          "Nat",
          [])),
      [
        label(
          "l",
          adt(
            "Nat",
            [])),
        label(
          "r",
          adt(
            "Nat",
            []))
      ],
      [],
      {}),
    cons(
      label(
        "mul",
        adt(
          "Nat",
          [])),
      [
        label(
          "l",
          adt(
            "Nat",
            [])),
        label(
          "r",
          adt(
            "Nat",
            []))
      ],
      [],
      {}),
    cons(
      label(
        "zero",
        adt(
          "Nat",
          [])),
      [],
      [],
      {}),
    cons(
      label(
        "succ",
        adt(
          "Nat",
          [])),
      [label(
          "prev",
          adt(
            "Nat",
            []))],
      [],
      {})
  })

we could go the other way around and construct a type literal dynamically:

rascal>type(\int(),())
type[value]: type(
  int(),
  ())
rascal>type(\int(),()) == #int
bool: true

we use type literals often in IO to express an expected type:

rascal>import ValueIO;
ok
rascal>int testInt = readTextValueString(#int, "1");
int: 1
rascal>tuple[int,int] testTuple = readTextValueString(#tuple[int,int], "\<1,2\>");
tuple[int,int]: <1,2>
Pitfalls
  • Note that the type reify operator always produces constant values, because type literals are always constants.

3.1.14. Relation

Synopsis

Relation values.

Syntax

{ < Exp11, Exp12, …​ > , < Exp21, Exp22, …​ > , …​ }

Types
Exp11 Exp12 …​ { < Exp11, Exp12, …​ > , …​ }

T1

T2

…​

rel[T1, T2, …​ ]

Description

A relation is a set of elements with the following property:

  • All elements have the same static tuple type.

Relations are thus nothing more than sets of tuples, but since they are used so often we provide a shorthand notation for them. Relations are represented by the type rel[T1 L1, T2 L2, …​ ], where T1, T2, …​ are arbitrary types and L1, L2, …​ are optional labels. It is a shorthand for set[tuple[T1 L1, T2 L2, …​ ]].

An n-ary relations with m tuples is denoted by {< E11, E12, …​, E1n >,< E21, E22, …​, E2n >, …​, < Em1, Em2, …​, Emn >}, where the Eij are expressions that yield the desired element type Ti.

Since relations are a form of set all operations (see Set) and functions (see [Prelude-Set]) are also applicable to relations.

The following additional operators are provided for relations:

There are also library functions available for Relations.

Examples
rascal>{<1,10>, <2,20>, <3,30>}
rel[int,int]: {
  <1,10>,
  <3,30>,
  <2,20>
}

instead of rel[int,int] we can also give set[tuple[int,int]] as type of the above expression remember that these types are interchangeable.

rascal>{<"a",10>, <"b",20>, <"c",30>}
rel[str,int]: {
  <"a",10>,
  <"b",20>,
  <"c",30>
}
rascal>{<"a", 1, "b">, <"c", 2, "d">}
rel[str,int,str]: {
  <"c",2,"d">,
  <"a",1,"b">
}
Relation CartesianProduct
Synopsis

Cartesian product of two relation values.

Syntax

Exp1 * Exp2

Types
Exp1 Exp2 Exp1 * Exp2

set[T1]

set[T2]

rel[T1, T2]

Description

Returns a binary relation that is the Cartesian product of two sets.

Examples
rascal>{1, 2, 3} * {9};
rel[int,int]: {
  <1,9>,
  <3,9>,
  <2,9>
}
rascal>{1, 2, 3} * {10, 11};
rel[int,int]: {
  <1,10>,
  <1,11>,
  <3,10>,
  <3,11>,
  <2,10>,
  <2,11>
}
Relation Composition
Synopsis

Composition of two relation values.

Syntax

Exp1 o Exp2

Types
Exp1 Exp2 Exp1 o Exp2

rel[T1, T2]

rel[T2, T3]

rel[T1, T3]

Description

Returns the composition of two binary relations.

Examples
rascal>import Relation;
ok
rascal>{<1,10>, <2,20>, <3,15>} o {<10,100>, <20,200>};
rel[int,int]: {
  <1,100>,
  <2,200>
}
Pitfalls

We use the letter o as operator and this may conflict other defined names.

Relation FieldSelection
Synopsis

Select a field (column) from a relation value.

Syntax

Exp . Name

Types
Exp Exp . Name

rel[T1 L1, T2 L2, …​ ]

set[Ti]

Description

Exp should evaluate to a relation that has an i-th field label Li that is identical to Name. Return a set with all values of that field. Name stands for itself and is not evaluated.

Examples
rascal>rel[str street, int nm] R = {<"abc", 1>, <"abc", 2>, <"def", 4>, <"def", 5>};
rel[str street,int nm]: {
  <"abc",1>,
  <"abc",2>,
  <"def",5>,
  <"def",4>
}
rascal>R.street;
set[str]: {"abc","def"}
Relation Join
Synopsis

Join two relation values.

Syntax

Exp1 join Exp2

Types
Exp1 Exp2 Exp1 join Exp2

rel[ T11, T12, T13, …​ ]

rel[ T21, T22, T23, …​ ]

rel[ T11, T12, T13, …​, T21, T22, T23, …​ ]

Description

Relation resulting from the natural join of the relation values of the two arguments. This relation contains tuples that are the result from concatenating the elements from both arguments.

Examples
rascal>{<1,2>, <10,20>} join {<2,3>};
rel[int,int,int,int]: {
  <1,2,2,3>,
  <10,20,2,3>
}
rascal>{<1,2>} join {3, 4};
rel[int,int,int]: {
  <1,2,3>,
  <1,2,4>
}
rascal>{<1,2>, <10,20>} join {<2,3>, <20,30>};
rel[int,int,int,int]: {
  <1,2,2,3>,
  <10,20,2,3>,
  <10,20,20,30>,
  <1,2,20,30>
}
Relation ReflexiveTransitiveClosure
Synopsis

The reflexive transitive closure of a binary relation.

Syntax

Exp *

Types
Exp Exp *

rel[T1, T2]

rel[T1, T2]

Description

Reflexive transitive closure is defined by repeated composition of a relation. If we define for a given relation R:

  • R0 = identity relation = {<a, a>, <b, b> | <a, b> ← R}

  • R1 = R

  • R2 = R o R

  • R3 = R o R2

  • …​

then the reflexive transitive closure R* can be defined in two ways: (also see Relation TransitiveClosure): * R* = R0 + R1 + R2 + R3 + …​ * R* = R0 + R+

Examples
rascal>{<1,2>, <2,3>, <3,4>}*;
rel[int,int]: {
  <4,4>,
  <1,1>,
  <1,3>,
  <1,2>,
  <1,4>,
  <3,3>,
  <3,4>,
  <2,3>,
  <2,2>,
  <2,4>
}
Relation Subscription
Synopsis

Indexing of a relation via tuple values.

Exp_0 [ Exp1, Exp2, …​ Expn]
Exp_0 [ Exp1]
Variant 1
Types
Exp_0 Exp1 Exp2 …​ Exp_0 [ Exp1, Exp2, …​ ]

rel[T1, T2, …​ Tm]

int

int

…​

rel[Tn, Tn+1, …​ Tm]

Variant 2
Exp0 Exp1 Exp0 [ Exp1 ]

rel[T1, T2, …​ Tm]

set[T1]

rel[T2, T2, …​ Tm]

Description

Relation resulting from subscription of a relation Exp0.

Variant 1

Subscription with the index values of Exp1, Exp2, …​. The result is a relation with all tuples that have these index values as first elements with the index values removed from the tuple. If the resulting tuple has only a single element, a set is returned instead of a relation. A wildcard _ as index value matches all possible values at that index position.

Variant 2

Subscription with a set of the index values of Exp1. The result is a relation with all tuples that have these index values as first element with the index values removed from the tuple.

Examples
rascal>R = {<1,10>, <2,20>, <1,11>, <3,30>, <2,21>};
rel[int,int]: {
  <3,30>,
  <1,10>,
  <1,11>,
  <2,20>,
  <2,21>
}
rascal>R[1];
set[int]: {10,11}
rascal>R[{1}];
set[int]: {10,11}
rascal>R[{1, 2}];
set[int]: {10,20,11,21}
rascal>RR = {<1,10,100>,<1,11,101>,<2,20,200>,<2,22,202>,
>>>>>>>              <3,30,300>};
rel[int,int,int]: {
  <1,10,100>,
  <3,30,300>,
  <1,11,101>,
  <2,20,200>,
  <2,22,202>
}
rascal>RR[1];
rel[int,int]: {
  <10,100>,
  <11,101>
}
rascal>RR[1,_];
set[int]: {100,101}

Introduce a relation with economic data and assign it to GDP:

rascal>rel[str country, int year, int amount] GDP =
>>>>>>>{<"US", 2008, 14264600>, <"EU", 2008, 18394115>,
>>>>>>> <"Japan", 2008, 4923761>, <"US", 2007, 13811200>,
>>>>>>> <"EU", 2007, 13811200>, <"Japan", 2007, 4376705>};
rel[str country,int year,int amount]: {
  <"EU",2007,13811200>,
  <"US",2007,13811200>,
  <"Japan",2007,4376705>,
  <"US",2008,14264600>,
  <"Japan",2008,4923761>,
  <"EU",2008,18394115>
}

and then retrieve the information for the index "Japan":

rascal>GDP["Japan"];
rel[int,int]: {
  <2007,4376705>,
  <2008,4923761>
}

or rather for the indices "Japan" and 2008:

rascal>GDP["Japan", 2008];
set[int]: {4923761}
Relation TransitiveClosure
Synopsis

Transitive closure on binary relation values.

Syntax

Exp +

Types
Exp Exp +

rel[T1, T2]

rel[T1, T2]

Description

Returns the transitive closure of a binary relation. Transitive closure is defined by repeated composition of a relation. If we define for a given relation R:

  • R1 = R

  • R2 = R o R

  • R3 = R o R2

  • …​

then the transitive closure R+ can be defined as

  • R+ = R1 + R2 + R3 + …​

Examples
rascal>{<1,2>, <2,3>, <3,4>}+;
rel[int,int]: {
  <3,4>,
  <1,3>,
  <1,2>,
  <1,4>,
  <2,3>,
  <2,4>
}

We can also simply (but not necessarily efficiently) define transitive closure ourselves:

rascal>rel[int,int] tclosure(rel[int,int] R) {
>>>>>>>   tc = R;
>>>>>>>   while(true){
>>>>>>>      tc1 = tc;
>>>>>>>      tc += tc o R;
>>>>>>>      if(tc1 == tc)
>>>>>>>         return tc;
>>>>>>>   }
>>>>>>>}
rel[int,int] (rel[int,int]): function(|prompt:///|(0,149,<1,0>,<9,1>))

tclosure({<1,2>, <2,3>, <3,4>});

rascal>----
>>>>>>>
cancelled
rascal>.Benefits
         ^ Parse error here
ok
rascal>
cancelled
rascal>.Pitfalls
         ^ Parse error here
ok
rascal>
cancelled

WARNING: unexpected errors in the above SHELL example. Documentation author please fix! :leveloffset: +1

3.2. Set

Synopsis

Set values.

Syntax

{ Exp1, Exp2, …​ }

Types
Exp1 Exp2 …​ { Exp1, Exp2, …​ }

T1

T2

…​

set[ lub(T1, T2, …​ ) ]

Description

A set is an unordered sequence of values and has the following properties:

  • All elements have the same static type.

  • The order of the elements does not matter.

  • A set contains an element only once. In other words, duplicate elements are eliminated and no matter how many times an element is added to a set, it will occur in it only once.

The type of a set has the form set[T], where T is an arbitrary type.

When a value or variable of type set occurs inside a set, that set value is inserted as set element. To achieve splicing of these elements, i.e., the insertion of the elements of the set value rather than the whole set, it has to be prefixed by the splice operator *.

The following operators are provided on sets:

There are also library functions available for Sets. .Examples ## Set types

rascal>{1, 2, 3};
set[int]: {1,3,2}
rascal>{<1,10>, <2,20>, <3,30>};
rel[int,int]: {
  <1,10>,
  <3,30>,
  <2,20>
}
rascal>{1, "b", 3};
set[value]: {"b",1,3}
rascal>{<"a", 10>, <"b", 20>, <"c", 30>}
rel[str,int]: {
  <"a",10>,
  <"b",20>,
  <"c",30>
}
rascal>{{"a", "b"}, {"c", "d", "e"}}
set[set[str]]: {
  {"a","b"},
  {"c","d","e"}
}

Note that

  • {1, 2, 3} and {3, 2, 1} are identical sets (since order is not relevant).

  • {1, 2, 3} and {1, 2, 3, 1} are also identical sets (since duplication is not relevant).

3.2.1. Set splicing

Introduce a set variable S

rascal>S = {1, 2, 3};
set[int]: {1,3,2}

and observe how the value of S is added as single element in another set:

rascal>{10, S, 20};
set[value]: {10,20,{1,3,2}}

or how its elements are added as elements to the other set:

rascal>{10, *S, 20};
set[int]: {10,1,3,20,2}

3.2.2. Set Comprehension

Synopsis

A set comprehension generates a set value.

Syntax

{ Exp1, Exp2, …​ | Gen1, Gen2, …​ }

Types
Exp1 Exp2 …​ { Exp1, Exp2, …​ | Gen1, Gen2, …​ }

T1

T2

…​

set[ lub( T1, T2, …​ ) ]

Description

A set comprehension consists of a number of contributing expressions Exp1, Exp2, …​ and a number of generators Gen1, Gen2, Gen3, …​ that are evaluated as described in Comprehensions.

Examples
rascal>{ N * N | int N <- [0 .. 10]};
set[int]: {16,64,1,9,81,4,0,49,36,25}
rascal>{ N * N | int N <- [0 .. 10], N % 3 == 0};
set[int]: {9,81,0,36}

3.2.3. Set Difference

Synopsis

The difference between two sets.

Syntax

Exp1 - Exp2

Types
Exp1 Exp2 Exp1 - Exp2

set[T1]

set[T2]

set[lub(T1,T2)]

set[T1]

T2

set[lub(T1,T2)]

Description

If both Exp1 and Exp2 have a set as value, the result is the difference of these two set values. If Exp2 does not have a set as value, it is first converted to a set before the difference is computed. The difference is computed by removing all elements of the second set from the first set.

Examples
rascal>{1, 2, 3, 4} - {1, 2, 3};
set[int]: {4}
rascal>{1, 2, 3, 4} - {3};
set[int]: {1,2,4}
rascal>{1, 2, 3, 4} - 3;
set[int]: {1,2,4}
rascal>{1, 2, 3, 4} - {5, 6, 7};
set[int]: {1,3,2,4}

3.2.4. Set Equal

Synopsis

Equal operator on set values.

Syntax

Exp1 == Exp2

Types
Exp1 Exp2 Exp1 == Exp2

set[T1]

set[T2]

bool

Description

Yields true if both arguments are equal sets and false otherwise.

Examples
rascal>{1, 2, 3} == {3, 2, 1};
bool: true
rascal>{1, 2, 3} == {1, 2};
bool: false

3.2.5. Set Insert

Synopsis

Add an element to a set.

Syntax

Exp1 + Exp2

Types
Exp1 Exp2 Exp1 + Exp2

set[T1]

T2

set[lub(T1,T2)]

T1

set[T2]

set[lub(T1,T2)]

Description

The + operator will add elements to sets.

Examples
rascal>{1, 2, 3} + 4;
set[int]: {1,3,2,4}
rascal>1 + { 2, 3, 4};
set[int]: {1,3,2,4}
rascal>{1} + 1;
set[int]: {1}
rascal>1 + {1};
set[int]: {1}
Pitfalls
  • if both operands of + are a set then it acts as Set Union.

3.2.6. Set Intersection

Synopsis

Intersection of two sets.

Syntax

Exp1 & Exp2

Types
Exp1 Exp2 Exp1 & Exp2

set[T1]

set[T2]

set[lub(T1,T2)]

Description

Returns the intersection of the two set values of Exp1 and Exp2. The intersection consists of the common elements of both sets.

Examples
rascal>{1, 2, 3, 4, 5} & {4, 5, 6};
set[int]: {5,4}

3.2.7. Set NotEqual

Synopsis

Not equal operator on set values.

Syntax

Exp1 != Exp2

Types
Exp1 Exp2 Exp1 != Exp2

set[T1]

set[T2]

bool

Description

Yields true if both arguments are unequal sets and false otherwise.

Examples
rascal>{1, 2, 3} != {3, 2, 1};
bool: false
rascal>{1, 2, 3} != {1, 2};
bool: true

3.2.8. Set Product

Synopsis

The product of two set values.

Syntax

Exp1 * Exp2

Types
Exp1 Exp2 Exp1 * Exp2

set[T1]

set[T2]

rel[T1,T2]

Description

Yields a relation resulting from the product of the values of Exp1 and Exp2. It contains a tuple for each combination of values from both arguments.

Examples
rascal>{1, 2, 3} * {4, 5, 6};
rel[int,int]: {
  <1,5>,
  <1,4>,
  <1,6>,
  <3,5>,
  <3,4>,
  <3,6>,
  <2,5>,
  <2,4>,
  <2,6>
}

A card deck can be created as follows:

rascal>{"clubs", "hearts", "diamonds", "spades"} * {1,2,3,4,5,6,7,8,9,10,11,12,13};
rel[str,int]: {
  <"hearts",10>,
  <"hearts",7>,
  <"hearts",1>,
  <"hearts",3>,
  <"hearts",13>,
  <"hearts",9>,
  <"hearts",2>,
  <"hearts",4>,
  <"hearts",11>,
  <"hearts",6>,
  <"hearts",12>,
  <"hearts",5>,
  <"hearts",8>,
  <"spades",10>,
  <"spades",7>,
  <"spades",1>,
  <"spades",3>,
  <"spades",13>,
  <"spades",9>,
  <"spades",2>,
  <"spades",4>,
  <"spades",11>,
  <"spades",6>,
  <"spades",12>,
  <"spades",5>,
  <"spades",8>,
  <"clubs",10>,
  <"clubs",7>,
  <"clubs",1>,
  <"clubs",3>,
  <"clubs",13>,
  <"clubs",9>,
  <"clubs",2>,
  <"clubs",4>,
  <"clubs",11>,
  <"clubs",6>,
  <"clubs",12>,
  <"clubs",5>,
  <"clubs",8>,
  <"diamonds",10>,
  <"diamonds",7>,
  <"diamonds",1>,
  <"diamonds",3>,
  <"diamonds",13>,
  <"diamonds",9>,
  <"diamonds",2>,
  <"diamonds",4>,
  <"diamonds",11>,
  <"diamonds",6>,
  <"diamonds",12>,
  <"diamonds",5>,
  <"diamonds",8>
}

3.2.9. Set Splice

Synopsis

Splice the elements of a set in an enclosing set.

Types
Exp Exp1 Expn {Exp1, …​, Exp, …​, Expn}

T

T1

Tn

set[lub(T1, …​, T, …​,Tn)]

Description

The operator * splices the elements of a set in an enclosing set.

Examples

Consider the following set in which the set {10, 20, 30} occurs as set element. It has as type set[value]:

rascal>{1, 2, {10, 20, 30}, 3, 4};
set[value]: {3,2,4,1,{10,20,30}}

The effect of splicing the same set element in the enclosing set gives a flat list of type set[int]:

rascal>{1, 2, *{10, 20, 30}, 3, 4};
set[int]: {10,1,3,20,2,4,30}

The same example can be written as:

rascal>S = {10, 20, 30};
set[int]: {10,20,30}
rascal>{1, 2, *S, 3, 4};
set[int]: {10,1,3,20,2,4,30}

3.2.10. Set StrictSubSet

Synopsis

Strict subset operator on set values.

Syntax

Exp1 < Exp2

Types
Exp1 Exp2 Exp1 < Exp2

set[T1]

set[T2]

bool

Description

Yields true if the value of Exp1 is a strict subset of the value of Exp2, and false otherwise.

Examples
rascal>{1, 2, 3} < {1, 2, 3, 4};
bool: true
rascal>{1, 2, 3} < {1, 3, 4};
bool: false
rascal>{1, 2, 3} < {1, 2, 3};
bool: false

3.2.11. Set StrictSuperSet

Synopsis

Strict superset operator on set values.

Syntax

Exp1 > Exp2

Types
Exp1 Exp2 Exp1 > Exp2

set[T1]

set[T2]

bool

Description

Yields true if the value of Exp1 is a strict superset of the value of Exp2, and false otherwise.

Examples
rascal>{1, 2, 3, 4} > {3, 2, 1};
bool: true
rascal>{1, 2, 3, 4} > {4, 3, 2, 1};
bool: false

3.2.12. Set SubSet

Synopsis

Subset operator on set values.

Syntax

Exp1Exp2

Types
Exp1 Exp2 Exp1Exp2

set[T1]

set[T2]

bool

Description

Yields true if the value of SetExp1 is a subset of the value of SetExp2, and false otherwise.

Examples
rascal>{1, 2, 3} <= {1, 2, 3, 4};
bool: true
rascal>{1, 2, 3} <= {1, 2, 3};
bool: true

3.2.13. Set SuperSet

Synopsis

Superset operator on set values.

Syntax

Exp1 >= Exp2

Types
Exp1 Exp2 Exp1 >= Exp2

set[T1]

set[T2]

bool

Description

Yields true if the value of Exp1 is a superset of the value of Exp2 and false otherwise.

Examples
rascal>{1, 2, 3, 4} >= {3, 2, 1};
bool: true
rascal>{1, 2, 3, 4} >= {4, 3, 2, 1};
bool: true

3.2.14. Set Union

Synopsis

Union of two set values.

Types
Exp1 Exp2 Exp1 + Exp2

set[T1]

set[T2]

set[lub(T1,T2)]

Description

The + operator computes set union if both operands are sets. If one of the operands is not a set, it acts as Set Insert instead.

Examples
rascal>{1, 2, 3} + {4, 5, 6};
set[int]: {5,1,3,2,4,6}
rascal>{1,2,3} + {2,3,4};
set[int]: {1,3,2,4}
rascal>{1, 2, 3} + {3};
set[int]: {1,3,2}
rascal>{2} + { 2, 3, 4};
set[int]: {3,2,4}

3.2.15. Set in

Synopsis

Membership test on set values.

Syntax

Exp1 in Exp2

Types
Exp1 Exp2 Exp1 in Exp2

T1 <: T2

set[T2]

bool

Description

Yields true if the value of Exp1 occurs as element in the value of Exp2 and false otherwise. The type of Exp1 should be compatible with the element type of Exp2.

Examples
rascal>2 in {1, 2, 3};
bool: true
rascal>4 in {1, 2, 3};
bool: false

3.2.16. Set notin

Synopsis

Negated membership test on set values.

Syntax

Exp1 notin Exp2

Types
Exp1 Exp2 Exp1 notin Exp2

T1 <: T2

set[T2]

bool

Description

Yields true if the value of Exp1 does not occur as element in the value of Exp2 and false otherwise. The type of Exp1 should be compatible with the element type of Exp2.

Examples
rascal>4 notin {1, 2, 3};
bool: true
rascal>4 notin {1, 2, 3, 4};
bool: false

3.3. String

Synopsis

String values.

Syntax

"StringChar1StringChar2…​" where `StringChari may be one of the following:

  • Ordinary character: Any character except <, >, ", ' or \.

  • Escaped character: Backslash \ followed by any of <, >, ", ' or \ represents the escaped character itself. Other escape sequences that are supported are:

    • \n: newline

    • \t: tab

    • \r: carriage return

    • \b: backspace

    • \f: vertical feed

    • \u hexDigit1 hexDigit2 hexDigit3 hexDigit4 : hexadecimal escapes with four digit indexes into UNICODE.

    • \U hexDigit1 hexDigit2 hexDigit3 hexDigit4 hexDigit5 hexDigit6 : hexadecimal escapes with six digit indexes into UNICODE.

    • \ a_hexDigit1_ hexDigit2: hexadecimal escapes with 2 digit indexes into ASCII (0x0 …​ 0x7F).

  • String Interpolation:

Form Description

<_Exp_>

Interpolate the value of the expression as a string

<if(Exp){> …​ StringChars …​ <}>

Conditional inclusion of Text, where StringChars may use variables introduced in Exp

<if(Exp){> …​ StringChars1 …​ <} else {> …​ StringChars2 …​ <}>

Conditional inclusion of either StringChars1 or StringChars2

<for(Exp){>…​ StringChars …​ <}>

Iterative splicing of StringChars into the result, where StringChars may use variables introduced in Exp.

<while(Exp){> …​ StringChars …​ <}>

Iterative splicing of StringChars into the result, where StringChars may use variables introduced in Exp.

<do {>…​ StringChars …​ <} while (Exp)>

Iterative splicing of StringChars into the result, where StringChars may use variables introduced in Exp.

  • Multiline:

Form Description

`StringChars1\n StringChars2 `

Strings can be multi-line without an escape or continuation marker

StringChars2\n ' StringChars2

A margin character ' indicates where the next line starts

Types

str

Description

The string values are represented by the type str and consist of character sequences surrounded by double quotes, e.g., "a" or "a\nlong\nstring".

Multiline: Strings may span more than one line. The margin character ' indicates which part of a line will be ignored. This is useful for indenting a multi-line string with the source code that generates it.

Interpolation: String literals support so-called string interpolation: inside string constants text between angle brackets (< and >) is first executed and then replaced by its string value. Various statements (if, for, while, do) also return a value and can be used in this way. In the interpolation variant of these statements the block or blocks that are part of the statement become arbitrary text (that may itself contain interpolations).

Auto-indent: Expressions that get interpolated in a string will be auto-indented. This means that each line that results from the evaluation of the expression is prefixed with the indentation level of the position of the expression in the current string.

The following operators are defined for Strings:

There are also library functions available for Strings.

Examples
rascal>N = 13;
int: 13
rascal>"The value of N is <N>";
str: "The value of N is 13"
rascal>"The value of N*N is <N*N>";
str: "The value of N*N is 169"
rascal>"The value is <(N < 10) ? 10 : N*N>";
str: "The value is 169"

As you can see the string value of variables and expressions is interpolated in the result as expected. <br> Some examples of more advances string interpolation

rascal>"N is <if(N < 10){> small <} else {> large <}>";
str: "N is  large "
rascal>"N is <if(N < 10){> small <} else {> large (<N>)<}>";
str: "N is  large (13)"
rascal>"before <for(x<-[1..5]){>a <x> b <}>after";
str: "before a 1 b a 2 b a 3 b a 4 b after"

multi-line string

rascal>import IO;
ok
rascal>println("hello
>>>>>>>this
>>>>>>>  is
>>>>>>>    new")
hello
this
  is
    new
ok

multi-line string with margin:

rascal>if (true)
>>>>>>>  println("this is
>>>>>>>          'what
>>>>>>>          '  margins
>>>>>>>          'are good for
>>>>>>>          ");
this is
what
  margins
are good for

ok

auto indent:

rascal>str genMethod(str n) = "int <n>() {
>>>>>>>                       '  return 0;
>>>>>>>                       '}";
str (str): function(|prompt:///|(0,99,<1,0>,<3,27>))
rascal>str genClass() = "class myClass {
>>>>>>>                 '  <genMethod("myMethod")>
>>>>>>>                 '}";
str (): function(|prompt:///|(0,99,<1,0>,<3,21>))
rascal>println(genClass());
class myClass {
  int myMethod() {
    return 0;
  }
}
ok
Benefits

String interpolation enables very flexible template-based text generation as used in generators for source code, markup and the like.

3.3.1. String Concatenation

Synopsis

Concatenate two strings.

Syntax

Exp1 + Exp2

Types
Exp1 Exp2 Exp1 + Exp2

str

str

str

Description

Concatenates the string values of Exp1 and Exp2.

Note that to concatenate other types of values into a string, you can use String interpolation.

Examples
rascal>"abc" + "def";
str: "abcdef"

3.3.2. String Equal

Synopsis

Equality operator on string values.

Syntax

Exp1 == Exp2

Types
Exp1 Exp2 Exp1 == Exp2

str

str

bool

Description

Yields true if both arguments are identical and false otherwise.

Examples
rascal>"abc" == "abc";
bool: true
rascal>"abc" == "defghi";
bool: false

3.3.3. String GreaterThan

Synopsis

Greater than operator on string values.

Syntax

Exp1 > Exp2

Types
Exp1 Exp2 Exp1 > Exp2

str

str

bool

Description

Yields true if the string value of Exp1 is strictly lexicographically greater than the string value of Exp2, and false otherwise.

Examples
rascal>"abcdef" > "abc";
bool: true
rascal>"defghi" > "abcdef";
bool: true
rascal>"a" > "abc";
bool: false

3.3.4. String GreaterThanOrEqual

Synopsis

Greater than or equal operator on string values.

Syntax

Exp1 >= Exp2

Types
Exp1 Exp2 Exp1 >= Exp2

str

str

bool

Description

Yields true if the string value of Exp1 is lexicographically greater than the string value of Exp2 or if both strings are equal, and false otherwise.

Examples
rascal>"abc" >= "abc";
bool: true
rascal>"abcdef" >= "abc";
bool: true
rascal>"defghi" >= "abcdef";
bool: true
rascal>"a" >= "abc";
bool: false

3.3.5. String LessThan

Synopsis

Less than operator on string values.

Syntax

Exp1 < Exp2

Types
Exp1 Exp2 Exp1 < Exp2

str

str

bool

Description

Yields true if the string value of Exp1 is strictly lexicographically less than the string value of Exp2, and false otherwise.

Examples
rascal>"abc" < "abcdef";
bool: true
rascal>"abc" < "defghi";
bool: true
rascal>"abc" < "a";
bool: false

3.3.6. String LessThanOrEqual

Synopsis

Less than or equal operator on string values.

Syntax

Exp1Exp2

Types
Exp1 Exp2 Exp1Exp2

str

str

bool

Description

Yields true if the string value of Exp1 is lexicographically less than the string value of Exp2 or if both string are equal, and false otherwise.

Examples
rascal>"abc" <= "abc";
bool: true
rascal>"abc" <= "abcdef";
bool: true
rascal>"abc" <= "defghi";
bool: true
rascal>"abc" <= "a";
bool: false

3.3.7. String NotEqual

Synopsis

Not equal operator on string values.

Syntax

Exp1 != Exp2

Types
Exp1 Exp2 Exp1 != Exp2

str

str

bool

Description

Yields true if both arguments are not identical and false otherwise.

Examples
rascal>"abc" != "defghi";
bool: true
rascal>"abc" != "abc";
bool: false

3.3.8. String Slice

Synopsis

Retrieve a slice of a string.

3.3.9. Exp1 [ Exp2 .. Exp4]

3.3.10. Exp1 [ Exp2 , Exp3 .. Exp4]

Syntax

where Exp2 and Exp4 are optional.

Types
Exp1 Exp2 Exp3 Exp4 Exp1 [ Exp2 .. Exp4 ] or Exp1 [ Exp2 , Exp3 .. Exp4]

str

int

int

int

str

Description

A String slice is similar to a list List Slice and uses the integer values of Exp2 and Exp4 to determine the begin (inclusive) and end (exclusive) of a slice from the string value S of Exp1. Negative indices count from the end of the string backwards. Using the second form, an extra index Exp3 is given that determines the index of the second element in the slice and establishes the step between successive elements in the slice. The default step is 1. If end is smaller than begin, the slice is constructed backwards.

Let Len be the length of S and let N2, N3 and N4 be the respective values of the expressions Exp2, Exp2 and Exp2 when they are present.

The slice parameters begin, end, and step are determined as follows:

  • Exp2:

    • If Exp2 is absent, then begin = 0.

    • Otherwise, if N2 >= 0 then begin = N2 else begin = N2 + Len.

  • Exp4:

    • If Exp4 is absent, then end = Len.

    • Otherwise, if N4 >= 0, then end = N4 else end = N4 + Len.

  • Exp3:

    • If Exp3 is absent, then if begin < end then step = 1 else step = -1.

    • Otherwise, if begin < end, then step = N3 - begin else step = begin - N3.

Now, the constraints 0 ⇐ begin < Len and 0 < end < Len should hold, otherwise the exception IndexOutOfBounds is thrown.

The slice consists of the elements S[begin], S[begin+step], S[end - step]. When begin >= end, the elements are listed in reverse order.

Examples

Consider the string S = "abcdefghi"; (with size 9) as running example.

Here is a view on L that will help to correlate positive and negative indices:

i 0 1 2 3 4 5 6 7 8

S[i]

"a"

"b"

"c"

"d"

"e"

"f"

"g"

"h"

"i"

-i

-9

-8

-7

-6

-5

-4

-3

-2

-1

Some common use cases (with beginend):

Slice Means:

S[begin..end]

characters with indices begin through end-1

S[begin..]

characters with indices begin through the rest of the string

S[..end]

characters with indices from the beginning through end-1

S[..]

the whole list

S[-1]

last element of the string

S[-2..]

the last two characters of the string

S[..-2]

all characters except the last two.

Let’s put this into practice now.

rascal>S = "abcdefghi";
str: "abcdefghi"

Slices with begin < end

rascal>S[1..3];
str: "bc"
rascal>S[1..];       // empty end => end of string
str: "bcdefghi"
rascal>S[..3];       // empty begin => first character of string
str: "abc"
rascal>S[..];        // both empty => whole string
str: "abcdefghi"

Slices with begin >= end

rascal>S[3..1];      // slice contains characters with indices 3 and 2 (in that order)
str: "dc"
rascal>S[3..3];      // empty slice when begin == end
str: ""

Slices with negative begin or end:

rascal>S[2..-2];     // equivalent to S[2..7]
str: "cdefg"
rascal>S[2..7];
str: "cdefg"
rascal>S[-4..-2];    // equivalent to S[5..7]
str: "fg"
rascal>S[5..7];
str: "fg"

Slices with an explicit second index:

rascal>S[1,3..6];
str: "bdf"
rascal>S[5,3..];
str: "fdb"

Explore error cases:

rascal>S[..10];
str: "abcdefghi"
rascal>S[1..20];
str: "bcdefghi"

3.3.11. String Subscription

Synopsis

Retrieve a substring via its index.

Syntax

Exp1 [ Exp2 ]

Types
Exp1 Exp2 Exp1 [ Exp2 ]

str

int

str

Description

String subscription uses the integer value of Exp2 as index in the string value of Exp1. The value of Exp2 should be greater or equal 0 and less than the number of characters in the string. If this is not the case, the exception IndexOutOfBounds is thrown.

Examples

Introduce a string, assign it to S and retrieve the element with index 1:

rascal>S = "abc";
str: "abc"
rascal>S[1];
str: "b"

Explore an error case:

rascal>S[5];
|prompt:///|(2,1,<1,2>,<1,3>): IndexOutOfBounds(5)
	at $shell$(|prompt:///|(0,5,<1,0>,<1,5>))
 
ok

3.4. Tuple

Synopsis

Tuple values.

Syntax

< Exp1, Exp2, …​ >

Types
Exp1 Exp2 …​ < Exp1, Exp2, …​ >

T1

T2

…​

tuple[T1, T2, …​ ]

Description

A tuple is a sequence of elements with the following properties:

  • Each element in a tuple (may) have a different type.

  • Each element of a tuple may have a label that can be used to select that element of the tuple.

  • Each tuple is fixed-width, i.e., has the same number of elements.

Tuples are represented by the type tuple[T1 L1, T2 L2, …​], where T1, T2, …​ are arbitrary types and L1, L2, …​ are optional labels.

The following operators are provided for tuples:

Examples
rascal>tuple[str first, str last, int age] P = <"Jo","Jones",35>;
tuple[str first,str last,int age]: <"Jo","Jones",35>
rascal>P.first;
str: "Jo"
rascal>P.first = "Bo";
tuple[str first,str last,int age]: <"Bo","Jones",35>

3.4.1. Tuple Concatenation

Synopsis

Concatenate two tuple values.

Syntax

Exp1 + Exp2

Types
Exp1 Exp_2 Exp1 > Exp_2

tuple[ T11, T12, …​ ]

tuple[ T21, T22, …​ ]

tuple[ T11, T12, …​, T21, T22, …​ ]

Description

Returns a tuple consisting of the concatenation of the tuple elements of Exp1 and Exp2.

Examples
rascal><"abc", 1, 2.5> + <true, "def">;
tuple[str,int,real,bool,str]: <"abc",1,2.5,true,"def">

3.4.2. Tuple Equal

Synopsis

Equality operator on tuple values.

Syntax

Exp1 == Exp2

Types
Exp1 Exp2 Exp1 == Exp2

tuple[ T11, T12, …​ ]

tuple[ T21, T22, …​ ]

bool

Description

Yields true if both tuples are identical and false otherwise.

Examples
rascal><1, "abc", true> == <1, "abc", true>;
bool: true

3.4.3. Tuple FieldSelection

Synopsis

Select a field from a tuple by its field name.

Syntax

Exp . Name

Types
Exp Name Exp . Name

tuple[ T1 L1, T2 L2, …​ ]

Li

Ti

Description

Field selection applies to tuples with named elements. Exp should evaluate to a tuple with field Name and returns the value of that field. Name stands for itself and is not evaluated.

Examples
rascal>tuple[int key, str val] T = <1, "abc">;
tuple[int key,str val]: <1,"abc">
rascal>T.val;
str: "abc"

3.4.4. Tuple GreaterThan

Synopsis

Greater than operator on tuple values.

Syntax

Exp1 > Exp2

Types
Exp1 Exp2 Exp1 > Exp2

tuple[ T11, T12, …​ ]

tuple[ T21, T22, …​ ]

bool

Description

Yields true if

  • both tuples are not equal, and

  • the left-most element in the tuple value of Exp1 that differs from the corresponding element in the tuple value of Exp2 is greater than that element in Exp2.

Otherwise the result if false.

Examples
rascal><1, "def", true> > <1, "abc", true>;
bool: true

3.4.5. Tuple GreaterThanOrEqual

Synopsis

Greater than or equal operator on tuple values.

Syntax

Exp1 >= Exp2

Types
Exp1 Exp2 Exp1 >= Exp2

tuple[ T11, T12, …​ ]

tuple[ T21, T22, …​ ]

bool

Description

Yields true if

  • both tuples are equal, or

  • the left-most element in the tuple value of Exp1 that differs from the corresponding element in the tuple value of Exp2 is greater than that element in Exp2.

Otherwise the result if false.

Examples
rascal><1, "abc", true> > <1, "abc", true>;
bool: false
rascal><1, "def", true> > <1, "abc", true>;
bool: true

3.4.6. Tuple LessThan

Synopsis

Less than operator on tuple values.

Syntax

Exp1 < Exp2

Types
Exp1 Exp2 Exp1 < Exp2

tuple[ T11, T12, …​ ]

tuple[ T21, T22, …​ ]

bool

Description

Yields true if

  • both tuples are not equal, and

  • the left-most element in the tuple value of Exp1 that differs from the corresponding element in the tuple value of Exp2 is less than that element in Exp2.

Otherwise the result if false.

Examples
rascal><1, "abc", true> < <1, "def", true>;
bool: true

3.4.7. Tuple LessThanOrEqual

Synopsis

Less than or equal operator on tuple values.

Syntax

Exp1Exp2

Types
Exp1 Exp2 Exp1Exp2

tuple[ T11, T12, …​ ]

tuple[ T21, T22, …​ ]

bool

Description

Yields true if

  • both tuples are equal, or

  • the left-most element in the tuple value of Exp1 that differs from the corresponding element in the tuple value of Exp2 is less than that element in Exp2.

Otherwise the result if false.

Examples
rascal><1, "abc", true> <= <1, "abc", true>;
bool: true
rascal><1, "abc", true> <= <1, "def", true>;
bool: true

3.4.8. Tuple NotEqual

Synopsis

Not equal operator on tuple values.

Syntax

Exp1 != Exp2

Types
Exp1 Exp2 Exp1 != Exp2

tuple[ T11, T12, …​ ]

tuple[ T21, T22, …​ ]

bool

Description

Yields true if both tuples are not identical and false otherwise.

Examples
rascal><1, "abc", true> != <1, "abc">;
bool: true
rascal><1, "abc", true> != <1, "abc", true>;
bool: false

3.4.9. Tuple Subscription

Synopsis

Retrieve a tuple field by its index position.

Syntax

Exp1 [ Exp2 ]

Description

Subscription retrieves the tuple element with index Exp2 from the tuple value of Exp1.

Examples

Introduce a tuple, assign it to T and retrieve the element with index 0:

rascal>T = <"mon", 1>;
tuple[str,int]: <"mon",1>
rascal>T[0];
str: "mon"

3.5. Value

Synopsis

Values of type value.

Types

value

Description

Value stands for all possible Rascal values and is represented by the type value. This type is a container for all other types and does not have any values itself.

The following operators are provided for values:

3.5.1. Value Conditional

Synopsis

Conditional expression on values.

Syntax

Exp1 ? Exp2 : Exp3

Types
Exp1 Exp2 Exp3 Exp1 ? Exp2 : Exp3

bool

T2

T3

lub(T2,T3)

Description

Yields the value of Exp2 if the value of Exp1 is true and the value of Exp3 otherwise. The result type is the least upper bound (also known as lub, see Static Typing) of the types of Exp2 and Exp3.

Examples
rascal>( 3 > 2 ) ? 30 : 40;
int: 30
rascal>( 3 < 2 ) ? "abc" : {3, 4};
set[int]: {3,4}

3.5.2. Value Equal

Synopsis

Equal operator on values.

Syntax

Exp1 == Exp2

Types
Exp1 Exp2 Exp1 == Exp2

value

value

bool

Description

Yields true if both arguments are identical and false otherwise.

Examples

Introduce two variables X, Y and Z and force them to be of type value:

rascal>value X = "abc";
value: "abc"
rascal>value Y = "abc";
value: "abc"
rascal>value Z = 3.14;
value: 3.14

Now compare X and Y for equality:

rascal>X == Y;
bool: true

and X and Z:

rascal>X == Z;
bool: false

3.5.3. Value GreaterThan

Synopsis

Greater than operator on values.

Syntax

Exp1 > Exp2

Types
Exp1 Exp2 Exp1 > Exp2

value

value

bool

Description

By brute force, a total less than operator between two values V1 and V2 of arbitrary types T1 and T2 is defined:

  • If the types T1 and T2 can be compared then V1 less than V2 is used.

  • Otherwise values are ordered according their type name, for instance, int is smaller than list, and map is smaller than rel.

Greater than yields true if the value of Exp2 is strictly less than (according to the ordering defined above) the value of Exp1, and false otherwise.

Examples

Introduce two variables X, Y and Z and force them to be of type value:

rascal>value X = "def";
value: "def"
rascal>value Y = "abc";
value: "abc"
rascal>value Z = 3.14;
value: 3.14

Now compare X and Y:

rascal>X > Y;
bool: true

and X and Z:

rascal>X > Z;
bool: false

3.5.4. Value GreaterThanOrEqual

Synopsis

Greater than or equal operator on values.

Syntax

Exp1 >= Exp2

Types
Exp1 Exp2 Exp1 >= Exp2

value

value

bool

Description

By brute force, a total less than operator between two values V1 and V2 of arbitrary types T1 and T2 is defined:

  • If the types T1 and T2 can be compared then V1 less than V2 is used.

  • Otherwise values are ordered according their type name, for instance, int is smaller than list, and map is smaller than rel.

Greater than or equal yields true if the value of Exp2 is strictly less than (according to the ordering defined above) the value of Exp1 or if both values are equal, and false otherwise.

Examples

Introduce two variables X, Y and Z and force them to be of type value:

rascal>value X = "def";
value: "def"
rascal>value Y = "abc";
value: "abc"
rascal>value Z = 3.14;
value: 3.14

Now compare X and Y:

rascal>X >= Y;
bool: true

and X and Z:

rascal>X >= Z;
bool: false

3.5.5. Value LessThan

Synopsis

Less than operator on values.

Syntax

Exp1 < Exp2

Types
Exp1 Exp2 Exp1 < Exp2

value

value

bool

Description

By brute force, a total less than operator between two values V1 and V2 of arbitrary types T1 and T2 is defined:

  • If the types T1 and T2 can be compared then V1 less than V2 is used.

  • Otherwise values are ordered according their type name, for instance, int is smaller than list, and map is smaller than rel.

Less than yields true if the value of Exp1 is strictly less than (according to the ordering defined above) the value of Exp2, and false otherwise.

Examples

Introduce two variables X, Y and Z and force them to be of type value:

rascal>value X = "abc";
|prompt:///|(6,9,<1,6>,<1,15>): Redeclared variable: X
Advice: |http://tutor.rascal-mpl.org/Errors/Static/RedeclaredVariable/RedeclaredVariable.html|
ok
rascal>value Y = "def";
|prompt:///|(6,9,<1,6>,<1,15>): Redeclared variable: Y
Advice: |http://tutor.rascal-mpl.org/Errors/Static/RedeclaredVariable/RedeclaredVariable.html|
ok
rascal>value Z = 3.14;
|prompt:///|(6,8,<1,6>,<1,14>): Redeclared variable: Z
Advice: |http://tutor.rascal-mpl.org/Errors/Static/RedeclaredVariable/RedeclaredVariable.html|
ok

WARNING: unexpected errors in the above SHELL example. Documentation author please fix! Now compare X and Y:

rascal>X < Y;
bool: false

and X and Z:

rascal>X < Z;
bool: false

3.5.6. Value LessThanOrEqual

Synopsis

Less than or equal operator on values.

Syntax

Exp1Exp2

Types
Exp1 Exp2 Exp1Exp2

value

value

bool

Description

By brute force, a total less than operator between two values V1 and V2 of arbitrary types T1 and T2 is defined:

  • If the types T1 and T2 can be compared then V1 less than V2 is used.

  • Otherwise values are ordered according their type name, for instance, int is smaller than list, and map is smaller than rel.

Less than or equal yields true if the value of Exp1 is strictly less than (according to the ordering defined above) the value of Exp2 or if both values are equal, and false otherwise.

Examples

Introduce two variables X, Y and Z and force them to be of type value:

rascal>value X = "abc";
value: "abc"
rascal>value Y = "def";
value: "def"
rascal>value Z = 3.14;
value: 3.14

Now compare X and Y:

rascal>X <= Y;
bool: true

and X and Z:

rascal>X <= Z;
bool: false

3.5.7. Value NotEqual

Synopsis

Not equal operator on values.

Syntax

Exp1 != Exp2

Types
Exp1 Exp2 Exp1 != Exp2

value

value

bool

Description

Yields true if both arguments are not identical and false otherwise.

Examples

Introduce two variables X, Y and Z and force them to be of type value:

rascal>value X = "abc";
value: "abc"
rascal>value Y = "abc";
value: "abc"
rascal>value Z = 3.14;
value: 3.14

Now compare X and Y for inequality:

rascal>X != Y;
bool: false

and X and Z:

rascal>X != Z;
bool: true

3.6. Void

Synopsis

Values of type void.

Types

void

Description

Void stands for nothing and is represented by the type void. It is a type without any values.

4. Operators

Synopsis

The Rascal operators.

Description

An operator expression consists of an operator and one or more operands. The evaluation order of the operands depends on the operator. The operator is applied to the operands and the resulting value (or values in some cases) is the result of the operator expression.

All operators are summarized in the following table. They are listed from highest precedence to lowest precedence. In other words, operators listed earlier in the table bind stronger.

Operator See Short Description

Exp . Name

Location, DateTime, Tuple, [Relation-FieldSelection.Relation],

Select named field from structured value

Exp1 [ Name = Exp2 ]

[FieldAssignment]

Change value of named field of structured value

Exp < field1, …​ >

[FieldProjection]

Select fields from relation or structured value

Exp is Name

[ParseTree], Concrete Syntax, Algebraic Data Type

Returns true if and only if the constructor name of the value produced by Exp is equal to Name

Exp has Name

[ParseTree], Concrete Syntax, Algebraic Data Type

Returns true if and only if the constructor (node or parse tree) of the value produced by Exp has any field labeled Name

Exp1 [ Exp2 , Exp3, …​. ]

List, Map, Tuple, Relation

Retrieve values for given index/key from list, map, tuple or relation.

Exp1 [ Exp2 , Exp3 .. Exp4 ]

List, String, Node

Retrieve a slice from a list, string, or node.

Exp?

Boolean

Test whether an expression has a defined value

!Exp

Boolean

Negate a Boolean value

- Exp

Number

Negation of numbers

Exp +

Relation, ListRelation,

Transitive closure on relation or list relation

Exp *

Relation, ListRelation

Reflexive transitive closure on relation or list relation

Exp @ Name

[Expressions-Selection]

Value of annotation Name of Exp's value

Exp1 [@ Name = Exp2]

[Expressions-Replacement]

Assign value of Exp2 to annotation Name of Exp1's value

Exp1 o Exp2

Relation, Map

Exp1 and Exp2 should evaluate to a relation or map; return their composition. Note: the letter "o" is thus a keyword

Exp1 / Exp2

Number

Divide two numbers

Exp1 % Exp2

Number

Remainder on numbers

Exp1 * Exp2

Number, List, Set, Relation

Multiply numbers; product of list, set, or relation

Exp1 & Exp2

List, Set, Map

Intersection of list, set (including relation), or map

Exp1 + Exp2

Number, String, List Concatenation, List Insert,List Append, Tuple Concatenation, Set, Map, Location

Add numbers; concatenate string, list or tuple; union on set (including relation), or map; concatenate location and string

Exp1 - Exp2

Number, List, Set, Map

Subtract numbers; difference of list, set (including relation), or map

Exp1 join Exp2

Relation

Join on relation

Exp1 in Exp2

List, Set, Map

Membership test for element in list, map, set (including relation)

Exp1 notin Exp2

List, Set, Map

Negated membership test for element in list, map, set (including relation)

Exp1Exp2

Number, String, Location, DateTime, List, Set, Map

Less than or equal on all values

Exp1 < Exp2

Number, String, Location, dateTime, List, Set, Map

Less than on all values

Exp1 >= Exp2

Number, String, Location, DateTime, List, Set, Map

Greater than or equal on all values

Exp1 > Exp2

Number, String, Location, DateTime, List, Set, Map

Greater than on all values.

Pat := Exp

Boolean Match

Pattern matches value of expression

Pat !:= Exp

Boolean NoMatch

Pattern does not match value of expression

Exp1 == Exp2

Number, String, Location, DateTime, List, Set, Map

Equality on all values

Exp1 != Exp2

Number, String, Location, DateTime, List, Set, Map

Inequality on all values

Exp1 ? Exp2

Boolean IfDefinedElse

Value of expression when it is defined, otherwise alternative value

Exp1 ? Exp2 : Exp3

Value Conditional

Conditional expression for all types

Exp1 =⇒ Exp2

Boolean Implication

Implication on Boolean values

Exp1 ⇐⇒ Exp2

Boolean Equivalence

Equivalence on Boolean values

Exp1 && Exp2

Boolean And

And on Boolean values

Exp1 || Exp2

Boolean Or

Or on Boolean values

4.1. Field Assignment

Synopsis

Assignment to a field of a tuple or datatype.

Syntax

Exp1 [ Name = Exp2 ]

Description

Exp1 should evaluate to a tuple or datatype with a field Name; assign the value of Exp2 to that field

Field assignment applies to all values that have named components like tuples and relations with named elements, data types, and locations. Field assignment returns a new value in which the named component has been replaced by a new value. Name stands for itself and is not evaluated.

Examples
rascal>tuple[int key, str val] T = <1, "abc">;
tuple[int key,str val]: <1,"abc">
rascal>T[val = "def"];
tuple[int key,str val]: <1,"def">
rascal> T;
tuple[int key,str val]: <1,"abc">

Observe that field assignment creates a new value with an updated field. The old value remains unchanged as can be seen from the unchanged value of T in the above example.

4.2. Field Projection

Synopsis

Projection of tuple.

Syntax

Exp < Field1, Field2 …​ >

Description

Exp should evaluate to a tuple or relation, and Fieldi should be a field name or an integer constant that refers to elements in the order in which they occur in the original value (counting from 0).

Examples

Suppose we have a relation with traffic information that records the name of the day, the day number, and the length of the traffic jams at that day.

rascal>rel[str day, int daynum, int length] Traffic =
>>>>>>>{<"mon", 1, 100>, <"tue", 2, 150>, <"wed", 3, 125>,
>>>>>>> <"thur", 4, 110>, <"fri", 5, 90>};
rel[str day,int daynum,int length]: {
  <"thur",4,110>,
  <"mon",1,100>,
  <"fri",5,90>,
  <"wed",3,125>,
  <"tue",2,150>
}
rascal>Traffic<length,daynum>;
rel[int length,int daynum]: {
  <110,4>,
  <125,3>,
  <90,5>,
  <150,2>,
  <100,1>
}
rascal>Traffic<2,day>;
rel[int length,str day]: {
  <110,"thur">,
  <125,"wed">,
  <90,"fri">,
  <150,"tue">,
  <100,"mon">
}

Field projection thus selects parts from a larger value that has a fixed number of parts. The selection is based on position and not on value and can be used to completely reorder or remove the parts of a larger value.

5. Call

Synopsis

Function call.

Syntax

Name ( Exp1, Exp2, …​ )

Types
Exp1 Exp2 …​ Name ( Exp1, Exp2, …​ )

T1

T2

…​

Determined by Name, Ti and function declarations

Description

First, the actual parameter expressions Expi are evaluated resulting in values Vi. Based on Name and the argument types Ti, the identity of the function to be called is determined.

The values Vi are bound to the formal parameter names of the declared functions and the function body is executed. The value returned by the function is used as value of the function call.

A constructor call has identical syntax to that of a function call, see Constructor,

See Function Declaration for more details about function declarations.

Describe keyword parameters.
Examples

First declare a function square with argument n that returns n^2:

rascal>int square(int n) { return n * n; }
int (int): function(|prompt:///|(0,35,<1,0>,<1,35>))

Next call square. This results in the following steps:

  • Based on the name square and the int argument 12 we identify the function to be called (= the function square we just defined).

  • Compute the value of the actual parameter (= 12).

  • Bind the formal parameter n to the actual value 12.

  • Execute the body of square.

  • The return value of square is the vale of the call:

rascal>square(12);
int: 144

6. Comprehensions

Synopsis

Comprehensions provide a concise notation to conditionally generate new values.

Description

Comprehensions are defined for the following types:

The syntax varies slightly for each type, but comprehensions have the following common elements:

  • A generator can come in two flavours:

    • an enumerator that generates all the values in some subject value.

    • a filter that performs an arbitrary test on previously generated values.

  • One or more contributing expressions that are added to the list, set or map that is being constructed.

The contributing expressions are evaluated for all possible values of the enumerators that are not excluded by a test. When a filter fails, execution continues with the preceding enumerator (if any).

Each enumerator may introduce new variables that can be used in subsequent generators as well as in the contributing expressions. A generator can use the variables introduced by preceding generators.

Examples

A list comprehension:

rascal>[ 3 * X | int X <- [1 .. 10] ];
list[int]: [3,6,9,12,15,18,21,24,27]

A list comprehension with a filter:

rascal>[ 3 * X | int X <- [1 .. 10], X > 5];
list[int]: [18,21,24,27]

A list comprehension with multiple contributing expressions:

rascal>[X, X * X | int X <- [1, 2, 3, 4, 5], X >= 3];
list[int]: [3,9,4,16,5,25]

A set comprehension with a filter:

rascal>{X | int X <- {1, 2, 3, 4, 5}, X >= 3};
set[int]: {5,3,4}

A set comprehension that constructs a relation:

rascal>{<X, Y> | int X <- {1, 2, 3}, int Y <- {2, 3, 4}, X >= Y};
rel[int,int]: {
  <2,2>,
  <3,3>,
  <3,2>
}
rascal>{<Y, X> | <int X, int Y> <- {<1,10>, <2,20>}};
rel[int,int]: {
  <10,1>,
  <20,2>
}

Introduce a map of fruits and use a map comprehension to filter fruits with an associated value larger than 10:

rascal>fruits = ("pear" : 1, "apple" : 3, "banana" : 0, "berry" : 25, "orange": 35);
map[str, int]: ("banana":0,"pear":1,"orange":35,"berry":25,"apple":3)
rascal>(fruit : fruits[fruit] | fruit <- fruits, fruits[fruit] > 10);
map[str, int]: ("orange":35,"berry":25)

6.1. Enumerator

Synopsis

Enumerate all values in a given subject value.

Syntax

PatternExp

Description

An enumerator generates all the values in a given subject value. For elementary types (bool, int, real, num, loc, datetime, str) this is just a singleton. For composite types (list, set, map, tuple, rel, node) the values of their elements, respectively, their direct children are enumerated. An enumerator is evaluated as follows:

  • Expression Exp is evaluated and may have an arbitrary value V.

  • The elements of V are enumerated one by one.

  • Each element value is matched against the pattern Pattern. There are two cases:

    • The match succeeds, any variables in Pattern are bound, and the next generator in the comprehension is evaluated. The variables that are introduced by an enumerator are only available to generators that appear later (i.e., to the right) in the comprehension. When this enumerator is the last generator in the comprehension, the contributing expressions of the comprehension are evaluated.

    • The match fails, no variables are bound. If V has more elements, a next element is tried. Otherwise, a previous generator (i.e., to the left) is tried. If this enumerator is the first generator in the comprehension, the evaluation of the comprehension is complete.

Type information is used to check the validity of an enumerator and guard you against mistakes. An impossible enumerator like

int N <- {"apples", "oranges"}

will be flagged as an error since the pattern can never match.

Examples

Here are some examples of enumerators:

  • int N ← {1, 2, 3, 4, 5}.

  • str K ← KEYWORDS, where KEYWORDS should evaluate to a value of set[str].

  • <str K, int N> ← {<"a",10>, <"b",20>, <"c",30>}.

  • <str K, int N> ← FREQUENCIES, where FREQUENCIES should evaluate to a value of type rel[str,int].

  • <str K, 10> ← FREQUENCIES, will only generate pairs with 10 as second element.

  • int N ← 17, will only generate the value 17.

Here are examples of enumerators in action:

rascal>[ N * N | int N <- [1, 2, 3, 4, 5] ];
list[int]: [1,4,9,16,25]
rascal>{<N, K> | <str K, int N> <- {<"a",10>, <"b",20>, <"c",30>}};
rel[int,str]: {
  <10,"a">,
  <20,"b">,
  <30,"c">
}

6.2. Filter

Synopsis

Filter values in a List Comprehension], Set Comprehension or Map Comprehension.

Syntax

Exp

Types
Exp

bool

Description

A filter is a boolean-valued expression. If the evaluation of the filter gives true this indicates that the current combination of generated values up to this filter is still desired and execution continues with subsequent generators. If the evaluation gives false this indicates that the current combination of values is undesired, and that another combination should be tried by going back to the previous generator.

Examples

Adding a filter to a comprehension, may restrict the values that are included in the result of the comprehension:

rascal>[ X * X | int X <- [1, 2, 3, 4, 5, 6] ];
list[int]: [1,4,9,16,25,36]
rascal>[ X * X | int X <- [1, 2, 3, 4, 5, 6], X % 3 == 0 ];
list[int]: [9,36]

Filters can also be applied to values produced by several generators:

rascal>[<X, Y> | int X <- [0 .. 10], int Y <- [0 .. 10], X + Y == 10]
lrel[int,int]: [
  <1,9>,
  <2,8>,
  <3,7>,
  <4,6>,
  <5,5>,
  <6,4>,
  <7,3>,
  <8,2>,
  <9,1>
]

7. Concrete Syntax

Synopsis

Concrete syntax is a notation for patterns that match parse trees and expressions that generate them.

Syntax
  • (Nonterminal) `sentence`

  • ` sentence `

where each sentence is a string over the language generated from Nonterminal. This language is extended in the following way. Each non-terminal reachable X from Non-terminal has an added alternative to allow nested Rascal Patterns inside of concrete syntax fragments:

  • syntax X = "<" Pattern p ">";

Description

A concrete syntax fragment allows the programmer to write patterns and expressions in the language that is currently analyzed, transformed or generated. The Concrete Syntax feature is derived from Syntax Definitions. For any non-terminal defined in a Syntax Definition, you may use Concrete Syntax to match or generate its parse trees.

Benefits
  • Easy notation for complex structures

Pitfalls
  • Since Rascal currently has no type-checker, the disambiguation of concrete syntax fragments is done heuristically which may lead to surprises.

  • The disambiguation of embedded concrete syntax fragments may change in the near future.

8. Reducer

Synopsis

Reduce generated values to a single value.

Syntax

( InitExp | RedExp | Gen1, Gen2, …​ )

Description

A reducer resembles the fold function found in most functional languages.

A reducer is equivalent to the following code:

it = InitExp; (1)
for(Gen1, Gen2, ... ) (2)
    it = RedExp; (3)
it; (4)

and is executed as follows:

1 A fresh variable it is initialized with InitExp. We call the variable it since we use it to initialize the reducer, to make changes to it, and to return it as result.
2 A for loop iterates over all values produced by the generators Gen1, Gen2, …​ .
3 In the body of the loop, variable it is updated to reflect a new reduced value. Note that it itself and variables introduced in Gen1, Gen2, …​ may occur in RedExp.
4 The value of it is the result of the reducer.
Examples
rascal>L = [1, 3, 5, 7];
list[int]: [1,3,5,7]
rascal>(0 | it + e | int e <- L);
int: 16
rascal>(1 | it * e | int e <- L);
int: 105

9. Statement as Expression

Synopsis

Statements that have a value and can be used as expressions.

Description

Several forms of statements produce a value and can be used as expression. This is further explained in the sections for the relevant statements, see If, While, Do and For.

Pitfalls

It is likely that the design of Rascal will evolve into completely merging expressions and statements.

10. Visit

Synopsis

Visit the elements in a tree or value.

Syntax
Strategy visit ( Exp ) {
case PatternWithAction1;
case PatternWithAction2;
...
default: ...
}
Description

Visiting, recursively traversing, the nodes in a deeply nested data-structure is a very common task in the EASY domain. In many cases (but certainly not all) this data-structure is a syntax tree of some source code file and the nodes correspond to expressions or statements.

The visit expression/statement allows to focus on the points of interest in the data-structure while automating the search over the other parts for the programmer.

Computing metrics or refactoring are examples of tasks that require a tree visit. There are three frequently occurring scenarios:

  • Accumulator: traverse the tree and collect information (fold).

  • Transformer: traverse the tree and transform it into another tree (map).

  • Accumulating Transformer: traverse the tree, collect information and also transform the tree.

The visit expression in Rascal can accommodate all these (and more) use cases.

Given a subject term (the current value of Exp) and a list of cases (consisting of a sequence of [Pattern with Action]s, it traverses the term. Depending on the precise actions it may perform replacement (mimicking a transformer), update local variables (mimicking an accumulator) or a combination of these two (accumulating transformer). If any of the actions contains an Insert statement, the value of the visit expression is a new value that is obtained by successive insertions in the subject term by executing one or more cases. Otherwise, the original value of the subject term is returned.

The visit expression is optionally preceded by one of the following strategy indications that determine the traversal order of the subject:

  • top-down: visit the subject from root to leaves.

  • top-down-break: visit the subject from root to leaves, but stop at the current path when a case matches.

  • bottom-up: visit the subject from leaves to root (this is the default).

  • bottom-up-break: visit the subject from leaves to root, but stop at the current path when a case matches.

  • innermost: repeat a bottom-up traversal as long as a case matches.

  • outermost: repeat a top-down traversal as long as a case matches.

The execution of the cases has the following effect:

  • A PatternWithAction of the form PatternExp replaces the current subtree of the subject by the value of Exp. Note that a copy of the subject is created at the start of the visit statement and all replacements are made in this copy. As a consequence, modifications made during the visit cannot influence matches later on. The modified copy of the subject is ultimately returned by the visit expression.

  • A PatternWithAction of the form Pattern : Statement executes Statement and this should lead to one of the following:

    • Execution of an Insert statement of the form insert Exp2. The value of Exp2 replaces the subtree of the subject that is currently being visited. Once again, this modification takes place in a copy of the original subject (see above). Note that:

      • An insert statement may only occur in a PatternWithAction in a visit expression or a rule.

      • PatternExp is equivalent to Pattern : insert Exp;.

    • Execution of a Fail statement: the next case is tried.

    • Execution of a Return statement that returns a value from the enclosing function.

The precise behaviour of the visit expression depends on the type of the subject:

  • For type node or ADT, all nodes of the tree are visited (in the order determined by the strategy). Concrete patterns and abstract patterns directly match tree nodes. Regular expression patterns match only values of type string.

  • For other structured types (list, set, map, tuple, rel), the elements of the structured type are visited and matched against the cases. When inserts are made, a new structured value is created. In these cases a strategy does not have any effect.

Examples

Visit a value and increment a counter for pattern leaf(int N) matches:

visit(t) {
     case leaf(int N): c = c + N;
   };

Replace all values that match the pattern red(l, r):

visit(t) {
     case red(l, r) => green(l, r)
   };

Do a bottom-up visit of an expression and apply the function simp to each subexpression:

bottom-up visit(e){
           case Exp e1 => simp(e1)
         }

More examples can, for instance, be found in Recipes, see ColoredTrees, WordReplacement, CountConstructors, and Derivative.

10.1. Pattern With Action

Synopsis

A pattern with an associated action that is executed on a successful match.

Syntax
  • PatternExp

  • Pattern: Statement

Description

Patterns can be used in various contexts, but a common context is a PatternWithAction, which in its turn, may be used in various statements such Switch and Visit.

There are two variants as listed above:

  • When the subject matches Pattern, the expression Exp is evaluated and the subject is replaced with the result.

  • When the subject matches Pat, the Statement is executed. More statements can be executed by including them in a Block.

In Switch statements, only the form Pattern : Statement is allowed. When the subject matches Pattern, the Statement is executed and the execution of the switch statement is complete. However, when a fail statement is executed in Statement further alternatives of Pattern are tried. If no alternatives remain, PatternWithAction as a whole fails and subsequent cases of the switch statement are tried.

In Visit expressions, the form PatternExp describes subtree replacement: the current subtree of the subject of the visit expression is replaced by the value of Exp. The form Pattern : Statement is as described for switch statements, with the addition that execution of an Insert statement will replace the current subtree. After both success or failure of the PatternWithAction, the traversal of the subject continues.

Examples

Two examples of variant 1 (replacement):

case red(CTree l, CTree r) => red(r,l)
case red(l, r) => green(l, r)

Three examples of variant 2 (Statement):

case /Leila/: println("The topic is Starwars");
case red(_, _):    println("A red root node");
case red(_,_): c = c + 1;

The action may also be a Block:

case red(_,_): { c = c + 1; println("c = <c>"); }

Statements

Synopsis

All Rascal statements.

Description

The following statements are available:

  • Append: Append an element to the list value produced by various loop statements.

  • Assert: An executable assertion.

  • Assignment: Assign a value to a variable or more complex data structure.

    • Annotation:

    • Constructor: Assign to constructor.

    • Field: Assign to a field of a tuple, relation or datatype.

    • IsDefined: Assign but replace if value is not defined.

    • Multiple: Assign to multiple assignables.

    • Slice: Assign to a slice of a list or string.

    • Subscription: Assign a single element of a structured value.

    • Variable: Assign to a variable.

  • Block: Group statements into a block.

  • Break: End the execution of a while, do or for loop.

  • Continue: Continue with the next iteration of while, do or for loop.

  • Do: Repeat statements while condition holds.

  • Fail: Let the current alternative of a pattern match fail.

  • For: For loop.

  • If: Conditional statement.

  • Insert: Insert a value in a tree during a Visit.

  • Return: Return a value as result of a [Function].

  • Solve: Solve a set of equalities by fixed-point iteration.

  • Switch:

  • Test: Test statement (deprecated).

  • Throw:

  • Try Catch: Try to execute a statement and catch resulting exceptions.

  • Visit:

  • While: While loop.

Statement Types

11. Append

Synopsis

Append an element to the list value produced by various loop statements.

Syntax

append Exp

Description

An append statement may only occur in the body of a While, Do or For statement. It appends the value of Exp to the resulting list value of the loop construct in which it occurs.

Examples
rascal>for(int i <- [1..5]) append i*i;
list[int]: [1,4,9,16]
rascal>L = for(int i <- [1..5]) append i*i;
list[int]: [1,4,9,16]

12. Assert

Synopsis

An executable assertion.

Syntax
  • assert Exp1

  • assert Exp1 : Exp2

Types
Exp1 Exp2

bool

str

Description

An assert statement may occur everywhere where a declaration is allowed. It has two forms:

An assert statement consists of a Boolean expression Exp1 and an optional string expression Exp2 that serves as a identifying message for this assertion.

When Exp1 evaluates to false, an AssertionFailed exception is thrown.

Examples
rascal>assert 1==2 : "is never true";
|prompt:///|(14,15,<1,14>,<1,29>): AssertionFailed("is never true")
	at $shell$(|main://$shell$|)
 
ok
rascal>int div(int x, int y) {
>>>>>>>  assert y != 0 : "y must be non-zero";
>>>>>>>  return x / y;
>>>>>>>}
int (int, int): function(|prompt:///|(0,81,<1,0>,<4,1>))
rascal>div(4,0);
|prompt:///|(42,20,<2,18>,<2,38>): AssertionFailed("y must be non-zero")
	at div(|prompt:///|(0,81,<1,0>,<4,1>))
	at $shell$(|prompt:///|(0,9,<1,0>,<1,9>))
 
ok

13. Assignment

Synopsis

Assign a value to a variable or more complex data structure.

Syntax

Assignable AssignmentOp Exp

where AssignmentOp may be one of =, +=, -=, *=, /=, or ?=.

An Assignable is one of the following:

  • Var

  • Assignable [ Exp ]

  • Assignable [ Exp .. Exp ]

  • Assignable [ Exp, Exp .. Exp ]

  • Assignable . Name

  • < Assignable, Assignable, …​, Assignable >

  • Assignable ? Exp

  • Assignable @ Name

  • Name ( Assignable, Assignable, …​ )

Description

The purpose of an assignment is to assign a new value to a simple variable or to an element of a more complex data structure.

The standard assignment operator is =. The other assignment operators can be expressed as abbreviations for the standard assignment operator.

Assignment Operator Equivalent to

Assignable += Exp

Assignable = Assignable + Exp

Assignable -= Exp

Assignable = Assignable - Exp

Assignable *= Exp

Assignable = Assignable * Exp

Assignable /= Exp

Assignable = Assignable / Exp

Assignable &= Exp

Assignable = Assignable & Exp

Assignable ?= Exp

Assignable = Assignable ? Exp

An assignable is either a single variable, (the base variable), optionally followed by subscriptions, slices or field selections. The assignment statement always results in assigning a completely new value to the base variable. We distinguish the following forms of assignment:

  • Annotation:

  • Constructor: Assign to constructor.

  • Field: Assign to a field of a tuple, relation or datatype.

  • IsDefined: Assign but replace if value is not defined.

  • Multiple: Assign to multiple assignables.

  • Slice: Assign to a slice of a list or string.

  • Subscription: Assign a single element of a structured value.

  • Variable: Assign to a variable.

13.1. Annotation

Synopsis

Assign to an annotation. This feature is deprecated.

Description

The value V of Assignable is determined and should be of a type that has an annotation Name. A new value V' is created that is a copy of V but with the value of annotation Name replaced by the value of Exp. V' is assigned to Assignable. See Annotation Declaration.

Examples

Examples have been removed since this feature is deprecated.

13.2. Constructor

Synopsis

Assign to constructor.

Description

First the value Exp is determined and should be a data value of the form Name(V1, V2, …​, Vn). Next the assignments `Assignablei = Vi are performed for 1 ⇐ i ⇐ n.

Examples
rascal>data FREQ = wf(str word, int freq);
ok
rascal>W = wf("rascal", 1000);
FREQ: wf("rascal",1000)
rascal>wf(S, I) = W;
FREQ: wf("rascal",1000)
rascal>S;
str: "rascal"
rascal>I;
int: 1000

13.3. Field

Synopsis

Assign to a field of a tuple, relation or datatype.

Description

The value V of Assignable is determined and should be of a type that has a field Name. The value of that field is replaced in V by the value of Exp resulting in a new value V' that is assigned to Assignable.

Examples
rascal>data FREQ = wf(str word, int freq);
ok
rascal>W = wf("rascal", 1000);
FREQ: wf("rascal",1000)
rascal>W.freq = 100000;
FREQ: wf("rascal",100000)

13.4. IsDefined

Synopsis

Assign but replace if value is not defined.

Description

First the value of Exp1 is determined and if that is defined it is assigned to Assignable. Otherwise, the value of Exp2 is assigned to Assignable. Values which can be undefined are values in Maps where the key is not set or values of Annotations which have not been set yet.

No other values can be used in an undefined state, so the ? operator does not make sense on undefined or uninitialized variables for example.

Examples
rascal>M = ("Andy": 1, "Brian" : 2);
map[str, int]: ("Andy":1,"Brian":2)

Using an isDefined assignable can we increment a non-existing entry:

rascal>M["SomebodyElse"] ? 0 += 1;
map[str, int]: ("Andy":1,"Brian":2,"SomebodyElse":1)
rascal>M["SomebodyElse"];
int: 1

And if we increment an existing entry the ? has no effect:

rascal>M["Andy"] ? 0 += 1;
map[str, int]: ("Andy":2,"Brian":2,"SomebodyElse":1)
rascal>M["Andy"]
int: 2

13.5. Multiple

Synopsis

Assign to multiple assignables.

Description

First the value Exp is determined and should be a tuple of the form < V1, V2, …​, Vn >. Next the assignments Assignablei = Vi are performed for 1 <= i <= n.

Examples
rascal><A, B, C> = <"abc", 2.5, [1,2,3]>;
tuple[str,real,list[int]]: <"abc",2.5,[1,2,3]>
rascal>A;
str: "abc"
rascal>B;
real: 2.5
rascal>C;
list[int]: [1,2,3]

13.6. Slice

Synopsis

Assign to a slice of a list or string.

Syntax
  • Assignable [ Exp1 .. Exp_3 ] = Exp4

  • Assignable [ Exp1, Exp2 .. Exp3 ] = Exp4

Exp1 and Exp3 are optional .Types

Description

A slice assignment is defined for List, String and Node and aims to replace a slice from the old value of the assignable by a new value. See List Slice, String Slice or Node Slice for a more detailed explanation of slicing.

Let V be the current value of Assignable.

  • Assignable [ Exp1 .. Exp3 ] = Exp4: The slice [ Exp1 .. Exp3 ] determines two indices begin (inclusive) and end (exclusive) in V. A new value V' is computed that is a copy of V but with all the elements in V with begin ⇐ index < end replaced by the elements of the value of Exp4. Note that the size of V and V' may differ. V' is assigned to the Assignable.

  • Assignable [ Exp1, Exp2 .. Exp3 ] = Exp4: The slice [ Exp1, Exp2 .. Exp3 ] determines two indices begin (inclusive) and end (exclusive) and a step between indices in _V. A new value V' is computed that is a copy of V but with all the elements in V with indices begin, begin+step. …​ end-stepindex < end replaced by the successive elements of the value of Exp4. Note that the size of V and V' may differ. V' is assigned to the Assignable. If the number of indices in the slice and the number of elements in the value of Exp4 is not equal the following is done:

    • If the number of elements in the slice is larger: the elements of Exp4 are used in a circular manner.

    • If the number of elements in the slice is smaller: the remaining elements of Exp4 is inserted after the last index in the slice.

Examples

Replace the elements with index 3, 4, 5 in L:

rascal>L = [0,1,2,3,4,5,6,7,8,9];
list[int]: [0,1,2,3,4,5,6,7,8,9]
rascal>L[3..6] = [100,200,300,400,500];
list[int]: [0,1,2,100,200,300,400,500,6,7,8,9]

Replace the elements with index 1, 3, 5, 7 in L (note how the elements from [100,200] are used in a circular way):

rascal>L = [0,1,2,3,4,5,6,7,8,9];
list[int]: [0,1,2,3,4,5,6,7,8,9]
rascal>L[1,3..8] = [100,200];
list[int]: [0,100,2,200,4,100,6,200,8,9]

Replace the elements with index 1, 3, 5, 7 in L (note how the unused elements from [100,200,300,400,500] are insert at index 7):

rascal>L = [0,1,2,3,4,5,6,7,8,9];
list[int]: [0,1,2,3,4,5,6,7,8,9]
rascal>L[1,3..8] = [100,200,300,400,500];
list[int]: [0,100,2,200,4,300,6,400,500,8,9]

Similar examples for slicing assignment on strings:

rascal>S = "abcdefghij";
str: "abcdefghij"
rascal>S[3..6] = "UVWXYZ";
str: "abcUVWXYZghij"
rascal>S = "abcdefghij";
str: "abcdefghij"
rascal>S[1,3..8] = "XY";
str: "aXcYeXgYij"
rascal>S = "abcdefghij";
str: "abcdefghij"
rascal>S[1,3..8] = "UVWXYZ";
str: "aUcVeWgXYZij"

Replace the elements with index 3, 4, 5 in node N:

rascal>N = "f"(0,true,2,"abc",4,5.5,6,{7,77},8,{9,99,999});
node: "f"(
  0,
  true,
  2,
  "abc",
  4,
  5.5,
  6,
  {7,77},
  8,
  {999,9,99})
rascal>N[3..6] = [100,200,300,400,500];
node: "f"(
  0,
  true,
  2,
  100,
  200,
  300,
  400,
  500,
  6,
  {7,77},
  8,
  {999,9,99})

Replace the elements with index 1, 3, 5, 7 in L (note how the elements from [100,200] are used in a circular way):

rascal>N = "f"(0,true,2,"abc",4,5.5,6,{7,77},8,{9,99,999});
node: "f"(
  0,
  true,
  2,
  "abc",
  4,
  5.5,
  6,
  {7,77},
  8,
  {999,9,99})
rascal>N[1,3..8] = [100,200];
node: "f"(
  0,
  100,
  2,
  200,
  4,
  100,
  6,
  200,
  8,
  {999,9,99})

Replace the elements with index 1, 3, 5, 7 in L (note how the unused elements from [100,200,300,400,500] are insert at index 7):

rascal>N = "f"(0,true,2,"abc",4,5.5,6,{7,77},8,{9,99,999});
node: "f"(
  0,
  true,
  2,
  "abc",
  4,
  5.5,
  6,
  {7,77},
  8,
  {999,9,99})
rascal>N[1,3..8] = [100,200,300,400,500];
node: "f"(
  0,
  100,
  2,
  200,
  4,
  300,
  6,
  400,
  500,
  8,
  {999,9,99})

13.7. Subscription

Synopsis

Assign a single element of a structured value.

Description

Let V be the current value of Assignable. The value of Exp1 is used as index in V and the value of Exp2 replaces the original value at that index position. The result is a new value V' that is assigned to the Assignable.

Examples

Assignable has a list value:

rascal>L = [10,20,30];
list[int]: [10,20,30]
rascal>P = L;
list[int]: [10,20,30]
rascal>L[1] = 200;
list[int]: [10,200,30]

Observe that P is unchanged:

rascal>P;
list[int]: [10,20,30]

Assignable has a map value:

rascal>M = ("abc": 1, "def" : 2);
map[str, int]: ("abc":1,"def":2)
rascal>M["def"] = 3;
map[str, int]: ("abc":1,"def":3)

Assignable has a tuple value:

rascal>T = <1, "abc", true>;
tuple[int,str,bool]: <1,"abc",true>
rascal>T[1] = "def";
tuple[int,str,bool]: <1,"def",true>

13.8. Variable

Synopsis

Assign to a variable.

Description

The expression Exp is evaluated and its value is assigned to the variable Var.

Examples
rascal>N = 3;
int: 3
rascal>N;
int: 3

14. Block

Synopsis

Group statements into a block.

Syntax

{ Statement1; …​ ; Statementn }

Description

A block consists of a sequence of statements separated by semi-colons.

Since a block is itself a statement, it may be used in all places where a statement is required. A block also introduces a new scope and variables that are declared in the block are local to that block. The value produced by a block is the value produced by its last statement (if any).

Examples

Here is a contrived block of three expressions (be aware of the last semi-colon):

rascal>{1;2;3;}
int: 3

its value is 3.

The effect of a local variable declared in a block can be seen as follows:

rascal>{int x = 3; x*x;}
int: 9

After the block we cannot refer to x:

rascal>x;
|prompt:///|(0,1,<1,0>,<1,1>): Undeclared variable: x
Advice: |http://tutor.rascal-mpl.org/Errors/Static/UndeclaredVariable/UndeclaredVariable.html|
 
ok

15. Break

Synopsis

End the execution of a while, do or for loop.

Description

A break statement is only allowed inside the body of a While, Do or For statement and is associated with the innermost loop statement in which it is contained. Its effect is to end the execution of the loop.

Also see Continue and Fail.

Examples

Here is an example using break to find the first number divisible by 3:

rascal>import IO;
ok
rascal>for(int i <- [1 .. 10]){
>>>>>>>    if(i % 3 == 0){
>>>>>>>       println("i = <i> is divisible by 3");
>>>>>>>       break;
>>>>>>>    }
>>>>>>>}
i = 3 is divisible by 3
list[void]: []

16. Continue

Synopsis

Continue with the next iteration of while, do or for loop.

Description

A continue statement is only allowed inside the body of a While, Do or For statement and is associated with the innermost loop statement in which it is contained. Its effect is to end the execution of the block for the current iteration of the loop and to continue with the next iteration of the loop.

Also see Break and Fail.

Examples

Here is an example using continue to avoid printing numbers that are divisible by 3:

rascal>import IO;
ok
rascal>for(int i <- [1 .. 10]){
>>>>>>>    if(i % 3 == 0)
>>>>>>>       continue;
>>>>>>>    println("i = <i>");
>>>>>>>}
i = 1
i = 2
i = 4
i = 5
i = 7
i = 8
list[void]: []

17. Do

Synopsis

Repeat statements while condition holds.

Syntax

do Statement while ( Exp );

Description

Statement is executed repeatedly, as long as the Boolean expression Exp yields true. Expression Exp is executed from scratch in each repetition and only the first true value (if any) is used.

By default, the value of a do statement is the empty list. In general, the value of a do statement consists of all values contributed by Append statements that are executed during the repeated execution of its body Statement.

Examples
rascal>import IO;
ok
rascal>int n = 3;
int: 3
rascal>do { println("n = <n>"); n -= 1; } while (n > 0);
n = 3
n = 2
n = 1
list[void]: []

Now build a list result using the append statement:

rascal>n = 3;
int: 3
rascal>do { append n * n; n -= 1; } while (n > 0);
list[int]: [9,4,1]

18. Fail

Synopsis

Let the current alternative of a pattern match fail.

Syntax

fail;

Description

A fail statement is only allowed in statements that are controlled by the outcome of a pattern match:

The fail statement is associated with the innermost pattern match by which it is controlled.

When fail is executed:

  • If the associated pattern has more alternatives, the next alternative is explored,

  • otherwise the pattern as a whole fails.

    • In the case of switch or visit this means that the next case will be tried.

    • For while, do and for, this implies that any bindings caused by the pattern are undone and that the next alternative in the test is tried; otherwise the loop is terminated.

    • For a function call it means that the next function declaration (or the default one) is tried.

Examples

Here is an example taken from Bubble. It uses a fail for the case that no unsorted element can be found in the list of numbers. As a result, the whole case fails and the default case is used.

rascal>import IO;
ok
rascal>public list[int] sort(list[int] numbers){
>>>>>>>  switch(numbers){
>>>>>>>    case [*int nums1, int p, int q, *int nums2]:
>>>>>>>       if(p > q){
>>>>>>>          return sort(nums1 + [q, p] + nums2);
>>>>>>>       } else {
>>>>>>>       	  fail;
>>>>>>>       }
>>>>>>>     default: return numbers;
>>>>>>>   }
>>>>>>>}
list[int] (list[int]): function(|prompt:///|(0,252,<1,0>,<11,1>))
rascal>sort([10, 1, 5, 3]);
list[int]: [1,3,5,10]

19. For

Synopsis

For loop.

Syntax

for ( Exp1 , Exp2 , …​ , Expn ) Statement;

Description

The for-statement executes Statement for all possible combinations of values of the expressions Expi. If one of the expressions is a boolean expression, we do try all its possible values.

By default, the value of a for statement is the empty list. In general, the value of a for statement consists of all values contributed by Append statements that are executed during the repeated execution of its body Statement.

Examples
rascal>import IO;
ok
rascal>for(int n <- [1 .. 5]) println("n = <n>");
n = 1
n = 2
n = 3
n = 4
list[void]: []
rascal>for(int n <- [1 .. 5]) append n * n;
list[int]: [1,4,9,16]

20. If

Synopsis

Conditional statement.

Syntax
  • if ( Exp ) Statement;

  • if ( Exp ) Statement1 else Statement2;

Types
Exp if ( Exp ) Statement;

bool

void

Exp Statement1 Statement2 if ( Exp ) Statement1 else Statement2;

bool

T1

T2

lub(T1, T2)

Description

The test Exp is evaluated and its outcome determines the statement to be executed: Statement1 if Exp yields true and Statement2 otherwise. The value of an if-then statement is equal to Statement when its test is true. Otherwise it is void. The value of an if-then-else statement is the value of the statement that was executed.

Examples
rascal>if( 3 > 2 ) 30; else 40;
int: 30
rascal>x = if( 3 > 2 ) 30; else 40;
int: 30
rascal>if( 3 > 2 ) 30;
int: 30

An if-then statement yields void when its test is false (demonstrated by the ok that is printed by the Rascal system):

rascal>if( 2 > 3 ) 30;
ok

21. Insert

Synopsis

Insert a value in a tree during a Visit.

Syntax

insert Exp;

Description

An insert statement may only occur in the action part of a Pattern With Action, more precisely in a case in a Visit expression. The value matched by the pattern of this case is replaced by the value of Exp.

The following rule applies:

  • The static type of Exp should be a subtype of the type of the value that is replaced.

Examples

Consider the following datatype CTree and assign a CTree value to variable T:

rascal>data CTree = leaf(int n) | red(CTree left, CTree right) | green(CTree left, CTree right);
ok
rascal>CTree T = red(green(leaf(1), red(leaf(2), leaf(3))), red(leaf(4), leaf(5)));
CTree: red(
  green(
    leaf(1),
    red(
      leaf(2),
      leaf(3))),
  red(
    leaf(4),
    leaf(5)))

We can now switch the arguments of all red nodes as follows:

rascal>visit(T){
>>>>>>>  case red(CTree l, CTree r): insert red(r,l);
>>>>>>>}
CTree: red(
  red(
    leaf(5),
    leaf(4)),
  green(
    leaf(1),
    red(
      leaf(3),
      leaf(2))))

Since this is a very common idiom, we also have a shorthand for it:

rascal>visit(T){
>>>>>>>  case red(CTree l, CTree r) => red(r,l)
>>>>>>>}
CTree: red(
  red(
    leaf(5),
    leaf(4)),
  green(
    leaf(1),
    red(
      leaf(3),
      leaf(2))))
Pitfalls

There is a glitch in the Rascal syntax that requires a semicolon after a case (as in the first example), but refuses it in the abbreviated version using (the second example).

22. Return

Synopsis

Return a value as result of a [Function].

Syntax
  • return;

  • return Exp

Description

A return statement comes in two variants: without and with an expression, both variants end the execution of the current function. The first variant applies to functions with void as return type. The second variants applies to non-void functions and returns the value of Exp as result of the function invocation. The following rules apply:

  • The static type of Exp should be compatible with the declared return type of the function in which the return statement occurs.

  • In each function with a return type that is not void, every possible execution path through the body of the function should end in a return statement.

In each function with a return type that is void, a return statement is implicitly assumed at the end of each execution path through the function body.

Examples
rascal>int twice(int n) { return 2 * n; }
int (int): function(|prompt:///|(0,34,<1,0>,<1,34>))
rascal>twice(5);
int: 10

Functions that only return a value can be abbreviated (and the return is implicit):

rascal>int twiceb(int n) = 2 * n;
int (int): function(|prompt:///|(0,26,<1,0>,<1,26>))
rascal>twiceb(5);
int: 10

23. Solve

Synopsis

Solve a set of equalities by fixed-point iteration.

Syntax

solve(Var1, Var2, …​, Varn; Exp) Statement;

Description

Rascal provides a solve statement for performing arbitrary fixed-point computations. This means, repeating a certain computation as long as it causes changes. This can, for instance, be used for the solution of sets of simultaneous linear equations but has much wider applicability.

The solve statement consists of the variables for which a fixed point will be computed and a statement. Optionally, an expression Exp directly following the list of variables gives an upper bound on the number of iterations.

Statement can use and modify the listed variables Vari. The statement is executed, assigning new values to the variables Vari, and this is repeated as long as the value of any of the variables was changed compared to the previous repetition. Note that this computation will only terminate if the variables range over a so-called bounded monotonic lattice, in which values can only become larger until a fixed upper bound or become smaller until a fixed lower bound.

Examples

Let’s consider transitive closure as an example (transitive closure is already available as built-in operator, we use it here just as a simple illustration). Transitive closure of a relation is usually defined as:

R+ = R + (R o R) + (R o R o R) + ...

In other words, it is the union of successive Relation Compositions of R with itself. For a given relation R this can be expressed as follows:

rascal>rel[int,int] R = {<1,2>, <2,3>, <3,4>};
rel[int,int]: {
  <1,2>,
  <3,4>,
  <2,3>
}
rascal>T = R;
rel[int,int]: {
  <1,2>,
  <3,4>,
  <2,3>
}
rascal>solve (T) {
>>>>>>>          T = T + (T o R);
>>>>>>>        }
rel[int,int]: {
  <3,4>,
  <1,3>,
  <1,2>,
  <1,4>,
  <2,3>,
  <2,4>
}

24. Switch

Synopsis

The switch statement is a control flow statement where the next block is selected by pattern matching against a number of case patterns.

Syntax
switch ( Exp ) {
case PatternWithAction1;
case PatternWithAction2;
...
default: ...
}
Description

A switch statement is similar to a switch statement in C or Java. The value of the expression Exp is the subject term that will be matched by the successive Pattern With Actions in the switch statement. The switch statement provides only matching at the top level of the subject term and does not traverse it. The type of the pattern in each case must be identical to the type of the subject term (or be a supertype of it). If no case matches, the switch acts as a dummy statement. There is no fall through from one case to the next.

Examples

Suppose we want to naively analyze a sentence and print the topic it is about:

rascal>import IO;
ok
rascal>S = "Princess Leila sipped from her rum punch";
str: "Princess Leila sipped from her rum punch"
rascal>switch(S){
>>>>>>>  case /Leila/: println("The topic is Star Wars");
>>>>>>>  case /rum/:   println("The topic is Drunken man");
>>>>>>>  case /punch/: println("The topic is Kick Boxing");
>>>>>>>}
The topic is Star Wars
ok

From the printed message you can infer that the cases are tried in the order in which they occur.

Pitfalls

The switch statement does not yet return a value, this will be changed.

25. Test

Synopsis

Test statement (deprecated).

Description

The test statement is deprecated and is replaced by the test modifier in function declarations, see Function Declaration.

26. Throw

Synopsis

Throw any value as an exception up the call stack.

Syntax

throw Exp

Description

A throw statement causes the immediate abortion of the execution of the current function with Exp \'s value as exception value. The exception can be caught by a Try Catch statement in the current function or in one of its callers. If the exception is not caught, the execution of the Rascal program is terminated. The following rules apply:

  • The static type of Exp should be RuntimeException, see RuntimeException.

  • The Rascal program may contain data declarations that extend the type RuntimeException.

Examples

Here is a a variant of string concatenation for ball haters:

rascal>str conc(str x, str y){ if("ball" in {x, y}) throw "I hate balls"; return x + y; }
str (str, str): function(|prompt:///|(0,82,<1,0>,<1,82>))
rascal>conc("fairy", "tale");
str: "fairytale"
rascal>conc("foot", "ball");
|prompt:///|(51,14,<1,51>,<1,65>): "I hate balls"
	at conc(|prompt:///|(24,43,<1,24>,<1,67>))
	at $shell$(|prompt:///|(0,21,<1,0>,<1,21>))
 
ok

27. Try Catch

Synopsis

Try to execute a statement and catch resulting exceptions.

Syntax
try
   Statement1;
catch PatternWithAction1;
catch PatternWithAction2;
...
catch: Statement2;
finally: Statement3;
Description

A try catch statement has as purpose to catch any Exceptions that are raised during the execution of Statement1. These exceptions may caused by:

  • The execution of an explicit Throw statement.

  • The Rascal system that discovers an abnormal condition, e.g., an out of bounds error when accessing a list element.

Note that all elements of the try catch statement are optional but that at least one has to be present. Their meaning is as follows:

  • If a pattern of some PatternWithActioni matches, the corresponding action is executed.

  • Otherwise, Statement2 is executed (when present).

  • Before leaving the try catch statement Statement3 is always executed (when present).

Examples

Let’s define a variant of the head function that returns the first element of a list, but throws an exception when the list is empty. Our variant will return 0 for an empty list:

rascal>import List;
ok
rascal>import Exception;
ok
rascal>int hd(list[int] x) { try return head(x); catch: return 0; }
int (list[int]): function(|prompt:///|(0,60,<1,0>,<1,60>))
rascal>hd([1,2,3]);
int: 1
rascal>hd([]);
int: 0

We can also be more specific and catch the EmptyList exception (which is available here since we have imported the Exception module):

rascal>int hd2(list[int] x) { try return head(x); catch EmptyList(): return 0; }
int (list[int]): function(|prompt:///|(0,73,<1,0>,<1,73>))
rascal>hd2([]);
int: 0

28. Visit

Synopsis

The Visit expression can also be used directly as a statement

Syntax

See [Expression-Visit].

Description

See Visit for the details.

Examples
rascal>x = [[1],[2],[3]];
list[list[int]]: [
  [1],
  [2],
  [3]
]
rascal>if (true) {

this visit is a nested statement in an if block:

>>>>>>>  visit (x) {
>>>>>>>    case int i => i + 1
>>>>>>>  }
>>>>>>>}
list[list[int]]: [
  [2],
  [3],
  [4]
]

29. While

Synopsis

While loop.

Syntax

while ( Exp ) Statement;

Description

The Boolean expression Exp is evaluated repeatedly and Statement is executed when the test is true. Execution ends the first time that the test yields false. The test Exp is executed from scratch in each repetition and only the first true value (if any) is used. This is relevant when Exp contains a Boolean Match or Boolean NoMatch operator.

By default, the value of a while statement is the empty list. In general, the value of a while statement consists of all values contributed by Append statements that are executed during the repeated execution of its body Statement.

Examples
rascal>import IO;
ok
rascal>int n = 3;
int: 3
rascal>while( n > 0 ) { println("n = <n>"); n -= 1; }
n = 3
n = 2
n = 1
list[void]: []

Now build a list result using the append statement:

rascal>n = 3;
int: 3
rascal>while (n > 0) { append n * n; n -= 1; }
list[int]: [9,4,1]

Just to be sure, a List Comprehension is the superior way to write this:

rascal>[n * n | n <- [3 .. 1]];
list[int]: [9,4]