diff --git a/src/org/rascalmpl/library/IO.rsc b/src/org/rascalmpl/library/IO.rsc index 5e3255b4f67..e37a598ae2d 100644 --- a/src/org/rascalmpl/library/IO.rsc +++ b/src/org/rascalmpl/library/IO.rsc @@ -140,8 +140,9 @@ Append a textual representation of some values to an existing or a newly created * All other values are printed as-is. * Each value is terminated by a newline character. -The existing file can be stored using any character set possible, if you know the character set, please use ((appendToFileEnc)). -Else the same method of deciding the character set is used as in ((readFile)). +The existing file can be stored using any character set possible. +If you know the character set, please use the `charset` keyword parameter. +Otherwise, the same method of deciding the character set is used as in ((readFile)). } @pitfalls{ * The same encoding pitfalls as the ((readFile)) function. @@ -239,6 +240,8 @@ public java bool isDirectory(loc file); See ((IO-iprintExp)) for a version that returns its argument as result and ((IO-iprintln)) for a version that adds a newline and ((IO-iprintToFile)) for a version that prints to a file. + +With a negative `lineLimit` the limit is ignored and the entire value will be printed. } @examples{ ```rascal-shell @@ -254,6 +257,7 @@ public java void iprint(value arg, int lineLimit = 1000); See ((IO-iprint)) for a version that displays the result on the console and ((IO-iprintExp)) for a version that returns its argument as result and ((IO-iprintln)) for a version that adds a newline. + } @examples{ ```rascal-shell @@ -308,6 +312,8 @@ and ((IO-iprint)) for a version that does not add a newline. By default we only print the first 1000 lines, if you want to print larger values, either use ((ValueIO-writeTextValueFile)) or change the limit with the lineLimit parameter. + +With a negative `lineLimit` the limit is ignored and the entire value will be printed. } @examples{ ```rascal-shell diff --git a/src/org/rascalmpl/library/List.rsc b/src/org/rascalmpl/library/List.rsc index aa7be84cc1c..6a69285dca9 100644 --- a/src/org/rascalmpl/library/List.rsc +++ b/src/org/rascalmpl/library/List.rsc @@ -19,7 +19,7 @@ module List import Exception; import Map; - +import String; @synopsis{Concatenate a list of lists.} @examples{ @@ -259,8 +259,7 @@ intercalate(", ", ["zebra", "elephant", "snake", "owl"]); ``` } str intercalate(str sep, list[value] l) = - "<}>"; - + "<}>"[..-size(sep)]; @synopsis{Intersperses a list of values with a separator.} @examples{ @@ -272,9 +271,8 @@ intersperse(1, []); intersperse([], [1]); ``` } -list[&T] intersperse(&T sep, list[&T] xs) = - (isEmpty(xs))? [] : ([head(xs)] | it + [sep,x] | x <- tail(xs)); - +list[&T] intersperse(&T sep, list[&T] xs) = + [x, sep | &T x <- xs][..-1]; @synopsis{Test whether a list is empty.} @description{ @@ -655,6 +653,29 @@ tuple[list[&T],list[&T]] split(list[&T] l) { return ; } +@synopsis{Groups sublists for consecutive elements which are `similar`} +@description{ +This function does not change the order of the elements. Only elements +which are similar end-up in a sub-list with more than one element. The +elements which are not similar to their siblings, end up in singleton +lists. +} +@examples{ +```rascal-shell +import List; +bool bothEvenOrBothOdd(int a, int b) = (a % 2 == 0 && b % 2 == 0) || (a % 2 == 1 && b % 2 == 1); +group([1,7,3,6,2,9], bothEvenOrBothOdd); +``` +} +public list[list[&T]] group(list[&T] input, bool (&T a, &T b) similar) { + lres = while ([hd, *tl] := input) { + sim = [hd, *takeWhile(tl, bool (&T a) { return similar(a, hd); })]; + append sim; + input = drop(size(sim), input); + } + + return lres; +} @synopsis{Sum the elements of a list.} @examples{ diff --git a/src/org/rascalmpl/library/ParseTree.rsc b/src/org/rascalmpl/library/ParseTree.rsc index e1e49aa00de..2ffabf48413 100644 --- a/src/org/rascalmpl/library/ParseTree.rsc +++ b/src/org/rascalmpl/library/ParseTree.rsc @@ -10,7 +10,6 @@ @contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} @contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} @contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} - @synopsis{Library functions for parse trees.} @description{ A _concrete syntax tree_ or [parse tree](http://en.wikipedia.org/wiki/Parse_tree) is an ordered, rooted tree that @@ -139,7 +138,6 @@ for the type of source locations. Therefore the annotation name has to be escape * We are in transition from deprecating the annotation `@\loc` with the keyword field `.src=|unknown:///|`. Currently the run-time already uses `.src` while the source code still uses `@\loc`. } - module ParseTree extend List; @@ -158,14 +156,17 @@ A `Tree` defines the trees normally found after parsing; additional constructors <3> Ambiguous subtree. <4> A single character. } - -data Tree(loc parseError = |unknown:///|(0,0,<0,0>,<0,0>)) //loc src = |unknown:///|(0,0,<0,0>,<0,0>) - = appl(Production prod, list[Tree] args) // <1> - | cycle(Symbol symbol, int cycleLength) // <2> - | amb(set[Tree] alternatives) // <3> - | char(int character) // <4> - ; - +data Tree (loc parseError = |unknown:///|(0, 0, <0, 0>, <0, 0>)) + //loc src = |unknown:///|(0,0,<0,0>,<0,0>) + = appl(Production prod, list[Tree] args) + // <1> + | cycle(Symbol symbol, int cycleLength) + // <2> + | amb(set[Tree] alternatives) + // <3> + | char(int character) + // <4> + ; @synopsis{Production in ParseTrees} @description{ @@ -184,21 +185,28 @@ construct ordered and un-ordered compositions, and associativity groups. for extending priority chains and such. <6> `error` means a node produced by error recovery. <7> `skipped` means characters skipped during error recovery, always the last child of an `appl` with a `error` production. -} -data Production - = prod(Symbol def, list[Symbol] symbols, set[Attr] attributes) // <1> - | regular(Symbol def) // <2> - ; - -data Production - = \priority(Symbol def, list[Production] choices) // <3> - | \associativity(Symbol def, Associativity \assoc, set[Production] alternatives) // <4> - | \reference(Symbol def, str cons) // <5> - ; +} +data Production + = prod(Symbol def, list[Symbol] symbols, set[Attr] attributes) + // <1> + | regular(Symbol def) + // <2> + ; data Production - = \error(Symbol def, Production prod, int dot) - | \skipped(Symbol def); + = \priority(Symbol def, list[Production] choices) + // <3> + | \associativity( + Symbol def, Associativity \assoc, set[Production] alternatives) + // <4> + | \reference(Symbol def, str cons) + // <5> + ; + +data Production + = \error(Symbol def, Production prod, int dot) + | \skipped(Symbol def) + ; @synopsis{A special exception that wraps errors that are (almost) certainly caused by unexpected parse errors} @description{ @@ -215,7 +223,6 @@ using try/catch you can make a language processor robust against (deeply nested) @pitfalls{ it is advised to try/catch these exception high up in the call graph of your language processor, otherwise you'll have to write try/catch in many different places } - data RuntimeException = ParseErrorRecovery(RuntimeException trigger, loc src); @synopsis{Attributes in productions.} @@ -224,24 +231,22 @@ An `Attr` (attribute) documents additional semantics of a production rule. Neith brackets are processed by the parser generator. Rather downstream processors are activated by these. Associativity is a parser generator feature though. } -data Attr - = \bracket() - | \assoc(Associativity \assoc) - ; - +data Attr + = \bracket() + | \assoc(Associativity \assoc) + ; @synopsis{Associativity attribute.} @description{ Associativity defines the various kinds of associativity of a specific production. -} -data Associativity +} +data Associativity = \left() - | \right() - | \assoc() + | \right() + | \assoc() | \non-assoc() ; - @synopsis{Character ranges and character class} @description{ * `CharRange` defines a range of characters. @@ -251,7 +256,6 @@ data CharRange = range(int begin, int end); alias CharClass = list[CharRange]; - @synopsis{Symbols that can occur in a ParseTree} @description{ The type `Symbol` is introduced in ((Library:module:Type)), see ((Type-Symbol)), to represent the basic Rascal types, @@ -279,43 +283,59 @@ e.g., `int`, `list`, and `rel`. Here we extend it with the symbols that may occu <19> Conditional occurrence of a symbol. } data Symbol // <1> - = \start(Symbol symbol); + = \start(Symbol symbol); // These symbols are the named non-terminals. -data Symbol - = \sort(str name) // <2> - | \lex(str name) // <3> - | \layouts(str name) // <4> - | \keywords(str name) // <5> - | \parameterized-sort(str name, list[Symbol] parameters) // <6> - | \parameterized-lex(str name, list[Symbol] parameters) // <7> - ; +data Symbol + = \sort(str name) + // <2> + | \lex(str name) + // <3> + | \layouts(str name) + // <4> + | \keywords(str name) + // <5> + | \parameterized-sort(str name, list[Symbol] parameters) + // <6> + | \parameterized-lex(str name, list[Symbol] parameters) + // <7> + ; // These are the terminal symbols. -data Symbol - = \lit(str string) // <8> - | \cilit(str string) // <9> - | \char-class(list[CharRange] ranges) // <10> - ; - +data Symbol + = \lit(str string) + // <8> + | \cilit(str string) + // <9> + | \char-class(list[CharRange] ranges) + // <10> + ; + // These are the regular expressions. data Symbol - = \empty() // <11> - | \opt(Symbol symbol) // <12> - | \iter(Symbol symbol) // <13> - | \iter-star(Symbol symbol) // <14> - | \iter-seps(Symbol symbol, list[Symbol] separators) // <15> - | \iter-star-seps(Symbol symbol, list[Symbol] separators) // <16> - | \alt(set[Symbol] alternatives) // <17> - | \seq(list[Symbol] symbols) // <18> - ; - + = \empty() + // <11> + | \opt(Symbol symbol) + // <12> + | \iter(Symbol symbol) + // <13> + | \iter-star(Symbol symbol) + // <14> + | \iter-seps(Symbol symbol, list[Symbol] separators) + // <15> + | \iter-star-seps(Symbol symbol, list[Symbol] separators) + // <16> + | \alt(set[Symbol] alternatives) + // <17> + | \seq(list[Symbol] symbols) + // <18> + ; + data Symbol // <19> - = \conditional(Symbol symbol, set[Condition] conditions); + = \conditional(Symbol symbol, set[Condition] conditions); bool subtype(Symbol::\sort(_), Symbol::\adt("Tree", _)) = true; - @synopsis{Datatype for declaring preconditions and postconditions on symbols} @description{ A `Condition` can be attached to a symbol; it restricts the applicability @@ -324,22 +344,23 @@ is followed by another symbol and `at-column` requires that it occurs at a certain position in the current line of the input text. } data Condition - = \follow(Symbol symbol) - | \not-follow(Symbol symbol) - | \precede(Symbol symbol) - | \not-precede(Symbol symbol) - | \delete(Symbol symbol) - | \at-column(int column) - | \begin-of-line() - | \end-of-line() - | \except(str label) - ; - + = \follow(Symbol symbol) + | \not-follow(Symbol symbol) + | \precede(Symbol symbol) + | \not-precede(Symbol symbol) + | \delete(Symbol symbol) + | \at-column(int column) + | \begin-of-line() + | \end-of-line() + | \except(str label) + ; @synopsis{Nested priority is flattened.} -Production priority(Symbol s, [*Production a, priority(Symbol _, list[Production] b), *Production c]) - = priority(s,a+b+c); - +Production priority( + Symbol s, + [*Production a, priority(Symbol _, list[Production] b), *Production c] +) + = priority(s, a + b + c); @synopsis{Normalization of associativity.} @description{ @@ -347,15 +368,24 @@ Production priority(Symbol s, [*Production a, priority(Symbol _, list[Production * Nested (equal) associativity is flattened. * ((ParseTree-priority)) under an associativity group defaults to choice. } -Production associativity(Symbol s, Associativity as, {*Production a, choice(Symbol t, set[Production] b)}) - = associativity(s, as, a+b); - -Production associativity(Symbol rhs, Associativity a, {associativity(rhs, Associativity b, set[Production] alts), *Production rest}) - = associativity(rhs, a, rest + alts); // the nested associativity, even if contradictory, is lost - -Production associativity(Symbol s, Associativity as, {*Production a, priority(Symbol t, list[Production] b)}) - = associativity(s, as, {*a, *b}); - +Production associativity( + Symbol s, Associativity as, + {*Production a, choice(Symbol t, set[Production] b)} +) + = associativity(s, as, a + b); + +Production associativity( + Symbol rhs, Associativity a, + {associativity(rhs, Associativity b, set[Production] alts), *Production rest} +) + = associativity(rhs, a, rest + alts); + +// the nested associativity, even if contradictory, is lost +Production associativity( + Symbol s, Associativity as, + {*Production a, priority(Symbol t, list[Production] b)} +) + = associativity(s, as, {*a, *b}); @synopsis{Annotate a parse tree node with a source location.} @description{ @@ -401,8 +431,12 @@ ignores @\loc annotations and whitespace and comments. * Annotated trees are strictly too big for optimal memory usage. Often `@\loc` is the first and only annotation, so it introduces a map for keyword parameters for every node. Also more nodes are different, impeding in optimal reference sharing. If you require long time storage of many parse trees it may be useful to strip them of annotations for selected categories of nodes, using ((reposition)). -} -anno loc Tree@\loc; +} anno + loc + Tree + @ + \loc +; @synopsis{Parse input text (from a string or a location) and return a parse tree.} @description{ @@ -477,16 +511,81 @@ catch ParseError(loc l): { } ``` } - -&T<:Tree parse(type[&T<:Tree] begin, str input, bool allowAmbiguity=false, int maxAmbDepth=2, bool allowRecovery=false, int maxRecoveryAttempts=30, int maxRecoveryTokens=3, bool hasSideEffects=false, set[Tree(Tree)] filters={}) - = parser(begin, allowAmbiguity=allowAmbiguity, maxAmbDepth=maxAmbDepth, allowRecovery=allowRecovery, maxRecoveryAttempts=maxRecoveryAttempts, maxRecoveryTokens=maxRecoveryTokens, hasSideEffects=hasSideEffects, filters=filters)(input, |unknown:///|); - -&T<:Tree parse(type[&T<:Tree] begin, str input, loc origin, bool allowAmbiguity=false, int maxAmbDepth=2, bool allowRecovery=false, int maxRecoveryAttempts=30, int maxRecoveryTokens=3, bool hasSideEffects=false, set[Tree(Tree)] filters={}) - = parser(begin, allowAmbiguity=allowAmbiguity, maxAmbDepth=maxAmbDepth, allowRecovery=allowRecovery, maxRecoveryAttempts=maxRecoveryAttempts, maxRecoveryTokens=maxRecoveryTokens, hasSideEffects=hasSideEffects, filters=filters)(input, origin); - -&T<:Tree parse(type[&T<:Tree] begin, loc input, bool allowAmbiguity=false, int maxAmbDepth=2, bool allowRecovery=false, int maxRecoveryAttempts=30, int maxRecoveryTokens=3, bool hasSideEffects=false, set[Tree(Tree)] filters={}) - = parser(begin, allowAmbiguity=allowAmbiguity, maxAmbDepth=maxAmbDepth, allowRecovery=allowRecovery, maxRecoveryAttempts=maxRecoveryAttempts, maxRecoveryTokens=maxRecoveryTokens, hasSideEffects=hasSideEffects, filters=filters)(input, input); - +&T <: Tree parse( + type[&T <: Tree] begin, + str input, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +) + = parser( + begin, + allowAmbiguity = allowAmbiguity, + maxAmbDepth = maxAmbDepth, + allowRecovery = allowRecovery, + maxRecoveryAttempts = maxRecoveryAttempts, + maxRecoveryTokens = maxRecoveryTokens, + hasSideEffects = hasSideEffects, + filters = filters + )( + input, + |unknown:///| + ); + +&T <: Tree parse( + type[&T <: Tree] begin, + str input, + loc origin, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +) + = parser( + begin, + allowAmbiguity = allowAmbiguity, + maxAmbDepth = maxAmbDepth, + allowRecovery = allowRecovery, + maxRecoveryAttempts = maxRecoveryAttempts, + maxRecoveryTokens = maxRecoveryTokens, + hasSideEffects = hasSideEffects, + filters = filters + )( + input, + origin + ); + +&T <: Tree parse( + type[&T <: Tree] begin, + loc input, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +) + = parser( + begin, + allowAmbiguity = allowAmbiguity, + maxAmbDepth = maxAmbDepth, + allowRecovery = allowRecovery, + maxRecoveryAttempts = maxRecoveryAttempts, + maxRecoveryTokens = maxRecoveryTokens, + hasSideEffects = hasSideEffects, + filters = filters + )( + input, + input + ); @synopsis{Generates a parser from an input grammar.} @description{ @@ -519,7 +618,16 @@ The parse function behaves differently depending of the given keyword parameters interpreted environment to make side effects (like a symbol table) and it can share more intermediate results as a result. } @javaClass{org.rascalmpl.library.Prelude} -java &T (value input, loc origin) parser(type[&T] grammar, bool allowAmbiguity=false, int maxAmbDepth=2, bool allowRecovery=false, int maxRecoveryAttempts=30, int maxRecoveryTokens=3, bool hasSideEffects=false, set[Tree(Tree)] filters={}); +java &T(value input, loc origin) parser( + type[&T] grammar, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); @javaClass{org.rascalmpl.library.Prelude} @synopsis{Generates a parser function that can be used to find the left-most deepest ambiguous sub-sentence.} @@ -532,7 +640,11 @@ the tree that exhibits ambiguity. This can be done very quickly, while the whole * The returned sub-tree usually has a different type than the parameter of the type[] symbol that was passed in. The reason is that sub-trees typically have a different non-terminal than the start non-terminal of a grammar. } -java Tree (value input, loc origin) firstAmbiguityFinder(type[Tree] grammar, bool hasSideEffects=false, set[Tree(Tree)] filters={}); +java Tree(value input, loc origin) firstAmbiguityFinder( + type[Tree] grammar, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); @synopsis{Generates parsers from a grammar (reified type), where all non-terminals in the grammar can be used as start-symbol.} @description{ @@ -540,7 +652,16 @@ This parser generator behaves the same as the `parser` function, but it produces nonterminal parameter. This can be used to select a specific non-terminal from the grammar to use as start-symbol for parsing. } @javaClass{org.rascalmpl.library.Prelude} -java &U (type[&U] nonterminal, value input, loc origin) parsers(type[&T] grammar, bool allowAmbiguity=false, int maxAmbDepth=2, bool allowRecovery=false, int maxRecoveryAttempts=30, int maxRecoveryTokens=3, bool hasSideEffects=false, set[Tree(Tree)] filters={}); +java &U(type[&U] nonterminal, value input, loc origin) parsers( + type[&T] grammar, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); @javaClass{org.rascalmpl.library.Prelude} @synopsis{Generates a parser function that can be used to find the left-most deepest ambiguous sub-sentence.} @@ -553,7 +674,11 @@ the tree that exhibits ambiguity. This can be done very quickly, while the whole * The returned sub-tree usually has a different type than the parameter of the type[] symbol that was passed in. The reason is that sub-trees typically have a different non-terminal than the start non-terminal of a grammar. } -java Tree (type[Tree] nonterminal, value input, loc origin) firstAmbiguityFinders(type[Tree] grammar, bool hasSideEffects=false, set[Tree(Tree)] filters={}); +java Tree(type[Tree] nonterminal, value input, loc origin) firstAmbiguityFinders( + type[Tree] grammar, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); @synopsis{Parse the input but instead of returning the entire tree, return the trees for the first ambiguous substring.} @description{ @@ -564,10 +689,10 @@ the cost of constructing nested ambiguity clusters. If the input sentence is not ambiguous after all, simply the entire tree is returned. } Tree firstAmbiguity(type[Tree] begin, str input) - = firstAmbiguityFinder(begin)(input, |unknown:///|); + = firstAmbiguityFinder(begin)(input, |unknown:///|); Tree firstAmbiguity(type[Tree] begin, loc input) - = firstAmbiguityFinder(begin)(input, input); + = firstAmbiguityFinder(begin)(input, input); @javaClass{org.rascalmpl.library.Prelude} @synopsis{Generate a parser and store it in serialized form for later reuse.} @@ -631,7 +756,16 @@ p(type(sort("E"), ()), "e+e", |src:///|); * reifiying types (use of `#`) will trigger the loading of a parser generator anyway. You have to use this notation for types to avoid that: `type(\start(sort("MySort")), ())` to avoid the computation for `#start[A]` } -java &U (type[&U] nonterminal, value input, loc origin) loadParsers(loc savedParsers, bool allowAmbiguity=false, int maxAmbDepth=2, bool allowRecovery=false, int maxRecoveryAttempts=30, int maxRecoveryTokens=3, bool hasSideEffects=false, set[Tree(Tree)] filters={}); +java &U(type[&U] nonterminal, value input, loc origin) loadParsers( + loc savedParsers, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); @synopsis{Load a previously serialized parser, for a specific non-terminal, from disk for usage} @description{ @@ -639,7 +773,17 @@ This loader behaves just like ((loadParsers)), except that the resulting parser bound to a specific non-terminal. } @javaClass{org.rascalmpl.library.Prelude} -java &U (value input, loc origin) loadParser(type[&U] nonterminal, loc savedParsers, bool allowAmbiguity=false, int maxAmbDepth=2, bool allowRecovery=false, int maxRecoveryAttempts=30, int maxRecoveryTokens=3, bool hasSideEffects=false, set[Tree(Tree)] filters={}); +java &U(value input, loc origin) loadParser( + type[&U] nonterminal, + loc savedParsers, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); @synopsis{Yield the string of characters that form the leafs of the given parse tree.} @description{ @@ -666,7 +810,6 @@ str unparse(Tree tree) = ""; java str printSymbol(Symbol sym, bool withLayout); @javaClass{org.rascalmpl.library.Prelude} - @synopsis{Implode a parse tree according to a given (ADT) type.} @description{ Given a grammar for a language, its sentences can be parsed and the result is a parse tree @@ -785,47 +928,57 @@ Can be imploded into: data Exp = add(Exp, Exp); ``` } -java &T<:value implode(type[&T<:value] t, Tree tree); +java &T <: value implode(type[&T <: value] t, Tree tree); @synopsis{Tree search result type for ((treeAt)).} -data TreeSearchResult[&T<:Tree] = treeFound(&T tree) | treeNotFound(); - - +data TreeSearchResult[&T <: Tree] + = treeFound(&T tree) + | treeNotFound() + ; @synopsis{Select the innermost Tree of a given type which is enclosed by a given location.} @description{ } -TreeSearchResult[&T<:Tree] treeAt(type[&T<:Tree] t, loc l, Tree a:appl(_, _)) { - if ((a@\loc)?, al := a@\loc, al.offset <= l.offset, al.offset + al.length >= l.offset + l.length) { - for (arg <- a.args, TreeSearchResult[&T<:Tree] r:treeFound(&T<:Tree _) := treeAt(t, l, arg)) { - return r; - } - - if (&T<:Tree tree := a) { - return treeFound(tree); - } - } - return treeNotFound(); +TreeSearchResult[&T <: Tree] treeAt( + type[&T <: Tree] t, loc l, Tree a: appl(_, _) +) { + if ((a@\loc)?, al := a@\loc, al.offset <= l.offset, al.offset + al.length >= l.offset + l.length) + { + for (arg <- a.args, TreeSearchResult[&T <: Tree] r: treeFound(&T <: Tree _) := treeAt(t, l, arg)) { + return r; + } + + if (&T <: Tree tree := a) { + return treeFound(tree); + } + } + return treeNotFound(); } -default TreeSearchResult[&T<:Tree] treeAt(type[&T<:Tree] t, loc l, Tree root) = treeNotFound(); +default TreeSearchResult[&T <: Tree] treeAt( + type[&T <: Tree] t, loc l, Tree root +) + = treeNotFound(); -bool sameType(label(_,Symbol s),Symbol t) = sameType(s,t); -bool sameType(Symbol s,label(_,Symbol t)) = sameType(s,t); -bool sameType(Symbol s,conditional(Symbol t,_)) = sameType(s,t); -bool sameType(conditional(Symbol s,_), Symbol t) = sameType(s,t); +bool sameType(label(_, Symbol s), Symbol t) = sameType(s, t); +bool sameType(Symbol s, label(_, Symbol t)) = sameType(s, t); +bool sameType(Symbol s, conditional(Symbol t, _)) = sameType(s, t); +bool sameType(conditional(Symbol s, _), Symbol t) = sameType(s, t); bool sameType(Symbol s, s) = true; default bool sameType(Symbol s, Symbol t) = false; - @synopsis{Determine if the given type is a non-terminal type.} bool isNonTerminalType(Symbol::\sort(str _)) = true; bool isNonTerminalType(Symbol::\lex(str _)) = true; bool isNonTerminalType(Symbol::\layouts(str _)) = true; bool isNonTerminalType(Symbol::\keywords(str _)) = true; -bool isNonTerminalType(Symbol::\parameterized-sort(str _, list[Symbol] _)) = true; -bool isNonTerminalType(Symbol::\parameterized-lex(str _, list[Symbol] _)) = true; +bool isNonTerminalType( + Symbol::\parameterized-sort(str _, list[Symbol] _) +) + = true; +bool isNonTerminalType(Symbol::\parameterized-lex(str _, list[Symbol] _)) + = true; bool isNonTerminalType(Symbol::\start(Symbol s)) = isNonTerminalType(s); default bool isNonTerminalType(Symbol s) = false; @@ -877,119 +1030,129 @@ yield of a tree should always produce the exact same locations as ((reposition)) * The default mark options simulate the behavior of ((parser)) functions. } &T <: Tree reposition( - &T <: Tree tree, - loc file = tree@\loc.top, - bool \markStart = true, - bool \markSyntax = true, - bool \markLexical = true, - bool \markSubLexical = true, - bool \markRegular = true, - bool \markLayout = true, - bool \markSubLayout = true, - bool \markLit = false, - bool \markSubLit = false, - bool \markAmb = false, - bool \markCycle = false, - bool \markChar = false - ) { + &T <: Tree tree, + loc file = tree@\loc.top, + bool \markStart = true, + bool \markSyntax = true, + bool \markLexical = true, + bool \markSubLexical = true, + bool \markRegular = true, + bool \markLayout = true, + bool \markSubLayout = true, + bool \markLit = false, + bool \markSubLit = false, + bool \markAmb = false, + bool \markCycle = false, + bool \markChar = false +) { // the cur variables are shared state by the `rec` local function that recurses over the entire tree int curOffset = 0; int curLine = 1; int curColumn = 0; - + @synopsis{Check if this rule is configured to be annotated} - default bool doAnno(Production _) = false; - bool doAnno(prod(\lex(_), _, _)) = markLexical; - bool doAnno(prod(\label(_, \lex(_)), _, _)) = markLexical; - bool doAnno(prod(\parameterized-lex(_, _), _, _)) = markLexical; + default bool doAnno(Production _) = false; + bool doAnno(prod(\lex(_), _, _)) = markLexical; + bool doAnno(prod(\label(_, \lex(_)), _, _)) = markLexical; + bool doAnno(prod(\parameterized-lex(_, _), _, _)) = markLexical; bool doAnno(prod(\label(_, \parameterized-lex(_, _)), _, _)) = markLexical; - bool doAnno(prod(\layouts(_), _, _)) = markLayout; + bool doAnno(prod(\layouts(_), _, _)) = markLayout; bool doAnno(prod(\label(_, \layouts(_)), _, _)) = markLayout; - bool doAnno(prod(\sort(_), _, _)) = markSyntax; - bool doAnno(prod(\label(_, \sort(_)), _, _)) = markSyntax; - bool doAnno(prod(\parameterized-sort(_, _), _, _)) = markSyntax; + bool doAnno(prod(\sort(_), _, _)) = markSyntax; + bool doAnno(prod(\label(_, \sort(_)), _, _)) = markSyntax; + bool doAnno(prod(\parameterized-sort(_, _), _, _)) = markSyntax; bool doAnno(prod(\label(_, \parameterized-sort(_, _)), _, _)) = markSyntax; - bool doAnno(\regular(_)) = markRegular; - bool doAnno(prod(\lit(_), _, _)) = markLit; - bool doAnno(prod(\cilit(_), _, _)) = markLit; - bool doAnno(prod(\start(_), _, _)) = markStart; - + bool doAnno(\regular(_)) = markRegular; + bool doAnno(prod(\lit(_), _, _)) = markLit; + bool doAnno(prod(\cilit(_), _, _)) = markLit; + bool doAnno(prod(\start(_), _, _)) = markStart; + @synopsis{Check if sub-structure of this rule is configured to be annotated} - default bool doSub(Production _) = true; - bool doSub(prod(\lex(_), _, _)) = \markSubLexical; - bool doSub(prod(\label(_, lex(_)), _, _)) = \markSubLexical; - bool doSub(prod(\layouts(_), _, _)) = \markSubLayout; + default bool doSub(Production _) = true; + bool doSub(prod(\lex(_), _, _)) = \markSubLexical; + bool doSub(prod(\label(_, lex(_)), _, _)) = \markSubLexical; + bool doSub(prod(\layouts(_), _, _)) = \markSubLayout; bool doSub(prod(\label(_, \layouts(_)), _, _)) = \markSubLayout; - bool doSub(prod(\lit(_), _, _)) = \markSubLit; - bool doSub(prod(\cilit(_), _, _)) = \markSubLit; - + bool doSub(prod(\lit(_), _, _)) = \markSubLit; + bool doSub(prod(\cilit(_), _, _)) = \markSubLit; + // the character nodes drive the actual current position: offset, line and column - Tree rec(Tree t:char(int ch), bool _sub) { - beginOffset = curOffset; - beginLine = curLine; - beginColumn = curColumn; - - curOffset += 1; - curColumn += 1; - - switch (t) { - case NewLineChar _ : { - curLine += 1; - curColumn = 0; + Tree rec(Tree t: char(int ch), bool _sub) { + beginOffset = curOffset; + beginLine = curLine; + beginColumn = curColumn; + + curOffset += 1; + curColumn += 1; + + switch(t) { + case NewLineChar _: { + curLine += 1; + curColumn = 0; + } } - } - - Tree washCC(Tree x) = x; // workaround for issue #2342 - - return markChar - ? washCC(char(ch))[@\loc=file(beginOffset, 1, , )] - : washCC(char(ch)) - ; + + Tree washCC(Tree x) = x; + + // workaround for issue #2342 + return + markChar + ? washCC(char(ch))[@\loc = file( + beginOffset, + 1, + , + + )] + : washCC(char(ch)); } - + // cycles take no space - Tree rec(cycle(Symbol s, int up), bool _sub) = markCycle - ? cycle(s, up)[@\loc=file(curOffset, 0, , )] - : cycle(s, up) - ; - + Tree rec(cycle(Symbol s, int up), bool _sub) + = markCycle + ? cycle(s, up)[@\loc = file(curOffset, 0, , )] + : cycle(s, up); + // application nodes always have children to traverse, to get to the individual characters eventually // different types of nodes lead to annotation, or not, depending on the parameters of ((reposition)) Tree rec(appl(Production prod, list[Tree] args), bool sub) { - beginOffset = curOffset; - beginLine = curLine; - beginColumn = curColumn; - - // once `sub` is false, going down, we can never turn it on again - newArgs = [mergeRec(a, sub && doSub(prod)) | a <- args]; - - return (sub && doAnno(prod)) - ? appl(prod, newArgs)[@\loc=file(beginOffset, curOffset - beginOffset, , )] - : appl(prod, newArgs) - ; - } - + beginOffset = curOffset; + beginLine = curLine; + beginColumn = curColumn; + + // once `sub` is false, going down, we can never turn it on again + newArgs = [mergeRec(a, sub && doSub(prod)) | a <- args]; + + return + (sub && doAnno(prod)) + ? appl(prod, newArgs)[@\loc = file( + beginOffset, + curOffset - beginOffset, + , + + )] + : appl(prod, newArgs); + } + // ambiguity nodes are simply choices between alternatives which each receive their own positions. Tree rec(amb(set[Tree] alts), bool sub) { - newAlts = {mergeRec(a, sub) | a <- alts}; - // inherit the outermost positions from one of the alternatives, since they are all the same by definition. - Tree x = getFirstFrom(newAlts); - return markAmb && x@\loc? - ? amb(newAlts)[@\loc=x@\loc] - : amb(newAlts) - ; + newAlts = {mergeRec(a, sub)| a <- alts}; + + // inherit the outermost positions from one of the alternatives, since they are all the same by definition. + Tree x = getFirstFrom(newAlts); + return markAmb && x@\loc? ? amb(newAlts)[@\loc = x@\loc] : amb(newAlts); } - + @synopsis{Recurse, but not without recovering all other keyword parameters except "src" a.k.a. @\loc from the original.} Tree mergeRec(Tree t, bool sub) { - oldParams = getKeywordParameters(t); - t = rec(t, sub); - newParams = getKeywordParameters(t); - mergedParams = (oldParams - ("src" : |unknown:///|)) + newParams; - return setKeywordParameters(t, mergedParams); + oldParams = getKeywordParameters(t); + t = rec(t, sub); + newParams = getKeywordParameters(t); + mergedParams = (oldParams - ("src" : |unknown:///|)) + newParams; + return setKeywordParameters(t, mergedParams); } - + // we start recursion at the top, not forgetting to merge its other keyword fields return mergeRec(tree, true); } - \ No newline at end of file + + diff --git a/src/org/rascalmpl/library/Type.rsc b/src/org/rascalmpl/library/Type.rsc index 8c6cafe1997..c3511cfda0b 100644 --- a/src/org/rascalmpl/library/Type.rsc +++ b/src/org/rascalmpl/library/Type.rsc @@ -30,7 +30,7 @@ The ((subtype)) relation of Rascal has all the mathematical properties of a _fin This is a core design principle of Rascal with the following benefits: * Type inference has a guaranteed least or greatest solution, always. This means that constraints are always solvable in an unambiguous manner. * A _principal type_ can always be computed, which is a most precise and unique solution of a type inference problem. Without the lattice, solution candidates could become incomparable and thus ambiguous. Without -this principal type property, type inference is predictable for programmers. +this principal type property, type inference is unpredictable for programmers. * Solving type inference constraints can be implemented efficiently. The algorithm, based on ((lub)) and ((glb)), makes progress _deterministically_ and does not require backtracking to find better solutions. Since the lattice is not very deep, fixed-point solutions are always found quickly. diff --git a/src/org/rascalmpl/library/analysis/diff/edits/ExecuteTextEdits.rsc b/src/org/rascalmpl/library/analysis/diff/edits/ExecuteTextEdits.rsc index f1c41d5c695..4ccf4641949 100644 --- a/src/org/rascalmpl/library/analysis/diff/edits/ExecuteTextEdits.rsc +++ b/src/org/rascalmpl/library/analysis/diff/edits/ExecuteTextEdits.rsc @@ -46,13 +46,13 @@ void executeFileSystemChange(changed(loc file, list[TextEdit] edits)) { } str executeTextEdits(str content, list[TextEdit] edits) { - assert isSorted(edits, less=bool (TextEdit e1, TextEdit e2) { - return e1.range.offset < e2.range.offset; - }); + // assert isSorted(edits, less=bool (TextEdit e1, TextEdit e2) { + // return e1.range.offset < e2.range.offset; + // }); - for (replace(loc range, str repl) <- reverse(edits)) { - content = ""; - } + int cursor = 0; - return content; + // linear-time streamed reconstruction of the entire text + return "< + cursor = range.offset + range.length;}>"; } diff --git a/src/org/rascalmpl/library/analysis/diff/edits/HiFiLayoutDiff.rsc b/src/org/rascalmpl/library/analysis/diff/edits/HiFiLayoutDiff.rsc index 65a5b722ca4..a72905fc087 100644 --- a/src/org/rascalmpl/library/analysis/diff/edits/HiFiLayoutDiff.rsc +++ b/src/org/rascalmpl/library/analysis/diff/edits/HiFiLayoutDiff.rsc @@ -27,6 +27,8 @@ module analysis::diff::edits::HiFiLayoutDiff extend analysis::diff::edits::HiFiTreeDiff; import ParseTree; // this should not be necessary because imported by HiFiTreeDiff import String; // this should not be be necessary because imported by HiFiTreeDiff +import lang::rascal::grammar::definition::Characters; +import IO; @synopsis{Normalization choices for case-insensitive literals.} data CaseInsensitivity @@ -56,7 +58,7 @@ list[TextEdit] layoutDiff(Tree original, Tree formatted, bool recoverComments = list[TextEdit] rec( t:appl(prod(Symbol tS, _, _), list[Tree] tArgs), // layout is not necessarily parsed with the same rules (i.e. comments are lost!) u:appl(prod(Symbol uS, _, _), list[Tree] uArgs)) - = [replace(t@\loc, recoverComments ? learnComments(t, u) : "") | tArgs != uArgs, "" != "" /* avoid useless edits */] + = [replace(t@\loc, repl) | tArgs != uArgs, str repl := (recoverComments ? learnComments(t, u) : ""), repl != "" /* do not edit anything if nothing has changed */] when delabel(tS) is layouts, delabel(uS) is layouts, @@ -106,22 +108,31 @@ list[TextEdit] layoutDiff(Tree original, Tree formatted, bool recoverComments = default list[TextEdit] rec( Tree t:appl(Production p, list[Tree] argsA), appl(p /* must be the same by the above assert */, list[Tree] argsB)) - = [*rec(a, b) | <- zip2(argsA, argsB)]; + = [*rec(argsA[i], argsB[i]) | i <- [0..size(argsA)]]; // first add required locations to layout nodes - original = reposition(original, markLit=true, markLayout=true, markSubLayout=true); + // TODO: check if indeed repositioning is never needed + // original = reposition(original, markLit=true, markLayout=true, markSubLayout=true); return rec(original, formatted); } +private Symbol newlineClass = \char-class([range(10,10)]); + @synopsis{Make sure the new layout still contains all the source code comments of the original layout} @description{ -This algorithm uses the @category("Comments") tag to detect source code comments inside layout substrings. If the original +This algorithm uses the `@category(/[cC]omments/)` tag to detect source code comments inside layout substrings. If the original layout contains comments, we re-introduce the comments at the expected level of indentation. New comments present in the replacement are kept and will overwrite any original comments. -This trick is complicated by the syntax of multiline comments and single line comments that have -to end with a newline. +There are corner cases with respect to the original comments: +* the single line comment that does not end with a newline itself, yet it must always end with a newline after it. +* multiple single line comments after each other + +Then there are corner cases with respect to the replacement whitespace: +* the last line of the replacement whitespace is special. This is the indentation to use for all comments. +* but there could be no newlines in the replacement whitespace; and still there is a single line comment to be included. +Now we need to infer an indentation level for what follows the comment from "thin air". } @benefits{ * if comments are kepts and formatted by tools like Tree2Box, then this algorithm does not overwrite these. @@ -132,7 +143,14 @@ to end with a newline. * if comments are not marked with `@category("Comment")` in the original grammar, then this algorithm recovers nothing. } private str learnComments(Tree original, Tree replacement) { - originalComments = ["" | /c:appl(prod(_,_,{\tag("category"(/^[Cc]omment$/)), *_}), _) := original]; + bool mustEndWithNewline(lit("\n")) = true; + bool mustEndWithNewline(conditional(Symbol s, _)) = mustEndWithNewline(s); + // if a comment can not contain newline characters, but everything else, then it must be followed by one: + bool mustEndWithNewline(\iter(Symbol cc:\char-class(_))) = intersection(cc, newlineClass) != newlineClass; + bool mustEndWithNewline(\iter-star(Symbol cc:\char-class(_))) = intersection(cc, newlineClass) != newlineClass; + default bool mustEndWithNewline(_) = false; + + originalComments = [ | /c:appl(prod(_,[*_,Symbol lastSym],{\tag("category"(/^[Cc]omment$/)), *_}), _) := original, str s := ""]; if (originalComments == []) { // if the original did not contain comments, stick with the replacements @@ -146,23 +164,42 @@ private str learnComments(Tree original, Tree replacement) { return ""; } - // At this point, we know that: (a) comments are not present in the replacement and (b) they used to be there in the original. - // So the old comments are going to be the new output. however, we want to learn indentation from the replacement. + // At this point, we know that: + // (a) comments are not present in the replacement and + // (b) they used to be there in the original. + // So the old comments are going to be copied to the new output. + // But, we want to indent them using the style of the replacement. + + // The last line of the replacement string typically has the indentation for the construct that follows: + // | // a comment + // | if (true) { + // ^^^^ + // newIndent + // + // However, if the replacement string is on a single line, then we don't have the indentation + // for the string on the next line readily available. In this case we indent the next line + // to the start column of the replacement layout, as a proxy. + + str replString = ""; + str newIndent = split("\n", replString)[-1] ? ""; - // Drop the last newline of single-line comments, because we don't want two newlines in the output for every comment: - str dropEndNl(str line:/^.*\n$/) = (line[..-1]); - default str dropEndNl(str line) = line; + if (/\n/ !:= replString) { + // no newline in the repl string, so no indentation available for what follows the comment... + newIndent = " <}>"; + } - // the first line of the replacement ,is the indentation to use. - str replString = ""; - str replacementIndent = /^\n+$/ !:= replString - ? split("\n", replString)[0] - : ""; - - // trimming each line makes sure we forget about the original indentation, and drop accidental spaces after comment lines - return replString + indent(replacementIndent, - " - '<}>"[..-1], indentFirstLine=false) + replString; + // we always place sequential comments vertically, because we don't know if we are dealing + // we a single line comment that has to end with newline by follow restriction or by a literal "\n". + // TODO: a deeper analysis of the comment rule that's in use could also be used to discover this. + str trimmedOriginals = " <- originalComments) {> + '<}><}>"; + + // we wrap the comment with the formatted whitespace to assure the proper indentation + // of its first line, and the proper indentation of what comes after this layout node + return replString + + indent(newIndent, trimmedOriginals, indentFirstLine=false) + + newIndent + ; } private Symbol delabel(label(_, Symbol t)) = t; diff --git a/src/org/rascalmpl/library/analysis/formalconcepts/CXTIO.rsc b/src/org/rascalmpl/library/analysis/formalconcepts/CXTIO.rsc index 42e239c53bb..c5950d1c815 100644 --- a/src/org/rascalmpl/library/analysis/formalconcepts/CXTIO.rsc +++ b/src/org/rascalmpl/library/analysis/formalconcepts/CXTIO.rsc @@ -6,33 +6,26 @@ http://www.eclipse.org/legal/epl-v10.html } module analysis::formalconcepts::CXTIO + import IO; import String; import List; import Set; import analysis::formalconcepts::FCA; -@synopsis{Read object attribute in .cxt format.} -public FormalContext[str, str] readCxt(loc input) { +@synopsis{Read object attribute in .cxt format.} +public FormalContext[str, str] readCxt(loc input) { list[str] d = readFileLines(input); int nRows = toInt(d[2]); int nCols = toInt(d[3]); - int theStart = 5+nRows+nCols; - list[str] e = tail(d, size(d)-theStart); + int theStart = 5 + nRows + nCols; + list[str] e = tail(d, size(d) - theStart); int idx = 5; - map [str, set[str]] vb = (); + map[str, set[str]] vb = (); for (str f <- e) { - set[str] b = {d[5+nRows+i]|int i<-[0, 1..size(f)], charAt(f,i)==88}; - vb[d[idx]] = b; - idx = idx+1; - } - return toFormalContext(vb); + set[str] b = {d[5 + nRows + i]| int i <- [0, 1..size(f)], charAt(f, i) == 88}; + vb[d[idx]] = b; + idx = idx + 1; } - -loc input = |file:///ufs/bertl/cxt/digits.cxt|; - -public void main() { - FormalContext[str, str] d = readCxt(input); - ConceptLattice[str, str] e = fca(d); - println(toDotString(e)); - } + return toFormalContext(vb); +} diff --git a/src/org/rascalmpl/library/analysis/formalconcepts/FCA.rsc b/src/org/rascalmpl/library/analysis/formalconcepts/FCA.rsc index a686a722d77..3700b4eb435 100644 --- a/src/org/rascalmpl/library/analysis/formalconcepts/FCA.rsc +++ b/src/org/rascalmpl/library/analysis/formalconcepts/FCA.rsc @@ -6,7 +6,6 @@ http://www.eclipse.org/legal/epl-v10.html } @contributor{Bert Lisser - Bert.Lisser@cwi.nl (CWI)} - @synopsis{Library for Formal Concept Analysis} @description{ Formal Concept Analysis is a somewhat ubiquitous tool in software analysis projects. @@ -14,7 +13,6 @@ It can be used to find latent groups of objects that share the same attributes i Typically, we apply `FCA` to a relation `rel[&O objects, &A attributes]`, which represents extracted source code artifacts and their attributes. } - module analysis::formalconcepts::FCA import Set; @@ -30,136 +28,148 @@ public alias ConceptLattice[&Object, &Attribute] = rel[Concept[&Object, &Attribu public alias Object2Attributes[&Object, &Attribute] = map[&Object, set[&Attribute]]; public alias Attribute2Objects[&Attribute, &Object] = map[&Attribute, set[&Object]]; - - @synopsis{Computes Concept Lattice given the Object Attribute Relation.} -public ConceptLattice[&Object, &Attribute] fca (FormalContext[&Object, &Attribute] fc) { +public ConceptLattice[&Object, &Attribute] fca(FormalContext[&Object, &Attribute] fc) { rel[set[&Attribute], set[&Attribute]] lat = createAttributeLattice(fc); - return {<, >|<-lat}; + return + {<, > + | <- lat + }; } - @synopsis{Computes Dot Graph from Concept Lattice.} public DotGraph toDot(ConceptLattice[&Object, &Attribute] cl) { - return toDot(cl, true); - } - + return toDot(cl, true); +} + public DotGraph toDot(ConceptLattice[&Object, &Attribute] cl, bool lab) { - map[Concept[&Object, &Attribute], int] z = makeNodes(cl); - set[Concept[&Object, &Attribute]] d = domain(z); - Stms nodes = []; - for (Concept[&Object, &Attribute] c <- d) { - nodes += compose(c, z, lab); - } - Stms edges = [ E("\"\"", "\"\"") | x<-cl]; - return digraph("fca", - [NODE( [<"style","filled">, <"fillcolor","cornsilk">,<"fontcolor","blue">,<"shape","ellipse">])] - +nodes+edges); - } - + map[Concept[&Object, &Attribute], int] z = makeNodes(cl); + set[Concept[&Object, &Attribute]] d = domain(z); + Stms nodes = []; + for (Concept[&Object, &Attribute] c <- d) { + nodes += compose(c, z, lab); + } + Stms edges = [E("\"\"", "\"\"") | x <- cl]; + return + digraph( + "fca", + [ + NODE( + [<"style", "filled">, + <"fillcolor", "cornsilk">, + <"fontcolor", "blue">, + <"shape", "ellipse"> ] + ) + ] + + nodes + + edges + ); +} + public Dotline toDotline(ConceptLattice[&Object, &Attribute] cl) { - return ; - } + return ; +} - public Outline toOutline(ConceptLattice[&Object, &Attribute] cl) { - map[Concept[&Object, &Attribute], int] z = makeNodes(cl); - set[Concept[&Object, &Attribute]] d = domain(z); - Outline r = (z[c]:["", ""] | Concept[&Object, &Attribute] c <- d); - return r; - } - + map[Concept[&Object, &Attribute], int] z = makeNodes(cl); + set[Concept[&Object, &Attribute]] d = domain(z); + Outline r = (z[c]: ["", ""] | Concept[&Object, &Attribute] c <- d ); + return r; +} + public FormalContext[&Object, &Attribute] toFormalContext(Object2Attributes[&Object, &Attribute] objects) { - return { | &Object object <- domain(objects), - &Attribute attribute <- objects[object]}; - } + return + { + | &Object object <- domain(objects), &Attribute attribute <- objects[object] + }; +} public FormalContext[&Object, &Attribute] toFormalContext(Attribute2Objects[&Object, &Attribute] attributes) { - return { | &Attribute attribute <- domain(attributes), - &Object object <- attributes[attribute]}; - } -/*---------------------------------------------------------------------------------------------*/ - -set[&T] intersection(set[set[&T]] st) -{ - set[&T] result = isEmpty(st)?{}:getOneFrom(st); - for(set[&T] elm <- st){ - result = result & elm; - } - return result; -} - -set[&T] union(set[set[&T]] st) -{ - set[&T] result = {}; - for(set[&T] elm <- st){ - result += elm; - } - return result; -} - -bool isSubset(set[set[&T]] candidate, set[&T] s ) { - for (set[&T] c <- candidate) - if (s + | &Attribute attribute <- domain(attributes), &Object object <- attributes[attribute] + }; +} + +set[&T] intersection(set[set[&T]] st) { + set[&T] result = isEmpty(st) ? {} : getOneFrom(st); + for (set[&T] elm <- st) { + result = result & elm; + } + return result; +} + +set[&T] union(set[set[&T]] st) { + set[&T] result = {}; + for (set[&T] elm <- st) { + result += elm; + } + return result; +} + +bool isSubset(set[set[&T]] candidate, set[&T] s) { + for (set[&T] c <- candidate) + if ( s < c) + return true; + return false; +} @javaClass{org.rascalmpl.library.analysis.formalconcepts.FCA} java set[&Attribute] sigma(FormalContext[&Object, &Attribute] fc, set[&Object] objects); - //= objects == {} ? fc<1> : { a | a <- fc<1>, all(obj <- objects, in fc)}; - + +//= objects == {} ? fc<1> : { a | a <- fc<1>, all(obj <- objects, in fc)}; @javaClass{org.rascalmpl.library.analysis.formalconcepts.FCA} java set[&Object] tau(FormalContext[&Object, &Attribute] fc, set[&Attributes] attributes); - //= attributes == {} ? fc<0> : { ob | ob <- fc<0>, all(a <- attributes, in fc)}; - -set[set[&T]] maxincl(set[set[&T]] c) {return {s|set[&T] s <- c, !isSubset(c, s)};} + +//= attributes == {} ? fc<0> : { ob | ob <- fc<0>, all(a <- attributes, in fc)}; +set[set[&T]] maxincl(set[set[&T]] c) { + return {s| set[&T] s <- c, !isSubset(c, s)}; +} rel[set[&Attribute], set[&Attribute]] createAttributeLattice(FormalContext[&Object, &Attribute] fc) { - set[&Object] G = domain(fc); - set[&Attribute] M = range(fc); - set[set[&Attribute]] layer = {M}; - set[set[&Attribute]] B = {sigma(fc, {g}) | g <- G}; - rel[set[&Attribute], set[&Attribute]] r = {}; - while (!isEmpty(layer)&& layer!={{}}) { - set[set[&Attribute]] nextLayer = {}; - for (set[&Attribute] m<-layer) { - set[set[&Attribute]] cover = maxincl({b&m|set[&Attribute] b<-B, (b&m)}; - nextLayer += cover; - } - layer = nextLayer; - } - return r; - } - - /*-----------------------------------------------------------------------------------*/ - + set[&Object] G = domain(fc); + set[&Attribute] M = range(fc); + set[set[&Attribute]] layer = {M}; + set[set[&Attribute]] B = {sigma(fc, {g})| g <- G}; + rel[set[&Attribute], set[&Attribute]] r = {}; + while (!isEmpty(layer) && layer != {{}}) { + set[set[&Attribute]] nextLayer = {}; + for (set[&Attribute] m <- layer) { + set[set[&Attribute]] cover = maxincl({b & m| set[&Attribute] b <- B, (b & m) < m}); + for (set[&Attribute] cov <- cover) + r += {}; + nextLayer += cover; + } + layer = nextLayer; + } + return r; +} + map[Concept[&Object, &Attribute], int] makeNodes(ConceptLattice[&Object, &Attribute] q) { - set[Concept[&Object, &Attribute]] c = carrier(q); - int i = 0; - map[Concept[&Object, &Attribute], int] r = (); - for (Concept[&Object, &Attribute] b<-c) { - if (!(r[b])?) { - r[b] = i; - i=i+1; - } - } - return r; - } - -set[&Attribute] addConcept(ConceptLattice[&Object, &Attribute] q, Concept[&Object, &Attribute] c) { - set[Concept[&Object, &Attribute]] parents = range(domainR(q, {c})); - return c[1] - union({p[1]|Concept[&Object, &Attribute] p <-parents}); -} - -Stm compose(Concept[&Object, &Attribute] c, map[Concept[&Object, &Attribute], int] z, bool lab) { - return N("\"\"", lab?[<"label", "">]:[]); -} - - - -@synopsis{Write relation in `.dot` format.} -public str toDotString(ConceptLattice[&Object, &Attribute] q) { - DotGraph d = toDot(q); - return toString(d); - } + set[Concept[&Object, &Attribute]] c = carrier(q); + int i = 0; + map[Concept[&Object, &Attribute], int] r = (); + for (Concept[&Object, &Attribute] b <- c) { + if ( !(r[b])?) { + r[b] = i; + i = i + 1; + } + } + return r; +} + +set[&Attribute] addConcept(ConceptLattice[&Object, &Attribute] q, Concept[&Object, &Attribute] c) { + set[Concept[&Object, &Attribute]] parents = range(domainR(q, {c})); + return c[1] - union({p[1]| Concept[&Object, &Attribute] p <- parents}); +} + +Stm compose(Concept[&Object, &Attribute] c, map[Concept[&Object, &Attribute], int] z, + bool lab) { + return N("\"\"", lab ? [<"label", "">] : []); +} + +@synopsis{Write relation in `.dot` format.} +public str toDotString(ConceptLattice[&Object, &Attribute] q) { + DotGraph d = toDot(q); + return toString(d); +} diff --git a/src/org/rascalmpl/library/format.log b/src/org/rascalmpl/library/format.log new file mode 100644 index 00000000000..4b719a1e9ae --- /dev/null +++ b/src/org/rascalmpl/library/format.log @@ -0,0 +1,85153 @@ +### |project://rascal/src/org/rascalmpl/library/Boolean.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@synopsis{Library functions for Booleans.} +@description{ +The following library functions are defined for Booleans: +(((TOC))) +} +module Boolean + +import Exception; + +@synopsis{Return an arbitrary Boolean value.} +@examples{ +```rascal-shell +import Boolean; +arbBool(); +arbBool(); +arbBool(); +``` +} +@benefits{ +* `arbBool` is a convenient generator for arbitrary binary choices. +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool arbBool(); + +@synopsis{Convert the strings "true" or "false" to a bool.} +public bool fromString(str s) { + if (s == "true") { + return true; + } + if (s == "false") { + return false; + } + throw IllegalArgument(s, "not \"true\" or \"false\""); +} + +@synopsis{Convert a Boolean value to integer.} +@description{ +Maps `true` to `1` and `false` to `0`. +} +@examples{ +```rascal-shell +import Boolean; +toInt(true); +toInt(false); +``` +} +public int toInt(bool b) { + return b ? 1 : 0; +} + +@synopsis{Convert Boolean value to real.} +@description{ +Maps `true` to `1.0` and `false` to `0.0`. +} +@examples{ +```rascal-shell +import Boolean; +toReal(true); +toReal(false); +``` +} +public real toReal(bool b) { + return b ? 1.0 : 0.0; +} + +@synopsis{Convert Boolean value to string.} +@description{ +Maps `true` to `"true"` and `false` to `"false"`. +} +@examples{ +```rascal-shell +import Boolean; +toString(true); +toString(false); +``` +} +public str toString(bool b) { + return b ? "true" : "false"; +} +### |project://rascal/src/org/rascalmpl/library/Content.rsc| +@synopsis{Content provides access to the content server of the Rascal terminal for viewing interactive HTML output.} +module Content + +import lang::json::IO; + +@synopsis{Content wraps the HTTP Request/Response API to support interactive visualization types +on the terminal.} +@description{ +Values wrapped in a `Content` wrapper will be displayed by interactive +Rascal applications such as the IDE, the REPL terminal and the documentation pages. + +```rascal-prepare +import Content; +``` + +For example, a piece of html can be displayed directly like such: +```rascal-shell,continue +html("\Rascal homepage\") +``` + +In its most general form, `Content` is an HTTP(s) webserver callback, such that you might deliver +any kind of content, based on any kind of request. If you produce a `Content` value +which processes requests dynamically, subsequent interaction from the web browser will be +processed as well. So using the `Content` wrapper you can start an interactive user experience +in the browser directly from the REPL. + +Content values stay plugged into the application server that is hidden in the REPL +environment until they have not been used for at least 30 minutes. If you want the same +interaction back after 30 minutes of non-usage, you have to produce another Content value +on the commandline. + +When you are happy with the interaction, or you want a permanent visualization which is not +garbage collected after 30 minutes, you can consider wrapping the same callback in +a webserver using the ((util::Webserver::serve)) function. +} +data Content + = content( + str id, Response(Request) callback, + str title = id, + ViewColumn viewColumn = normalViewColumn(1)) + | content( + Response response, + str title = "*static content*", + ViewColumn viewColumn = normalViewColumn(1)) + ; + +@synopsis{Directly serve a static html page} +Content html(str html) = content(response(html)); + +@synopsis{Directly serve the contents of a file} +Content file(loc src) = content(response(src)); + +@synopsis{Directly serve the contents of a string as plain text} +Content plainText(str text) = content(plain(text)); + +alias Body = value(type[value] expected); + +@synopsis{Request values represent what a browser is asking for, most importantly the URL path.} +@description{ +A request value also contains the full HTTP headers, the URL parameters as a `map[str,str]` +and possibly uploaded content, also coded as a map[str,str]. From the constructor type, +`put` or `get` you can see what kind of HTTP request it was. +} +@pitfalls{ +* Note that `put` and `post` have not been implemented yet in the REPL server. +} +data Request (map[str, str] headers = (), + map[str, str] parameters = (), + map[str, str] uploads = ()) + = get(str path) + | put(str path, Body content) + | post(str path, Body content) + | delete(str path) + | head(str path) + ; + +@synopsis{A response encodes what is send back from the server to the browser client.} +@description{ +The three kinds of responses, encode either content that is already a `str`, +some file which is streamed directly from its source location or a jsonResponse +which involves a handy, automatic, encoding of Rascal values into json values. +} +data Response + = response(Status status, str mimeType, map[str, str] header, str content) + | fileResponse(loc file, str mimeType, map[str, str] header) + | jsonResponse( + Status status, map[str, str] header, value val, + str dateTimeFormat = "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'", + JSONFormatter[value] formatter = str (value _){ fail; }, + bool explicitConstructorNames = false, + bool explicitDataTypes = false, + bool dateTimeAsInt = true, + bool rationalsAsString = false) + ; + +@synopsis{Utility to quickly render a string as HTML content} +Response response(str content, map[str, str] header = ()) + = response(ok(), "text/html", header, content); + +@synopsis{Utility to quickly report an HTTP error with a user-defined message} +Response response(Status status, str explanation, map[str, str] header = ()) + = response(status, "text/plain", header, explanation); + +@synopsis{Utility to quickly make a plaintext response.} +Response plain(str text) = response(ok(), "text/plain", (), text); + +@synopsis{Utility to serve a file from any source location.} +Response response(loc f, map[str, str] header = ()) + = fileResponse(f, mimeTypes[f.extension] ? "text/plain", header); + +@synopsis{Utility to quickly serve any rascal value as a json text.} +@benefits{ +This comes in handy for asynchronous HTTP requests from Javascript clients. Rascal Values are +fully transformed to their respective JSON serialized form before being communicated over HTTP. +} +default Response response(value val, map[str, str] header = ()) + = jsonResponse(ok(), header, val); + +@synopsis{Utility to quickly serve any rascal value as a json text, formatting data-types on-the-fly using a `formatter` function} +@benefits{ +Fast way of producing JSON strings for embedded DSLs on the Rascal side. +} +Response response( + value val, + JSONFormatter[value] formatter, + map[str, str] header = () +) + = jsonResponse(ok(), header, val, formatter = formatter); + +@synopsis{Encoding of HTTP status} +data Status + = ok() + | created() + | accepted() + | noContent() + | partialContent() + | redirect() + | notModified() + | badRequest() + | unauthorized() + | forbidden() + | notFound() + | rangeNotSatisfiable() + | internalError() + ; + +@synopsis{A static map with default MIME interpretations for particular file extensions.} +public map[str extension, str mimeType] mimeTypes + = ( + "json" : "application/json", + "css" : "text/css", + "htm" : "text/html", + "html" : "text/html", + "xml" : "text/xml", + "java" : "text/x-java-source, text/java", + "txt" : "text/plain", + "asc" : "text/plain", + "ico" : "image/x-icon", + "gif" : "image/gif", + "jpg" : "image/jpeg", + "jpeg" : "image/jpeg", + "png" : "image/png", + "mp3" : "audio/mpeg", + "m3u" : "audio/mpeg-url", + "mp4" : "video/mp4", + "ogv" : "video/ogg", + "flv" : "video/x-flv", + "mov" : "video/quicktime", + "swf" : "application/x-shockwave-flash", + "js" : "application/javascript", + "pdf" : "application/pdf", + "doc" : "application/msword", + "ogg" : "application/x-ogg", + "zip" : "application/octet-stream", + "exe" : "application/octet-stream", + "class" : "application/octet-stream" + ) ; + +@synopsis{Hint the IDE where to open the next web view or editor} +@description{ +The `viewColumn` decides where in the IDE a web client or editor is opened, +_if_ the current IDE honors this parameter. + +There are _9_ possible +view columns: 1, 2, 3, 4, 5, 6, 7, 8, 9. + +Next to this: +* view column `-1` is converted to the _currently active_ view column before the editor is opened. +* view column `-2` is chosen to be a view column _beside_ (to the right) of the currently active view column. + +All other view column integers are ignored and interpreted as `-1`. +} +alias ViewColumn = int; + +ViewColumn activeViewColumn() = -1; +ViewColumn besideViewColumn() = -2; +ViewColumn normalViewColumn(int v) = v + when v >= 1 && v <= 9; +### |project://rascal/src/org/rascalmpl/library/DateTime.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Mark Hills - Mark.Hills@cwi.nl (CWI)} +@synopsis{Library functions for date and time.} +@description{ +The following library functions are defined for datetime: +(((TOC))) +} +module DateTime + +import List; + +@synopsis{Get the current datetime.} +@examples{ +```rascal-shell +import DateTime; +now(); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime now(); + +@synopsis{Create a new date.} +@examples{ +```rascal-shell +import DateTime; +createDate(2012,1,1); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime createDate(int year, int month, int day); + +@synopsis{Create a new time (with optional timezone offset).} +@examples{ +```rascal-shell +import DateTime; +createTime(8,15,30,55); +createTime(8,15,30,55,2,0); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public +java datetime createTime(int hour, int minute, int second, int millisecond); + +// Create a new time with the given numeric timezone offset. +@javaClass{org.rascalmpl.library.Prelude} +public +java datetime createTime( + int hour, int minute, int second, int millisecond, int timezoneHourOffset, + int timezoneMinuteOffset +); + +@synopsis{Create a new datetime (with optional timezone offset).} +@examples{ +```rascal-shell +import DateTime; +createDateTime(2012,1,1,8,15,30,55); +createDateTime(2012,1,1,8,15,30,55,2,0); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public +java datetime createDateTime( + int year, int month, int day, int hour, int minute, int second, int millisecond +); + +// Create a new datetime with the given numeric timezone offset. +@javaClass{org.rascalmpl.library.Prelude} +public +java datetime createDateTime( + int year, int month, int day, int hour, int minute, int second, + int millisecond, int timezoneHourOffset, int timezoneMinuteOffset +); + +@synopsis{Create a new datetime by combining a date and a time.} +@examples{ +```rascal-shell +import DateTime; +D = createDate(2012, 1, 1); +T = createTime(8, 15, 45, 30); +joinDateAndTime(D, T); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime joinDateAndTime(datetime date, datetime time); + +@synopsis{Split an existing datetime into a tuple with the date and the time.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +splitDateTime(N); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java tuple[datetime date, datetime time] splitDateTime(datetime dt); + +@synopsis{Increment the years by given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +incrementYears(N); +incrementYears(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime incrementYears(datetime dt, int n); + +// Increment the years by 1. +public datetime incrementYears(datetime dt) { + return incrementYears(dt, 1); +} + +@synopsis{Increment the months by a given amount or by 1.} +@function{ + +} +@examples{ +```rascal-shell +import DateTime; +N = now(); +incrementMonths(N); +incrementMonths(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime incrementMonths(datetime dt, int n); + +// Increment the months by 1. +public datetime incrementMonths(datetime dt) { + return incrementMonths(dt, 1); +} + +@synopsis{Increment the days by given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +incrementDays(N); +incrementDays(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime incrementDays(datetime dt, int n); + +// Increment the days by 1. +public datetime incrementDays(datetime dt) { + return incrementDays(dt, 1); +} + +@synopsis{Increment the hours by a given amount or by 1.`} +@examples{ +```rascal-shell +import DateTime; +N = now(); +incrementHours(N); +incrementHours(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime incrementHours(datetime dt, int n); + +// Increment the hours by 1. +public datetime incrementHours(datetime dt) { + return incrementHours(dt, 1); +} + +@synopsis{Increment the minutes by a given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +incrementMinutes(N); +incrementMinutes(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime incrementMinutes(datetime dt, int n); + +// Increment the minutes by 1. +public datetime incrementMinutes(datetime dt) { + return incrementMinutes(dt, 1); +} + +@synopsis{Increment the seconds by a given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +incrementSeconds(N); +incrementSeconds(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime incrementSeconds(datetime dt, int n); + +// Increment the seconds by 1. +public datetime incrementSeconds(datetime dt) { + return incrementSeconds(dt, 1); +} + +@synopsis{Increment the milliseconds by a given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +incrementMilliseconds(N); +incrementMilliseconds(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime incrementMilliseconds(datetime dt, int n); + +// Increment the milliseconds by 1. +public datetime incrementMilliseconds(datetime dt) { + return incrementMilliseconds(dt, 1); +} + +@synopsis{Decrement the years by a given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +decrementYears(N); +decrementYears(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime decrementYears(datetime dt, int n); + +// Decrement the years by 1. +public datetime decrementYears(datetime dt) { + return decrementYears(dt, 1); +} + +@synopsis{Decrement the months by a given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +decrementMonths(N); +decrementMonths(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime decrementMonths(datetime dt, int n); + +// Decrement the months by 1. +public datetime decrementMonths(datetime dt) { + return decrementMonths(dt, 1); +} + +@synopsis{Decrement the days by a given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +decrementDays(N); +decrementDays(N, 3); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime decrementDays(datetime dt, int n); + +// Decrement the days by 1. +public datetime decrementDays(datetime dt) { + return decrementDays(dt, 1); +} + +@synopsis{Decrement the hours by a given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +decrementHours(N); +decrementHours(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime decrementHours(datetime dt, int n); + +// Decrement the hours by 1. +public datetime decrementHours(datetime dt) { + return decrementHours(dt, 1); +} + +@synopsis{Decrement the minutes by a given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +decrementMinutes(N); +decrementMinutes(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime decrementMinutes(datetime dt, int n); + +// Decrement the minutes by 1. +public datetime decrementMinutes(datetime dt) { + return decrementMinutes(dt, 1); +} + +@synopsis{Decrement the seconds by a given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +decrementSeconds(N); +decrementSeconds(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime decrementSeconds(datetime dt, int n); + +// Decrement the seconds by 1. +public datetime decrementSeconds(datetime dt) { + return decrementSeconds(dt, 1); +} + +@synopsis{Decrement the milliseconds by a given amount or by 1.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +decrementMilliseconds(N); +decrementMilliseconds(N, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime decrementMilliseconds(datetime dt, int n); + +// Decrement the milliseconds by 1. +public datetime decrementMilliseconds(datetime dt) { + return decrementMilliseconds(dt, 1); +} + +@synopsis{A closed interval on the time axis.} +data interval = Interval(datetime begin, datetime end); + +@synopsis{Given two datetime values, create an interval.} +@examples{ +```rascal-shell +import DateTime; +B = now(); +E = incrementDays(B, 2); +createInterval(B, E); +``` +} +// TODO: Question, should we throw here if begin > end? +public interval createInterval(datetime begin, datetime end) { + return Interval(begin, end); +} + +@synopsis{A duration of time, measured in individual years, months, etc.} +data Duration + = duration( + int years, int months, int days, int hours, int minutes, + int seconds, int milliseconds); + +@javaClass{org.rascalmpl.library.Prelude} +private +java tuple[int, int, int, int, int, int, int] createDurationInternal( + datetime begin, datetime end +); + +// TODO: Add an exception for the non-matching case +@synopsis{Create a new duration representing the duration between the begin and end dates.} +@examples{ +```rascal-shell +import DateTime; +B = now(); +E1 = incrementHours(B); +createDuration(B, E1); +E2 = incrementMinutes(B); +createDuration(B, E2); +``` +} +public Duration createDuration(datetime begin, datetime end) { + switch(createDurationInternal(begin, end)) { + case <int y, int m, int d, int h, int min, int s, int ms>: + return duration(y, m, d, h, min, s, ms); + } + return duration(0, 0, 0, 0, 0, 0, 0); +} + +// Given an interval, create a new duration representing the duration between the interval begin and end. +public Duration createDuration(interval i) { + return createDuration(i.begin, i.end); +} + +@synopsis{Return the number of days in an interval, including the begin and end days.} +@examples{ +```rascal-shell +import DateTime; +B = now(); +E = incrementDays(B, 2); +I = createInterval(B, E); +daysInInterval(I); +``` +} +public int daysInInterval(interval i) { + return daysDiff(i.begin, i.end); +} + +@synopsis{Return the difference between two dates and/or datetimes in days.} +@examples{ +```rascal-shell +import DateTime; +B = now(); +E = incrementDays(B, 2); +daysDiff(B, E); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java int daysDiff(datetime begin, datetime end); + +@synopsis{Given an interval, return a list of days.} +@description{ +Given an interval `i`, return a list of days `[i.begin, ..., i.end]`. +} +@examples{ +```rascal-shell +import DateTime; +B = now(); +E = incrementDays(B, 2); +I = createInterval(B, E); +dateRangeByDay(I); +``` +} +public list[datetime] dateRangeByDay(interval i) { + list[datetime] l = []; + datetime loopDate = i.end.justDate; + datetime beginDate = i.begin.justDate; + + while(loopDate >= beginDate) { + l = insertAt(l, 0, loopDate); + loopDate = decrementDays(loopDate); + } + + return l; +} + +@synopsis{Parse an input date given as a string using the given format string.} +@examples{ +```rascal-shell +import DateTime; +parseDate("2011-12-23", "yyyy-MM-dd"); +parseDate("20111223", "yyyyMMdd"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime parseDate(str inputDate, str formatString); + +@synopsis{Parse an input date given as a string using a specific locale and format string.} +@javaClass{org.rascalmpl.library.Prelude} +public +java datetime parseDateInLocale(str inputDate, str formatString, str locale); + +@synopsis{Parse an input time given as a string using the given format string.} +@examples{ +```rascal-shell +import DateTime; +parseTime("11/21/19", "HH/mm/ss"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime parseTime(str inputTime, str formatString); + +@synopsis{Parse an input time given as a string using a specific locale and format string.} +@javaClass{org.rascalmpl.library.Prelude} +public +java datetime parseTimeInLocale(str inputTime, str formatString, str locale); + +@synopsis{Parse an input datetime given as a string using the given format string.} +@examples{ +```rascal-shell +import DateTime; +parseDateTime("2011/12/23/11/19/54", "YYYY/MM/dd/HH/mm/ss"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime parseDateTime(str inputDateTime, str formatString); + +@synopsis{Parse an input datetime given as a string using a specific locale and format string.} +@javaClass{org.rascalmpl.library.Prelude} +public +java datetime parseDateTimeInLocale( + str inputDateTime, str formatString, str locale +); + +@synopsis{Print an input date using the given format string.} +@examples{ +```rascal-shell +import DateTime; +printDate(now()); +printDate(now(), "YYYYMMdd"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str printDate(datetime inputDate, str formatString); + +// Print an input date using a default format string +@javaClass{org.rascalmpl.library.Prelude} +public java str printDate(datetime inputDate); + +@synopsis{Print an input date using a specific locale and format string.} +@examples{ +```rascal-shell +import DateTime; +printDateInLocale(now(), "Europe/Netherlands"); +printDateInLocale(now(), "French"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public +java str printDateInLocale(datetime inputDate, str formatString, str locale); + +// Print an input date using a specific locale and a default format string +@javaClass{org.rascalmpl.library.Prelude} +public java str printDateInLocale(datetime inputDate, str locale); + +@synopsis{Print an input time using the given format string.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +printTime(N); +printTime(N, "HH/mm/ss"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str printTime(datetime inputTime, str formatString); + +// Print an input time using a default format string +@javaClass{org.rascalmpl.library.Prelude} +public java str printTime(datetime inputTime); + +@synopsis{Print an input time using a specific locale and format string.} +@javaClass{org.rascalmpl.library.Prelude} +public +java str printTimeInLocale(datetime inputTime, str formatString, str locale); + +// Print an input time using a specific locale and a default format string +@javaClass{org.rascalmpl.library.Prelude} +public java str printTimeInLocale(datetime inputTime, str locale); + +@synopsis{Print an input datetime using the given format string.} +@examples{ +```rascal-shell +import DateTime; +N = now(); +printDateTime(N); +printDateTime(N, "yyyy-MM-dd\'T\'HH:mm:ss.SSSZZ"); +printDateTime(N, "YYYY/MM/dd/HH/mm/ss"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str printDateTime(datetime inputDateTime, str formatString); + +// Print an input datetime using a default format string +@javaClass{org.rascalmpl.library.Prelude} +public java str printDateTime(datetime inputDateTime); + +@synopsis{Print an input datetime using a specific locale and format string.} +@javaClass{org.rascalmpl.library.Prelude} +public +java str printDateTimeInLocale( + datetime inputDateTime, str formatString, str locale +); + +// Print an input datetime using a specific locale and a default format string +@javaClass{org.rascalmpl.library.Prelude} +public java str printDateTimeInLocale(datetime inputDateTime, str locale); + +@synopsis{Create a new arbitrary datetime.} +@examples{ +```rascal-shell +import DateTime; +arbDateTime(); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime arbDateTime(); +### |project://rascal/src/org/rascalmpl/library/Exception.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Jeroen van den Bos - Jeroen.van.den.Bos@cwi.nl (CWI)} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@synopsis{Exceptions thrown by the Rascal run-time} +module Exception + +@synopsis{The `Exception` datatype used in all Rascal exceptions.} +@description{ +This data type declares all exceptions that are thrown by the +Rascal run-time environment which can be caught by a Rascal program. + +Since declarations for data types are extensible, the user can add new exceptions when needed. +However, this is not necessary. The throw statement can throw any value. + +Exceptions are either generated by the Rascal run-time (e.g., `IndexOutOfBounds`) or they +are generated by a throw statement. They can all be caught with a try-catch using pattern matching. +} +@examples{ +Import relevant libraries: +```rascal-shell,continue +import Exception; +import IO; +``` +Define the map `weekend` and do a subscription with a non-existing key: +```rascal-shell,continue,error +weekend = ("saturday": 1, "sunday": 2); +weekend["monday"]; +``` +Repeat this, but catch the exception. We use variable `N` to track what happened: +```rascal-shell,continue +N = 1; +try { + N = weekend["monday"]; +} catch NoSuchKey(v): + N = 100; +println(N); +``` +} +data RuntimeException + = Ambiguity(loc location, str nonterminal, str sentence) + | ArithmeticException(str message) + | AssertionFailed() + | AssertionFailed(str label) + | CallFailed(list[value] arguments) + | DateTimeParsingError(str message) + | DateTimePrintingError(str message) + | EmptyList() + | EmptyMap() + | EmptySet() + | IndexOutOfBounds(int index) + | IllegalArgument() + | IllegalArgument(value v) + | IllegalArgument(value v, str message) + | IllegalTypeArgument(str cls, str message) + | ImplodeError(str message) + | InvalidURI(str uri) + | InvalidUseOfDate(str message) + | InvalidUseOfDateTime(str message) + | InvalidUseOfLocation(str message) + | InvalidUseOfTime(str message) + | IO(str message) + | Java(str class, str message) + // deprecated + | Java(str class, str message, RuntimeException cause) + // deprecated + | JavaException(str class, str message) + | JavaException(str class, str message, RuntimeException cause) + | JavaCompilation( + str message, int line, int column, str source, list[loc] classpath) + | MalFormedURI(str uri) + | ModuleNotFound(str name) + // Deprecated + | MultipleKey(value key, value first, value second) + | NoMainFunction() + | NoParent(loc location) + | NoSuchAnnotation(str label) + | NoSuchElement(value v) + | NoSuchField(str name) + | NoSuchKey(value key) + | NotImplemented(str message) + | ParseError(loc location) + // add versions of ParseError + | PathNotFound(loc location) + | PathNotFound(set[loc] locs) + | PermissionDenied() + | PermissionDenied(str message) + | RegExpSyntaxError(str message) + | SchemeNotSupported(loc location) + | StackOverflow() + | Timeout() + | UnavailableInformation() + ; +### |project://rascal/src/org/rascalmpl/library/Grammar.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl - CWI} +@contributor{Vadim Zaytsev - Vadim.Zaytsev@cwi.nl - CWI} +@synopsis{A simple but effective internal format for the representation of context-free grammars.} +module Grammar + +extend ParseTree; + +@synopsis{The Grammar datatype} +@description{ +Grammar is the internal representation (AST) of syntax definitions used in Rascal. +A grammar is a set of productions and set of start symbols. The productions are +stored in a map for efficient access. +} +data Grammar + = \grammar(set[Symbol] starts, map[Symbol sort, Production def] rules); + +data GrammarModule + = \module(str name, set[str] imports, set[str] extends, Grammar grammar); + +data GrammarDefinition + = \definition(str main, map[str name, GrammarModule \mod] modules); + +public Grammar grammar(set[Symbol] starts, set[Production] prods) { + map[Symbol, Production] rules = (); + + for (p <- prods) { + t = (p.def is label) ? p.def.symbol : p.def; + + if (t in rules) { + if (choice(_, existing) := rules[t]) { + rules[t] = choice(t, existing + p); + } + else { + rules[t] = choice(t, {p, rules[t]}); + } + } + else { + rules[t] = choice(t, {p}); + } + } + return grammar(starts, rules); +} + +Grammar grammar(type[&T <: Tree] sym) + = grammar({sym.symbol}, sym.definitions); + +@synopsis{An item is an index into the symbol list of a production rule.} +data Item = item(Production production, int index); + +@synopsis{Compose two grammars.} +@description{ +Compose two grammars by adding the rules of g2 to the rules of g1. +The start symbols of g1 will be the start symbols of the resulting grammar. +} +public Grammar compose(Grammar g1, Grammar g2) { + for (s <- g2.rules) + if (g1.rules[s]?) + g1.rules[s] = choice(s, {g1.rules[s], g2.rules[s]}); + else + g1.rules[s] = g2.rules[s]; + g1.starts += g2.starts; + + reduced_rules = (); + for (s <- g1.rules) { + c = g1.rules[s]; + if (c is choice) { + c.alternatives + -= {*choices| priority(_, choices) <- c.alternatives} + + {*alts| associativity(_, _, alts) <- c.alternatives}; + } + reduced_rules[s] = c; + } + + return grammar(g1.starts, reduced_rules); +} + +// TODO:COMPILER +// The above code (temporarily?) replaces the following code +// Reason: the algorithm is faster and compiled code chokes in the set matches +// for not yet known reason. +//public Grammar compose(Grammar g1, Grammar g2) { +// set[Production] empty = {}; +// for (s <- g2.rules) +// if (g1.rules[s]?) +// g1.rules[s] = choice(s, {g1.rules[s], g2.rules[s]}); +// else +// g1.rules[s] = g2.rules[s]; +// g1.starts += g2.starts; +// +// return innermost visit(g1) { +// case c:choice(_, {p, *r, Production x:priority(_,/p)}) => c[alternatives = {x, *r}] +// case c:choice(_, {p, *r, Production x:associativity(_,_,/p)}) => c[alternatives = {x, *r}] +// }; +//} +@synopsis{Compute a relation from extender to extended module for the given grammar} +@description{ +Note that this relation is already transitively closed because that is the semantics of extend. +} +rel[str \module, str extended] extends(GrammarDefinition def) { + return + {| m <- def.modules, \module(_, _, exts, _) := def.modules[m], e <- exts}+; +} + +@synopsis{Compute a relation from importer to imported modules for the given grammar} +rel[str \module, str imported] imports(GrammarDefinition def) { + return + {| m <- def.modules, \module(_, imps, _, _) := def.modules[m], i <- imps}; +} + +@synopsis{Compute which modules directly depend on which other modules.} +@description{ +This function computes dependencies via import and extend relations. Every module +X that imports Y or extends Y ends up in the result as . The extends relation +that we use is already transitively closed. Next to this we also add dependencies + for all modules X that import Y which extends Z. Because of the transitive +nature of module extension, a module that extends another module exposes all +rules to any importing module. +} +rel[str \module, str dependency] dependencies(GrammarDefinition def) { + imps = imports(def); + exts = extends(def); + + return imps + exts + imps o exts; +} +### |project://rascal/src/org/rascalmpl/library/IO.rsc| +@license{ + Copyright (c) 2009-2022 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Bas Basten - Bas.Basten@cwi.nl (CWI)} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Mark Hills - Mark.Hills@cwi.nl (CWI)} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@synopsis{Library functions for input/output.} +@description{ +The following input/output functions are defined: +(((TOC))) +} +module IO + +import Exception; +extend analysis::diff::edits::FileSystemChanges; + +@synopsis{All functions in this module that have a charset parameter use this as default.} +private str DEFAULT_CHARSET = "UTF-8" ; + +@synopsis{Register a logical file scheme including the resolution method via a table.} +@description{ +Logical source location schemes, such as `|java+interface://JRE/java/util/List|` are used for +precise qualified names of artifacts while abstracting from their physical location in a specific part +of a file on disk or from some webserver or source repository location. + +Using this function you can create your own schemes. The authority field is used for scoping the +names you wish to resolve to certain projects. This way one name can resolve to different locations +in different projects. +} +@benefits{ +* Logical source locations are supported by IDE features such as hyperlinks +* Logical source locations are supported by all IO functions as well +} +@pitfalls{ +* Repeated calls to registerLocations for the same `scheme` and `authority` will overwrite the `m` map. +* The registry is an intentional memory leak; so make sure you use it wisely. See also ((unregisterLocations)). +* When the files references by the physical locations are being written to (edited, removed), then you +may expect problems. The registry is not automatically invalidated. +} +@javaClass{org.rascalmpl.library.Prelude} +java void registerLocations( + str scheme, str authority, map[loc logical, loc physical] m +); + +@synopsis{Undo the effect of ((registerLocations))} +@description{ +For debugging or for memory management you may wish to remove a lookup table. +} +@javaClass{org.rascalmpl.library.Prelude} +java void unregisterLocations(str scheme, str authority); + +@javaClass{org.rascalmpl.library.Prelude} +java loc resolveLocation(loc l); + +@javaClass{org.rascalmpl.library.Prelude} +@synopsis{Finds files in the deployment locations of the configuration of the current runtime} +@description{ +* For the interpreter this looks at the source path and finds any file in there. +* For the compiler this looks at the classpath and finds resources in there. + +The goal of `findResources` is to access additional files that are distributed +with a Rascal program. For example, the `index.html` file that comes with a server implemented +using ((util::Webserver)) would be located using this function. Such an additional file is +essential to the functioning of the given Rascal code. + +There is a difference between Rascal programs under development and Rascal programs deployed +(in a jar file). The idea is that calls to this function _behave the same_ regardless of this +mode. To make this true, the implementation of `findResources` makes the following +assumptions: +* In interpreter mode, the additional files are stored relative to the source roots of the current project. +This means that all source roots are searched as configured for the current interpreter. It is possible +that multiple files match the query, hence the result set. Because jar files and target folders are typically +added to the source roots by the configuration code for Rascal's IDEs, this works out smoothly also for deployed projects. +* In compiled mode, the additional files are stored relative to the roots of the classpath. This +means they are jar files and target folders of active projects. Your Maven project configuration should +take care to _copy_ all relevant resources from source folders to target folders and to package those +relative to the root of the jar for this to work in development mode. For deployment mode, the setup +works through the resources API of the JVM that uses the Java ClassLoader API. +} +@benefits{ +By satisfying the above two assumptions it is possible to write Rascal code that uses `findResources` that +is portable between interpreter and compiled mode, _and_ does not change between development +and deployed mode either. +} +@pitfalls{ +* Avoid nesting resources in sub-folders in jar files (see Maven resources plugin) since nested files will not be found. +* Inside jar files the root for module names should be the same as for resources +* Inside source folders, the names of resources should be relative to the same root as the Rascal module names +* `findResources` searches exhaustively for all files with the given name; which may come at an expensive for projects with +large search paths. It makes sense to keep this in a constant module variable that is initialized only once. +* Name clashes are bound to happen. Prepare to, at least, throw a meaningful error message in the case of name clashes. E.g. `findResources("index.html")` +is _very_ likely to produce more than one result. Either choose more unique names, or filter the result in a meaningful manner, +or throw an exception that explains the situation to the user of your code. Specifically library project maintainers have to consider +this happenstance. The recommendation is to nest resources next to qualified module names, like so: if `lang/x/myLanguage/Syntax.rsc` +is a module name, then `lang/x/myLanguage/examples/myExampleFile.mL` is an example resource location. +} +java set[loc] findResources(str fileName); +set[loc] findResources(loc path) = findResources(path.path) + when path.scheme == "relative"; + +@synopsis{Search for a single resource instance, and fail if no or multiple instances are found} +@description{ +This is a utility wrapper around ((findResources)). +It processes the result set of ((findResources)) to: +* return a singleton location if the `fileName`` was found. +* throw an IO exception if no instances of `fileName` was found. +* throw an IO exception if multiple instances of `fileName` were found. +} +@benefits{ +* Save some code to unpack of the set that ((findResources)) produces. +} +@pitfalls{ +* ((getResource)) searches for all instances in the entire run-time context of the current +module. So if the search path (classpath) grows, new similar files may be added that match and this +function will start throwing IO exceptions. If you can influence the `fileName`, then make sure +to pick a name that is always going to be unique for the current project. +} +loc getResource(str fileName) throws IO { + result = findResources(fileName); + + switch(result) { + case {}: + throw IO(" not found"); + case {loc singleton}: + return singleton; + default: + throw IO(" found more than once: "); + } +} + +@synopsis{Append a value to a file.} +@description{ +Append a textual representation of some values to an existing or a newly created file: + +* If a value is a simple string, the quotes are removed and the contents are de-escaped. +* If a value has a non-terminal type, the parse tree is unparsed to produce a value. +* All other values are printed as-is. +* Each value is terminated by a newline character. + +The existing file can be stored using any character set possible. +If you know the character set, please use the `charset` keyword parameter. +Otherwise, the same method of deciding the character set is used as in ((readFile)). +} +@pitfalls{ +* The same encoding pitfalls as the ((readFile)) function. +} +@javaClass{org.rascalmpl.library.Prelude} +public +java void appendToFile( + loc file, + value V..., + str charset = DEFAULT_CHARSET, + bool inferCharset = !(charset?) +) throws PathNotFound, IO; + +@synopsis{Append a value to a file.} +@description{ +Append a textual representation of some values to an existing or a newly created file: + +* If a value is a simple string, the quotes are removed and the contents are de-escaped. +* If a value has a non-terminal type, the parse tree is unparsed to produce a value. +* All other values are printed as-is. +* Each value is terminated by a newline character. + +Files are encoded using the charset provided. +} +@deprecated{ +Use `appendToFile(file, V, charset=DEFAULT_CHARSET)` instead. +} +public +void appendToFileEnc( + loc file, + str charset, + value V... +) throws PathNotFound, IO + = appendToFile(file, V, charset = charset, inferCharset = false); + +@synopsis{Returns all available character sets.} +@javaClass{org.rascalmpl.library.Prelude} +public java set[str] charsets(); + +@synopsis{Returns whether this charset can be used for encoding (use with ((writeFile)))} +@javaClass{org.rascalmpl.library.Prelude} +public java set[str] canEncode(str charset); + +@synopsis{Print a value and return true.} +@description{ +Print a value and return `true`. This is useful for debugging complex Boolean expressions or comprehensions. +The only difference between this function and ((IO-println)) is that its return type is `bool` rather than `void`. +} +@examples{ +```rascal-shell +import IO; +bprintln("Hello World"); +``` +} +public bool bprintln(value arg) { + println(arg); + return true; +} + +@synopsis{Check whether a given location exists.} +@description{ +Check whether a certain location exists, i.e., whether an actual file is associated with it. +} +@examples{ +```rascal-shell +import IO; +``` + +Does the library file `IO.rsc` exist? +```rascal-shell,continue +exists(|std:///IO.rsc|); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool exists(loc file); + +@synopsis{Find a named file in a list of locations.} +@examples{ +```rascal-shell +import IO; +``` +Find the file `IO.rsc` in the standard library: +```rascal-shell,continue +find("IO.rsc", [|std:///|]); +``` +} +public loc find(str name, list[loc] path) throws PathNotFound { + if (dir <- path, f := dir + "/", exists(f)) { + return f; + } + throw PathNotFound({dir + "/"| dir <- path}); +} + +@synopsis{Check whether a given location is a directory.} +@description{ +Check whether the location `file` is a directory. +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool isDirectory(loc file); + +@synopsis{Print an indented representation of a value.} +@description{ +See ((IO-iprintExp)) for a version that returns its argument as result +and ((IO-iprintln)) for a version that adds a newline +and ((IO-iprintToFile)) for a version that prints to a file. +} +@examples{ +```rascal-shell +import IO; +iprint(["fruits", ("spider" : 8, "snake" : 0), [10, 20, 30]]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java void iprint(value arg, int lineLimit = 1000); + +@synopsis{Print an indented representation of a value to the specified location.} +@description{ +See ((IO-iprint)) for a version that displays the result on the console +and ((IO-iprintExp)) for a version that returns its argument as result +and ((IO-iprintln)) for a version that adds a newline. +} +@examples{ +```rascal-shell +import IO; +iprintToFile(|file:///tmp/fruits.txt|, ["fruits", ("spider" : 8, "snake" : 0), [10, 20, 30]]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public +java void iprintToFile(loc file, value arg, str charset = DEFAULT_CHARSET); + +@javaClass{org.rascalmpl.library.Prelude} +public java str iprintToString(value arg); + +@synopsis{Print an indented representation of a value and returns the value as result.} +@description{ +See ((IO-iprintlnExp)) for a version that adds a newline. +} +@examples{ +```rascal-shell +import IO; +iprintExp(["fruits", ("spider" : 8, "snake" : 0), [10, 20, 30]]); +``` +} +public &T iprintExp(&T v) { + iprint(v); + return v; +} + +@synopsis{Print an indented representation of a value followed by a newline and returns the value as result.} +@description{ +See ((IO-iprintExp)) for a version that does not add a newline. +} +@examples{ +```rascal-shell +import IO; +iprintlnExp(["fruits", ("spider" : 8, "snake" : 0), [10, 20, 30]]); +``` +} +public &T iprintlnExp(&T v) { + iprintln(v); + return v; +} + +@synopsis{Print a indented representation of a value and add a newline at the end.} +@description{ +See ((IO-iprintlnExp)) for a version that returns its argument as result +and ((IO-iprint)) for a version that does not add a newline. + +By default we only print the first 1000 lines, if you want to print larger values, either +use ((ValueIO-writeTextValueFile)) or change the limit with the lineLimit parameter. +} +@examples{ +```rascal-shell +import IO; +iprintln(["fruits", ("spider" : 8, "snake" : 0), [10, 20, 30]]); +iprintln([ {"hi"} | i <- [0..1000]], lineLimit = 10); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java void iprintln(value arg, int lineLimit = 1000); + +@synopsis{Check whether a given location is actually a file (and not a directory).} +@description{ +Check whether location `file` is actually a file. +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool isFile(loc file); + +@synopsis{Check whether a given location is a readable file} +@description{ +Tries to predict whether a `file` is readable. +If the file does not exist, this will throw an exception +} +@pitfalls{ +There are (rare) cases where it will return `true`` and ((readFile)) will still throw an exception (and even less likely but possible for the inverse). +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool isReadable(loc file) throws PathNotFound; + +@synopsis{Check whether a given location is a writable file (and not readonly/non-existing).} +@description{ +Tries to predict whether a file `file` is writable. +If the file does not exist, this will throw an exception +} +@pitfalls{ +There are cases where it will return `true`` and ((writeFile)) will still throw an exception (and even less likely but possible for the inverse). +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool isWritable(loc file) throws PathNotFound; + +@synopsis{Last modification date of a location.} +@description{ +Returns last modification time of the file at location `file`. +} +@examples{ +```rascal-shell +import IO; +``` +Determine the last modification date of the Rascal standard library: +```rascal-shell,continue +lastModified(|std:///IO.rsc|); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime lastModified(loc file); + +@synopsis{Creation datetime of a location.} +@description{ +Returns the creation time of the file at location `file`. +} +@examples{ +```rascal-shell +import IO; +``` +Determine the last modification date of the Rascal standard library: +```rascal-shell,continue +created(|std:///IO.rsc|); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java datetime created(loc file); + +@synopsis{Set the modification date of a file to `now` or create the file if it did not exist yet} +@javaClass{org.rascalmpl.library.Prelude} +java void touch(loc file); + +@synopsis{Set the modification date of a file to the timestamp} +@javaClass{org.rascalmpl.library.Prelude} +java void setLastModified(loc file, datetime timestamp); + +@synopsis{List the entries in a directory.} +@description{ +List the entries in directory `file`. +} +@examples{ +```rascal-shell +import IO; +``` +List all entries in the standard library: +```rascal-shell,continue +listEntries(|std:///|); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java list[str] listEntries(loc file); + +@synopsis{Create a new directory.} +@description{ +Create a directory at location `file`. +} +@javaClass{org.rascalmpl.library.Prelude} +public java void mkDirectory(loc file) throws PathNotFound, IO; + +@synopsis{Print a value without subsequent newline.} +@description{ +Print a value on the output stream. +See ((IO-println)) for a version that adds a newline +and ((IO-printExp)) for a version that returns its argument as value. +} +@examples{ +Note that the only difference with ((IO-println)) is that no newline is added after the value is printed +```rascal-shell +import IO; +print("Hello World"); +``` + +NOTE: Since `print` does not add a newline, the prompt `ok` appears at a weird place, i.e., +glued to the output of `print`. +} +@javaClass{org.rascalmpl.library.Prelude} +public java void print(value arg); + +@synopsis{Print a value and return it as result.} +@examples{ +```rascal-shell +import IO; +printExp(3.14); +printExp("The value of PI is approximately ", 3.14); +``` +} +public &T printExp(&T v) { + print(""); + return v; +} + +public &T printExp(str msg, &T v) { + print(""); + return v; +} + +@synopsis{Print a value to the output stream and add a newline.} +@description{ +Print a value on the output stream followed by a newline. +See ((IO-print)) for a version that does not add a newline +and ((IO-printlnExp)) for a version that returns its argument as value. +} +@examples{ +```rascal-shell +import IO; +println("Hello World"); +``` +Introduce variable S and print it: +```rascal-shell,continue +S = "Hello World"; +println(S); +``` +Introduce variable L and print it: +```rascal-shell,continue +L = ["a", "b", "c"]; +println(L); +``` +Use a string template to print several values: +```rascal-shell,continue +println(": "); +``` +Just print a newline +```rascal-shell,continue +println(); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java void println(value arg); + +@javaClass{org.rascalmpl.library.Prelude} +public java void println(); + +@synopsis{Print a value followed by a newline and return it as result.} +@examples{ +```rascal-shell +import IO; +printlnExp(3.14); +printlnExp("The value of PI is approximately ", 3.14); +``` +NOTE: Since `printExp` does no produce a newline after its output, the result prompt `real: 3.14` is glued to the +output of `printExp`. +} +public &T printlnExp(&T v) { + println(""); + return v; +} + +public &T printlnExp(str msg, &T v) { + println(""); + return v; +} + +@synopsis{Raw print of a value.} +@description{ + +} +@pitfalls{ +This function is only available for internal use in the Rascal development team. +} +@javaClass{org.rascalmpl.library.Prelude} +public java void rprint(value arg); + +@synopsis{Raw print of a value followed by newline.} +@description{ + +} +@pitfalls{ +This function is only available for internal use in the Rascal development team. +} +@javaClass{org.rascalmpl.library.Prelude} +public java void rprintln(value arg); + +@synopsis{Read the contents of a location and return it as string value.} +@description{ +Return the contents of a file location as a single string. +Also see ((readFileLines)). +} +@encoding{ +A text file can be encoded in many different character sets, most common are UTF8, ISO-8859-1, and ASCII. +If you know the encoding of the file, please use the ((readFileEnc)) and ((readFileLinesEnc)) overloads. +If you do not know, we try to detect this. This detection is explained below: + +* If the implementation of the used scheme in the location + (e.g.,`|project:///|`) defines the charset of the file then this is used. +* Otherwise if the file contains a UTF8/16/32 [BOM](http://en.wikipedia.org/wiki/Byte_order_mark), + then this is used. +* As a last resort the IO library uses heuristics to determine if UTF-8 or UTF-32 could work: + ** Are the first 32 bytes valid UTF-8? Then use UTF-8. + ** Are the first 32 bytes valid UTF-32? Then use UTF-32. +* Finally, we fall back to the system default (as given by the Java Runtime Environment). + +*To summarize*, we use UTF-8 by default, except if the text that the location points to has available meta-data, the file contains a BOM, or +the first 32 bytes of the file are not valid UTF-8. +} +@pitfalls{ +* In case encoding is not known, we try to estimate as best as we can. +* We default to UTF-8, if the file was not encoded in UTF-8 but the first characters were valid UTF-8, + you might get an decoding error or just strange looking characters. +} +@javaClass{org.rascalmpl.library.Prelude} +public +java str readFile( + loc file, + str charset = DEFAULT_CHARSET, + bool inferCharset = !(charset?) +) throws PathNotFound, IO; + +@synopsis{Read the contents of a location and return it as string value.} +@description{ +Return the contents (decoded using the Character set supplied) of a file location as a single string. +Also see ((readFileLinesEnc)). +} +@deprecated{ +Use `readFile(file, inferCharset=false, charset=DEFAULT_CHARSET)` instead. +} +public +str readFileEnc(loc file, str charset) throws PathNotFound, IO + = readFile(file, inferCharset = false, charset = charset); + +@synopsis{Read the content of a file and return it as a base-64 encoded string.} +@description{ +} +@javaClass{org.rascalmpl.library.Prelude} +public +java str readBase64( + loc file, + bool includePadding = true +) throws PathNotFound, IO; + +@deprecated{ +Use readBase64 instead. +} +public str uuencode(loc file) = readBase64(file); + +@synopsis{Decode a base-64 encoded string and write the resulting bytes to a file.} +@description{ +} +@javaClass{org.rascalmpl.library.Prelude} +public java void writeBase64(loc file, str content) throws PathNotFound, IO; + +@deprecated{ +Use writeBase64 instead. +} +public void uudecode(loc file, str content) = writeBase64(file, content); + +@synopsis{Read the content of a file and return it as a base-32 encoded string.} +@description{ +} +@javaClass{org.rascalmpl.library.Prelude} +public +java str readBase32( + loc file, + bool includePadding = true +) throws PathNotFound, IO; + +@synopsis{Decode a base-32 encoded string and write the resulting bytes to a file.} +@description{ +} +@javaClass{org.rascalmpl.library.Prelude} +public java void writeBase32(loc file, str content) throws PathNotFound, IO; + +@synopsis{Read the contents of a file and return it as a list of bytes.} +@javaClass{org.rascalmpl.library.Prelude} +public java list[int] readFileBytes(loc file) throws PathNotFound, IO; + +@synopsis{Read the contents of a file location and return it as a list of strings.} +@description{ +Return the contents of a file location as a list of lines. +Also see ((readFile)). +} +@encoding{ +Look at ((readFile)) to understand how this function chooses the character set. If you know the character set used, please use ((readFileLinesEnc)). +} +@pitfalls{ +* In case encoding is not known, we try to estimate as best as we can (see [readFile]). +* We default to UTF-8, if the file was not encoded in UTF-8 but the first characters were valid UTF-8, + you might get an decoding error or just strange looking characters (see ((readFile))). +} +@javaClass{org.rascalmpl.library.Prelude} +public +java list[str] readFileLines( + loc file, + str charset = DEFAULT_CHARSET +) throws PathNotFound, IO; + +@synopsis{Writes a list of strings to a file, where each separate string is ended with a newline} +@benefits{ +* mirrors ((readFileLines)) in its functionality +} +@pitfalls{ +* if the individual elements of the list also contain newlines, the output may have more lines than list elements +} +public +void writeFileLines(loc file, list[str] lines, str charset = DEFAULT_CHARSET) { + writeFile(file, "<for (str line <- lines) {> + '<}>", charset = charset); +} + +@synopsis{Read the contents of a file location and return it as a list of strings.} +@description{ +Return the contents (decoded using the Character set supplied) of a file location as a list of lines. +Also see ((readFileLines)). +} +@deprecated{ +Use `readFileLines(file, charset=DEFAULT_CHARSET)` instead. +} +public +list[str] readFileLinesEnc(loc file, str charset) throws PathNotFound, IO + = readFileLines(file, charset = charset); + +@javaClass{org.rascalmpl.library.Prelude} +@synopsis{Remove files or directories} +public java void remove(loc file, bool recursive = true) throws IO; + +@javaClass{org.rascalmpl.library.Prelude} +@synopsis{Rename files or directories} +@benefits{ +* will rename between schemes and within schemes, seamlessly. +* within schemes renaming is implemented as close to the operating system rename functionality as possible. This can be very fast. +} +@pitfalls{ +* Between-scheme renaming can cause large recursive copy operations: + - that can take a lot more time + - if interrupted on the OS level will leave semi-copied target files and not-yet-removed origins +} +public java void rename(loc from, loc to, bool overwrite = false) throws IO; + +@synopsis{Write values to a file.} +@description{ +Write a textual representation of some values to a file: + +* If a value is a simple string, the quotes are removed and the contents are de-escaped. +* If a value has a non-terminal type, the parse tree is unparsed to produce a value. +* All other values are printed as-is. +* Each value is terminated by a newline character. + +Files are encoded in UTF-8, in case this is not desired, use ((writeFileEnc)). +} +@javaClass{org.rascalmpl.library.Prelude} +public +java void writeFile( + loc file, + value V..., + str charset = DEFAULT_CHARSET +) throws PathNotFound, IO; + +@synopsis{Write a list of bytes to a file.} +@javaClass{org.rascalmpl.library.Prelude} +public +java void writeFileBytes(loc file, list[int] bytes) throws PathNotFound, IO; + +@synopsis{Write values to a file.} +@description{ +Write a textual representation of some values to a file: + +* If a value is a simple string, the quotes are removed and the contents are de-escaped. +* If a value has a non-terminal type, the parse tree is unparsed to produce a value. +* All other values are printed as-is. +* Each value is terminated by a newline character. + +Files are encoded using the charset provided. +} +@deprecated{ +Use `writeFile(file, charset=...)` instead. +} +public +void writeFileEnc(loc file, str charset, value V...) throws PathNotFound, IO + = writeFile(file, V, charset = charset); + +@synopsis{Read the contents of a location and return its MD5 hash.} +@description{ +MD5 hash the contents of a file location. +} +@javaClass{org.rascalmpl.library.Prelude} +public java str md5HashFile(loc file) throws PathNotFound, IO; + +@javaClass{org.rascalmpl.library.Prelude} +public java str md5Hash(value v); + +@javaClass{org.rascalmpl.library.Prelude} +public java str createLink(str title, str target); + +@javaClass{org.rascalmpl.library.Prelude} +@deprecated{ +Use `readBase64` instead. +} +public +java str toBase64(loc file, bool includePadding = true) throws PathNotFound, IO; + +@javaClass{org.rascalmpl.library.Prelude} +java void copy( + loc source, + loc target, + bool recursive = false, + bool overwrite = true +) throws IO; + +@deprecated{ +Use the `copy` function instead +} +void copyFile(loc source, loc target) { + copy(source, target, recursive = false, overwrite = true); +} + +@deprecated{ +Use the `copy` function instead +} +void copyDirectory(loc source, loc target) { + copy(source, target, recursive = true, overwrite = true); +} + +@javaClass{org.rascalmpl.library.Prelude} +@synopsis{Is an alias for ((rename))} +java void move(loc source, loc target, bool overwrite = true) throws IO; + +@javaClass{org.rascalmpl.library.Prelude} +java loc arbLoc(); + +@javaClass{org.rascalmpl.library.Prelude} +java void watch( + loc src, bool recursive, void(FileSystemChange event) watcher +); + +@javaClass{org.rascalmpl.library.Prelude} +java void unwatch( + loc src, bool recursive, void(FileSystemChange event) watcher +); + +@synopsis{Categories of IO capabilities for loc schemes} +@description{ +* ((reading)) includes at least these functions: + * ((readFile)) + * ((lastModified)) and ((created)) + * ((exists)) + * ((isFile)) and ((isDirectory)) + * `loc.ls`, and ((listEntries)) +* ((writing)) includes at least these functions: + * ((writeFile)) + * ((mkDirectory)) + * ((remove)) + * ((rename)) and ((move)) +* ((classloading)) means that for this scheme a specialized/optimized ClassLoader can be produced at run-time. By +default an abstract (slower) ClassLoader can be built using `read` capabilities on `.class` files. +* ((watching)) means that for this scheme a file watcher can be instantiated. By default +a slower more generic file watcher is created based on capturing `write` actions. + * ((resolving)) schemes provide a transparent facade for more abstract URIs. The abstract URI is +rewritten to a more concrete URI on-demand. Logical URIs can be nested arbitrarily. + +These capabilities extend naturally to other IO functions that use the internal versions of the above functionality, +such as used in ((ValueIO)) and ((lang::json::IO)), etc. +} +@pitfalls{ +* IO capabilities are not to be confused with file _permissions_. It is still possible that a file +has write capabilities, but writing is denied by the OS due to a lack of permissions. +} +data IOCapability + = reading() + | writing() + | classloading() + | resolving() + | watching() + ; + +@javaClass{org.rascalmpl.library.Prelude} +@synopsis{List the IO capabilities of a loc (URI) scheme} +java set[IOCapability] capabilities(loc location); +### |project://rascal/src/org/rascalmpl/library/List.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Vadim Zaytsev - vadim@grammarware.net - UvA} +@synopsis{Library functions for lists.} +@description{ +The following library functions are available for lists: +(((TOC))) +} +module List + +import Exception; +import Map; +import String; + +@synopsis{Concatenate a list of lists.} +@examples{ +```rascal-shell +import List; +concat([]); +concat([[]]); +concat([[1]]); +concat([[1],[],[2,3]]); +concat([[1,2],[3],[4,5],[]]); +``` +} +list[&T] concat(list[list[&T]] xxs) = [*xs | list[&T] xs <- xxs]; + +@synopsis{Delete an element from a list.} +@description{ +Delete the `n`-th element from a list. A new list without the `n`-th element is returned as result. +The `IndexOutOfBounds` exception is thrown when n is not a valid index. +} +@examples{ +```rascal-shell +import List; +delete([1, 2, 3], 1); +delete(["zebra", "elephant", "snake", "owl"], 2); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] delete(list[&T] lst, int n); + +@synopsis{Get the distribution of the elements of the list. That +is how often does each element occur in the list?} +@examples{ +```rascal-shell +import List; +distribution([4,4,4,3,1,2,1,1,3,4]); +``` +} +map[&T element, int occurs] distribution(list[&T] lst) { + map[&T element, int occurs] res = (); + for (e <- lst) { + res[e] ? 0 += 1; + } + return res; +} + +@synopsis{Drop elements from the head of a list.} +@description{ +Drop `n` elements (or `size(lst)` elements if `size(lst) < n`) from the head of `lst`. +See ((List-take)) to get elements from the head of a list]. +} +@examples{ +```rascal-shell +import List; +drop(2, [5, 1, 7, 3]); +drop(10, [5, 1, 7, 3]); +drop(2, ["zebra", "elephant", "snake", "owl"]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] drop(int n, list[&T] lst); + +@synopsis{Remove multiple occurrences of elements in a list. The first occurrence remains.} +@examples{ +```rascal-shell +import List; +dup([3, 1, 5, 3, 1, 7, 1, 2]); +``` +} +list[&T] dup(list[&T] lst) = ( [] | (ix in it) ? it : it + [ix] | &T ix <- lst ); + +@javaClass{org.rascalmpl.library.Prelude} +@synopsis{A function that implements `lst[index]`} +@description{ +The expression `lst[index]` has the same semantics as calling `elementAt(index)`. +} +@benefits{ +* ((elementAt)) can be passed a function argument. +} +@pitfalls{ +* `lst[index]` is significantly faster than `elementAt(index)` +} +java &T elementAt(list[&T] lst, int index); + +@synopsis{Pick a random element from a list.} +@description{ +Get an arbitrary element from a list. See ((List-takeOneFrom)) for a function that also removes the selected element. +} +@examples{ +```rascal-shell +import List; +getOneFrom(["zebra", "elephant", "snake", "owl"]); +getOneFrom(["zebra", "elephant", "snake", "owl"]); +getOneFrom(["zebra", "elephant", "snake", "owl"]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java &T getOneFrom(list[&T] lst); + +@synopsis{Pick first element from a list.} +@description{ +Get the first element from a list. As opposed to ((List-getOneFrom)) this function always returns the same (first) list element. +} +&T getFirstFrom([&T f, *&T _]) = f; +&T getFirstFrom(list[&T] _: []) { + throw EmptyList(); +} + +@synopsis{Get the first element(s) from a list.} +@description{ +* Returns the first element of a list or throws `EmptyList` when the list is empty. + This is identical to ((List-top)). +* Returns the first `n` elements of a list or throws `IndexOutOfBounds` when the list is too short. + This is similar to ((take)). +} +@examples{ +```rascal-shell +import List; +``` +Get the first element: +```rascal-shell,continue +head([1, 2, 3]); +head(["zebra", "elephant", "snake", "owl"]); +``` +An exception is thrown when taking the head of an empty list: +```rascal-shell,continue,error +head([]); +``` +Get the first n elements: +```rascal-shell,continue +head([1, 2, 3, 4], 2); +head(["zebra", "elephant", "snake", "owl"], 2); +``` +An exception is thrown when the second argument exceeds the length of the list: +```rascal-shell,continue,error +head([1, 2, 3, 5], 5); +``` +} +&T head([&T h, *&T _]) = h; +&T head(list[&T] _: []) { + throw EmptyList(); +} + +// Get the first n elements of a list +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] head(list[&T] lst, int n) throws IndexOutOfBounds; + +@synopsis{Split a list in a head and a tail.} +@description{ +This function is identical to ((List-pop)). +} +@examples{ +```rascal-shell +import List; +headTail([3, 1, 4, 5]); +pop([3, 1, 4, 5]); +headTail(["zebra", "elephant", "snake", "owl"]); +``` +} +tuple[&T, list[&T]] headTail([&T h, *&T t]) = ; +tuple[&T, list[&T]] headTail(list[&T] _: []) { + throw EmptyList(); +} + +@synopsis{A list of legal index values of a list.} +@description{ +Returns a list of all legal index values for a given list `lst`. +} +@examples{ +```rascal-shell +import List; +index([1, 3, 5]); +index(["zebra", "elephant", "snake", "owl"]); +``` +} +@benefits{ +This function is useful in for loops over lists. +} +list[int] index(list[&T] lst) = upTill(size(lst)); + +@synopsis{Index of first occurrence of an element in a list.} +@description{ +Return index of first occurrence of `elt` in `lst`, or `-1` if `elt` is not found. +Also see ((List-lastIndexOf)). +} +@examples{ +```rascal-shell +import List; +indexOf([3, 1, 4, 5], 4); +indexOf([3, 1, 4, 5], 7); +indexOf(["zebra", "elephant", "snake", "owl"], "snake"); +indexOf(["zebra", "elephant", "snake", "owl"], "eagle"); +``` +} +int indexOf(list[&T] lst, &T elt) { + for (int i <- [0..size(lst)]) { + if (lst[i] == elt) + return i; + } + return -1; +} + +@synopsis{Insert an element at a specific position in a list.} +@description{ +Returns a new list with the value of `elm` inserted at index position `n` of the old list. +} +@examples{ +```rascal-shell +import List; +insertAt([1,2,3], 1, 5); +insertAt(["zebra", "elephant", "snake", "owl"], 2, "eagle"); +``` +An exception is thrown when the index position is outside the list: +```rascal-shell,continue,error +insertAt([1,2,3], 10, 5); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] insertAt(list[&T] lst, int n, &T elm) throws IndexOutOfBounds; + +@synopsis{Join a list of values into a string separated by a separator.} +@examples{ +```rascal-shell +import List; +intercalate("/", [3]); +intercalate("/", [3, 1, 4, 5]); +intercalate(", ", [3, 1, 4, 5]); +intercalate(", ", ["zebra", "elephant", "snake", "owl"]); +``` +} +str intercalate(str sep, list[value] l) + = "<for (value e <- l) {><}>"[..-size(sep)]; + +@synopsis{Intersperses a list of values with a separator.} +@examples{ +```rascal-shell +import List; +intersperse(", ", ["a","b","c"]); +intersperse(0, [1, 2, 3]); +intersperse(1, []); +intersperse([], [1]); +``` +} +list[&T] intersperse(&T sep, list[&T] xs) = [x, sep | &T x <- xs][..-1]; + +@synopsis{Test whether a list is empty.} +@description{ +Returns `true` when a list is empty and `false` otherwise. +} +@examples{ +```rascal-shell +import List; +isEmpty([]); +isEmpty([1, 2, 3]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java bool isEmpty(list[&T] lst); + +@synopsis{Return the last element of a list, if any.} +@description{ +Also see ((List-tail)) that returns a list of one or more of the last elements of a list. +} +@examples{ +```rascal-shell +import List; +last([1]); +last([3, 1, 4, 5]); +last(["zebra", "elephant", "snake", "owl"]); +tail([3, 1, 4, 5]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java &T last(list[&T] lst) throws EmptyList; + +@synopsis{Return index of last occurrence of elt in lst, or -1 if elt is not found.} +@description{ +Also see ((List-indexOf)). +} +@examples{ +```rascal-shell +import List; +lastIndexOf([3, 1, 4, 5, 4], 4); +lastIndexOf([3, 1, 4, 5, 4], 7); +lastIndexOf(["zebra", "owl", "elephant", "snake", "owl"], "owl"); +``` +} +int lastIndexOf(list[&T] lst, &T elt) { + for (i <- reverse(index(lst))) { + if (lst[i] == elt) + return i; + } + return -1; +} + +@synopsis{Apply a function to all list elements and return list of results.} +@description{ +Apply a function `fn` to each element of `lst` and return the list of results. +} +@examples{ +```rascal-shell +import List; +int incr(int x) { return x + 1; } +mapper([1, 2, 3, 4], incr); +``` +} +list[&U] mapper(list[&T] lst, &U(&T) fn) = [fn(elm) | &T elm <- lst]; + +@synopsis{Determine the largest element in a list.} +@examples{ +```rascal-shell +import List; +max([1, 3, 5, 2, 4]); +max(["zebra", "elephant", "snake", "owl"]); +``` +} +&T max([&T h, *&T t]) = ( h | e > it ? e : it | e <- t ); +&T max(list[&T] _: []) { + throw EmptyList(); +} + +@synopsis{Merge the elements of two sorted lists into one list.} +@description{ +Merge the elements of two sorted lists into one list using the built-in ordering between values. +Optional, a comparison function `lessOrEqual` may be given for a user-defined ordering between values. +} +@examples{ +```rascal-shell +import List; +merge([1, 3, 5], [2, 7, 9, 15]); +merge(["ape", "elephant", "owl", "snail", "zebra"], ["apple", "berry", "orange", "pineapple"]); +``` +Merge two lists of strings and use their length as ordering: +```rascal-shell,continue +import String; +merge(["ape", "owl", "snail", "zebra", "elephant"], ["apple", "berry", "orange", "pineapple"], bool(str x, str y){ return size(x) <= size(y); }); +``` +} +list[&T] merge(list[&T] left, list[&T] right) { + res = while(!isEmpty(left) && !isEmpty(right)) { + if (head(left) <= head(right)) { + append head(left); + left = tail(left); + } + else { + append head(right); + right = tail(right); + } + } + return res + left + right; +} + +list[&T] merge(list[&T] left, list[&T] right, bool(&T a, &T b) lessOrEqual) { + res = while(!isEmpty(left) && !isEmpty(right)) { + if (lessOrEqual(head(left), head(right))) { + append head(left); + left = tail(left); + } + else { + append head(right); + right = tail(right); + } + } + return res + left + right; +} + +@synopsis{Determine the smallest element in a list.} +@examples{ +```rascal-shell +import List; +min([1, 3, 5, 2, 4]); +min(["zebra", "elephant", "snake", "owl"]); +``` +} +&T min([&T h, *&T t]) = ( h | e < it ? e : it | e <- t ); +&T min(list[&T] _: []) { + throw EmptyList(); +} + +@synopsis{Mix the elements of two lists.} +@description{ +Let n be the minimum of the length of the two lists `l` and `r`. +`mix` returns a list in which the first `n` elements are taken alternately from the left and the right list, +followed by the remaining elements of the longest list. +} +@examples{ +```rascal-shell +import List; +mix([3, 1, 7, 5, 9], [15, 25, 35]); +mix([3, 1, 7], [15, 25, 35, 45, 55]); +mix([3, 1, 7], ["elephant", "snake"]); +``` +} +list[&T] mix(list[&T] l, list[&T] r) { + sizeL = size(l); + sizeR = size(r); + minSize = sizeL < sizeR ? sizeL : sizeR; + return [l[i], r[i] | i <- [0..minSize]] + drop(sizeR, l) + drop(sizeL, r); +} + +@synopsis{Compute all permutations of a list.} +@examples{ +```rascal-shell +import List; +permutations([1,2,3]); +``` +} +set[list[&T]] permutations(list[&T] lst) + = permutationsBag(distribution(lst)); + +private +set[list[&T]] permutationsBag(map[&T element, int occurs] b) + = isEmpty(b) + ? {[]} + : {[e] + rest| e <- b, rest <- permutationsBag(removeFromBag(b, e))}; + +@synopsis{Pop top element from list, return a tuple.} +@description{ +This function is identical to ((headTail)). +Also see ((List-push)) and ((List-top)). +} +@examples{ +```rascal-shell +import List; +pop([3, 1, 4, 5]); +headTail([3, 1, 4, 5]); +pop(["zebra", "elephant", "snake", "owl"]); +``` +} +tuple[&T, list[&T]] pop(list[&T] lst) = headTail(lst); + +@synopsis{Return all but the last element of a list.} +@examples{ +```rascal-shell +import List; +prefix([3, 1, 4, 5]); +prefix([]); +prefix(["zebra", "elephant", "snake", "owl"]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] prefix(list[&T] lst); + +@synopsis{Push an element in front of a list.} +@description{ +Also see ((List-pop)) and ((List-top)). +} +@examples{ +```rascal-shell +import List; +push(7, [3, 1, 4, 5]); +push("eagle", ["zebra", "elephant", "snake", "owl"]); +``` +} +list[&T] push(&T elem, list[&T] lst) = [elem] + lst; + +@synopsis{Apply a function to successive elements of list and combine the results.} +@description{ +Apply the function `fn` to successive elements of list `lst` starting with `unit`. +The function application `reducer(lst, add, 0)` has the same semantics +as the expression `(0 | add(it, e) | e <- lst)`. +} +@examples{ +```rascal-shell +import List; +int add(int x, int y) { return x + y; } +reducer([10, 20, 30, 40], add, 0); +``` +} +@benefits{ +* reducer can be passed as a function argument +} +@pitfalls{ +* a reducer expression can be a lot faster +* reducer expressions are more versatile (allowing multiple generators and filters) +} +&T reducer(list[&T] lst, &T(&T, &T) fn, &T unit) = ( unit | fn(it, elm) | elm <- lst ); + +list[&T] remove(list[&T] lst, int indexToDelete) + = [lst[i] | i <- index(lst), i != indexToDelete]; + +private +map[&T element, int occurs] removeFromBag(map[&T element, int occurs] b, &T el) + = removeFromBag(b, el, 1); + +private +map[&T element, int occurs] removeFromBag( + map[&T element, int occurs] b, &T el, int nr +) + = !(b[el]?) ? b : (b[el] <= nr ? b - (el : b[el]) : b + (el : b[el] - nr)); + +@synopsis{Reverse a list.} +@description{ +Returns a list with the elements of `lst` in reverse order. +} +@examples{ +```rascal-shell +import List; +reverse([1,4,2,3]); +reverse(["zebra", "elephant", "snake", "owl"]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] reverse(list[&T] lst); + +@synopsis{Determine the number of elements in a list.} +@examples{ +```rascal-shell +import List; +size([20, 10, 30]); +size(["zebra", "elephant", "snake", "owl"]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java int size(list[&T] lst); + +@synopsis{Compute a sublist of a list.} +@description{ +Returns a sublist of `lst` from index `start` of length `len`. + +:::warning +In most cases it is better to use the built-in slice notation, +see the example below. +::: +} +@examples{ +```rascal-shell +import List; +slice([10, 20, 30, 40, 50, 60], 2, 3); +slice(["zebra", "elephant", "snake", "owl"], 1, 2); +``` +Here are the equivalent expressions using the slice notation: +```rascal-shell +[10, 20, 30, 40, 50, 60][2 .. 5]; +["zebra", "elephant", "snake", "owl"][1 .. 3]; +``` +WARNING: In the slice notation the upper bound is exclusive. +} +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] slice(list[&T] lst, int begin, int len); + +@synopsis{Sort the elements of a list.} +@description{ +Sort the elements of a list: + +* Use the built-in ordering on values to compare list elements. +* Give an additional `lessThan` function that will be used to compare elements. +} +@examples{ +```rascal-shell +import List; +import String; +sort([10, 4, -2, 11, 100, 5]); +fruits = ["mango", "strawberry", "pear", "pineapple", "banana", "grape", "kiwi"]; +sort(fruits); +sort(fruits, bool(str a, str b){ return size(a) > size(b); }); +``` +} +list[&T] sort(list[&T] lst) = sort(lst, bool (&T a, &T b) { + return a < b; + }); + +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] sort(list[&T] l, bool(&T a, &T b) less); + +@synopsis{Check whether a list is sorted or not.} +@description{ +Checks whether or not a list is sorted by searching for any out-of-order elements. +The empty list is defined to be "sorted" and what sorted means is defined the +higher-order parameter "less" which should implement a partial-order relation +between the two parameters. +} +bool isSorted(list[&T] l, bool(&T a, &T b) less = bool (&T a, &T b){ return a < b; }) + = !any([*_, &T a, &T b, *_] := l, less(b, a)); + +@synopsis{Shuffle a list.} +@description{ +Returns a random (unbiased) shuffled list. +} +@examples{ +```rascal-shell +import List; +shuffle([1,4,2,3]); +shuffle(["zebra", "elephant", "snake", "owl"]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] shuffle(list[&T] l); + +@synopsis{Shuffle a list with a seed.} +@description{ +Returns a random (unbiased) shuffled list, every call with the same seed shuffles in the same order. +} +@examples{ +```rascal-shell +import List; +shuffle([1,2,3,4]); +shuffle([1,2,3,4]); +shuffle([1,2,3,4], 1); +shuffle([1,2,3,4], 1); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] shuffle(list[&T] l, int seed); + +@synopsis{Split a list into two halves.} +@examples{ +```rascal-shell +import List; +split([3, 1, 4, 5, 7]); +split(["zebra", "elephant", "snake", "owl"]); +``` +} +tuple[list[&T], list[&T]] split(list[&T] l) { + half = size(l) / 2; + return ; +} + +@synopsis{Groups sublists for consecutive elements which are `similar`} +@description{ +This function does not change the order of the elements. Only elements +which are similar end-up in a sub-list with more than one element. The +elements which are not similar to their siblings, end up in singleton +lists. +} +@examples{ +```rascal-shell +import List; +bool bothEvenOrBothOdd(int a, int b) = (a % 2 == 0 && b % 2 == 0) || (a % 2 == 1 && b % 2 == 1); +group([1,7,3,6,2,9], bothEvenOrBothOdd); +``` +} +public list[list[&T]] group(list[&T] input, bool(&T a, &T b) similar) { + lres = while([hd, *tl] := input) { + sim = [hd, *takeWhile(tl, bool (&T a) { + return similar(a, hd); + })]; + append sim; + input = drop(size(sim), input); + } + + return lres; +} + +@synopsis{Sum the elements of a list.} +@examples{ +```rascal-shell +import List; +sum([3, 1, 4, 5]); +sum([3, 1.5, 4, 5]); +``` +} +(&T <: num) sum([(&T <: num) hd, *(&T <: num) tl]) = ( hd | it + i | i <- tl ); +(&T <: num) sum(list[&T] _: []) { + throw EmptyList(); +} + +@synopsis{Get the tail element(s) from a list.} +@description{ +* Return a list consisting of all but the first element of `lst`. +* Return a list consisting of the last `n` elements of `lst`. +} +@examples{ +All but first element: +```rascal-shell,continue +import List; +tail([10,20,30]); +``` +Try an error case: +```rascal-shell,continue,error +tail([]); +``` +Last n elements: +```rascal-shell,continue +tail([10, 20, 30, 40, 50, 60], 3); +``` +Try an error case: +```rascal-shell,continue,error +tail([10, 20, 30, 40, 50, 60], 10); +``` +} +list[&T] tail([&T _, *&T t]) = t; +list[&T] tail(list[&T] _: []) { + throw EmptyList(); +} + +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] tail(list[&T] lst, int len) throws IndexOutOfBounds; + +@synopsis{Get number of elements from the head of a list.} +@description{ +Get `n` elements (or `size(lst)` elements if `size(lst) < n`) from the head of the list. +See ((List-drop)) to remove elements from the head of a list. +} +@examples{ +```rascal-shell +import List; +take(2, [3, 1, 4, 5]); +take(6, [3, 1, 4, 5]); +take(2, ["zebra", "elephant", "snake", "owl"]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java list[&T] take(int n, list[&T] lst); + +@synopsis{Remove an arbitrary element from a list, returns the element and the modified list.} +@description{ +Select an arbitrary element from `lst`, and return a tuple consisting of: + +* the selected element, and +* a new list consisting of all elements of `lst` except the selected element. + + +See ((List-getOneFrom)) to only selected an element from a list. +} +@examples{ +```rascal-shell +import List; +takeOneFrom([10,20,30,40,50]); +takeOneFrom([10,20,30,40,50]); +takeOneFrom([10,20,30,40,50]); +takeOneFrom(["zebra", "elephant", "snake", "owl"]); +takeOneFrom(["zebra", "elephant", "snake", "owl"]); +takeOneFrom(["zebra", "elephant", "snake", "owl"]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java tuple[&T, list[&T]] takeOneFrom(list[&T] lst); + +@synopsis{Take elements from the front of the list as long as a predicate is true.} +@examples{ +```rascal-shell +import List; +bool isEven(int a) = a mod 2 == 0; +takeWhile([2,4,6,8,1,2,3,4,5],isEven); +``` +} +list[&T] takeWhile(list[&T] lst, bool(&T a) take) { + i = 0; + return while(i < size(lst) && take(lst[i])) { + append lst[i]; + i += 1; + } +} + +@synopsis{Convert a list of pairs to a map; first elements are associated with a set of second elements.} +@description{ +Convert a list of tuples to a map in which the first element of each tuple is +associated with the set of second elements from all tuples with the same first element. Keys should be unique. +} +@examples{ +```rascal-shell +import List; +toMap([<1,10>, <1, 11>, <2, 20>, <3, 30>, <3, 31>]); +``` +} +@pitfalls{ +`toMap` collects all values in tuples with the same first value in a set. +Contrast this with `toMapUnique` that associates each first tuple value with the second tuple value, +but imposes the constraint that those keys are unique. +} +@javaClass{org.rascalmpl.library.Prelude} +java map[&A, list[&B]] toMap(list[tuple[&A, &B]] lst) throws MultipleKey; + +@synopsis{Convert a list of tuples to a map; result must be a map.} +@description{ +Convert a list of tuples to a map; result must be a map. +} +@examples{ +```rascal-shell +import List; +toMapUnique([<1,10>, <2, 20>, <3, 30>]); +``` +Let's explore an error case: +```rascal-shell,continue,error +toMapUnique([<1,10>, <1, 11>, <2, 20>, <3, 30>]); +``` +} +@pitfalls{ +The keys in a map are unique by definition. +`toMapUnique` throws a `MultipleKey` exception when the list contains more than one tuple with the same first value. +} +@javaClass{org.rascalmpl.library.Prelude} +java map[&A, &B] toMapUnique(list[tuple[&A, &B]] lst) throws MultipleKey; + +@synopsis{Take the top element of a list.} +@description{ +This function is identical to ((List-head)). +Also see ((List-pop)) and ((List-push)). +} +@examples{ +```rascal-shell +import List; +top([3, 1, 4, 5]); +top(["zebra", "elephant", "snake", "owl"]); +``` +} +&T top([&T t, *&T _]) = t; + +@synopsis{Convert a list to a relation.} +@description{ +Convert a list to relation, where each tuple encodes which elements are followed by each other. + This function will return an empty relation for empty lists and for singleton lists. +} +@examples{ +```rascal-shell +import List; +toRel([3, 1, 4, 5]); +toRel(["zebra", "elephant", "snake", "owl"]); +``` +} +rel[&T, &T] toRel(list[&T] lst) { + return {| [*_, from, to, *_] := lst}; +} + +@synopsis{Convert a list to a set.} +@description{ +Convert `lst` to a set. +} +@examples{ +```rascal-shell +import List; +toSet([10, 20, 30, 40]); +toSet(["zebra", "elephant", "snake", "owl"]); +``` +Note that the same can be done using splicing +```rascal-shell,continue +l = [10,20,30,40]; +s = {*l}; +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java set[&T] toSet(list[&T] lst); + +@synopsis{Convert a list to a string.} +@description{ +Convert `lst` to a string. +} +@examples{ +```rascal-shell +import List; +toString([10, 20, 30]); +toString(["zebra", "elephant", "snake", "owl"]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java str toString(list[&T] lst); + +@synopsis{Convert a list to an indented string.} +@description{ +Convert `lst` to a indented string. +} +@examples{ +```rascal-shell +import List; +itoString([10, 20, 30]); +itoString(["zebra", "elephant", "snake", "owl"]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java str itoString(list[&T] lst); + +@synopsis{Make a pair (triple) of lists from a list of pairs (triples).} +@description{ +Also see ((List-unzip3)); +} +@examples{ +```rascal-shell +import List; +unzip2([<3,"thirty">, <1,"ten">, <4,"forty">]); +unzip3([<3,"thirty",300>, <1,"ten",100>, <4,"forty",400>]); +``` +} +tuple[list[&T], list[&U]] unzip2(list[tuple[&T, &U]] lst) + = <[t | <- lst], [u | <_, u> <- lst]>; + +// Make a triple of lists from a list of triples. +tuple[list[&T], list[&U], list[&V]] unzip3(list[tuple[&T, &U, &V]] lst) + = <[t | <- lst], [u | <_, u, _> <- lst], [w | <_, _, w> <- lst]>; + +@synopsis{Returns the list 0,1..n-1.} +@description{ +Returns the list `0`, `1`, .., `n-1`, this is slightly faster than `[0..n]`, since the returned values are shared. +} +@examples{ +```rascal-shell +import List; +upTill(10); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +java list[int] upTill(int n); + +@synopsis{Make a list of pairs from two (three) lists of the same length.} +@description{ +Also see ((List-unzip3)). +} +@examples{ +```rascal-shell +import List; +zip2([3, 1, 4], ["thirty", "ten", "forty"]); +zip3([3, 1, 4], ["thirty", "ten", "forty"], [300, 100, 400]); +``` +} +list[tuple[&T first, &U second]] zip2(list[&T] a, list[&U] b) { + if (size(a) != size(b)) + throw IllegalArgument(, "List size mismatch"); + return [ | i <- index(a)]; +} + +list[tuple[&T first, &U second, &V third]] zip3( + list[&T] a, list[&U] b, list[&V] c +) { + if (size(a) != size(b) || size(a) != size(c)) + throw IllegalArgument(, "List size mismatch"); + return [ | i <- index(a)]; +} +### |project://rascal/src/org/rascalmpl/library/ListRelation.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Vadim Zaytsev - vadim@grammarware.net - UvA} +@synopsis{Library functions for list relations.} +@description{ +The following library functions are defined for list relations : +(((TOC))) +} +module ListRelation + +import List; + +@synopsis{Return the list of all elements in any tuple in a list relation.} +@examples{ +```rascal-shell +import ListRelation; +carrier([<1,10>, <2,20>]); +carrier([<1,10,100,1000>, <2,20,200,2000>]); +``` +} +public list[&T] carrier(lrel[&T, &T] R) = [V0, V1 | <&T V0, &T V1> <- R]; + +public +list[&T] carrier(lrel[&T, &T, &T] R) + = [V0, V1, V2 | <&T V0, &T V1, &T V2> <- R]; + +public +list[&T] carrier(lrel[&T, &T, &T, &T] R) + = [V0, V1, V2, V3 | <&T V0, &T V1, &T V2, &T V3> <- R]; + +public +list[&T] carrier(lrel[&T, &T, &T, &T, &T] R) + = [V0, V1, V2, V3, V4 | <&T V0, &T V1, &T V2, &T V3, &T V4> <- R]; + +@synopsis{A list relation restricted to certain element values in tuples.} +@description{ +Returns list relation `R` restricted to tuples with elements in set `S`. +} +@examples{ +```rascal-shell +import ListRelation; +carrierR([<1,10>, <2,20>, <3,30>], {10, 1, 20}); +``` +} +public +lrel[&T, &T] carrierR(lrel[&T, &T] R, set[&T] S) + = [ | <&T V0, &T V1> <- R, V0 in S, V1 in S]; + +public +lrel[&T, &T, &T] carrierR(lrel[&T, &T, &T] R, set[&T] S) + = [ | <&T V0, &T V1, &T V2> <- R, V0 in S, V1 in S, V2 in S]; + +public +lrel[&T, &T, &T, &T] carrierR(lrel[&T, &T, &T, &T] R, set[&T] S) + = [ | <&T V0, &T V1, &T V2, &T V3> <- R, V0 in S, V1 in S, V2 in S, V3 in S]; + +public +lrel[&T, &T, &T, &T, &T] carrierR(lrel[&T, &T, &T, &T, &T] R, set[&T] S) + = [ + | <&T V0, &T V1, &T V2, &T V3, &T V4> <- R, V0 in S, V1 in S, V2 in S, V3 in S, V4 in S]; + +@synopsis{A list relation excluding tuples containing certain values.} +@description{ +Returns list relation `R` excluding tuples with some element in `S`. +} +@examples{ +```rascal-shell +import ListRelation; +carrierX([<1,10>, <2,20>, <3,30>], {10, 1, 20}); +``` +} +public +lrel[&T, &T] carrierX(lrel[&T, &T] R, set[&T] S) + = [ | <&T V0, &T V1> <- R, V0 notin S, V1 notin S]; + +public +lrel[&T, &T, &T] carrierX(lrel[&T, &T, &T] R, set[&T] S) + = [ | <&T V0, &T V1, &T V2> <- R, V0 notin S, V1 notin S, V2 notin S]; + +public +lrel[&T, &T, &T, &T] carrierX(lrel[&T, &T, &T, &T] R, set[&T] S) + = [ + | <&T V0, &T V1, &T V2, &T V3> <- R, V0 notin S, V1 notin S, V2 notin S, V3 notin S]; + +public +lrel[&T, &T, &T, &T, &T] carrierX(lrel[&T, &T, &T, &T, &T] R, set[&T] S) + = [ + | <&T V0, &T V1, &T V2, &T V3, &T V4> <- R, V0 notin S, V1 notin S, V2 notin S, V3 notin S, V4 notin S]; + +@synopsis{Complement of a list relation.} +@description{ +Given a list relation `R` a new relation `U` can be constructed that contains +all possible tuples with element values that occur at corresponding tuple positions in `R`. +The function `complement` returns the complement of `R` relative to `U`, in other words: `U - R`. +} +@examples{ +```rascal-shell +import ListRelation; +``` + +Declare `R` and compute corresponding `U`: + +```rascal-shell,continue +R = [<1,10>, <2, 20>, <3, 30>]; +U = domain(R) * range(R); +``` + +Here is the complement of `R` computed in two ways: +```rascal-shell,continue +U - R; +complement([<1,10>, <2, 20>, <3, 30>]); +``` +} +public +lrel[&T0, &T1] complement(lrel[&T0, &T1] R) + = size(R) < 2 ? [] : dup([ | &T0 V0 <- R<0>, &T1 V1 <- R<1>, notin R]); + +// Was: (domain(R) * range(R)) - R; +public +lrel[&T0, &T1, &T2] complement(lrel[&T0, &T1, &T2] R) + = dup( + [ | &T0 V0 <- R<0>, &T1 V1 <- R<1>, &T2 V2 <- R<2>, notin R] + ); + +public +lrel[&T0, &T1, &T2, &T3] complement(lrel[&T0, &T1, &T2, &T3] R) + = dup( + [ + | &T0 V0 <- R<0>, &T1 V1 <- R<1>, &T2 V2 <- R<2>, &T3 V3 <- R<3>, notin R] + ); + +public +lrel[&T0, &T1, &T2, &T3, &T4] complement(lrel[&T0, &T1, &T2, &T3, &T4] R) + = dup( + [ + | &T0 V0 <- R<0>, &T1 V1 <- R<1>, &T2 V2 <- R<2>, &T3 V3 <- R<3>, &T4 V4 <- R<4>, notin R] + ); + +@synopsis{Domain of a list relation: a list consisting of the first element of each tuple, uniquely.} +@description{ +The domain can be seen as all possible inputs of the relation image operation. The +result contains elements (or tuples) in the order of appearance of the original relation, +but all occurrences after the first occurrence of an element have been removed. +} +@examples{ +```rascal-shell +import ListRelation; +domain([<1,10>, <2,20>]); +domain([<"mon", 1>, <"tue", 2>]); +``` +} +public list[&T0] domain(lrel[&T0, &T1] R) = dup(R<0>); +public list[&T0] domain(lrel[&T0, &T1, &T2] R) = dup(R<0>); +public list[&T0] domain(lrel[&T0, &T1, &T2, &T3] R) = dup(R<0>); +public list[&T0] domain(lrel[&T0, &T1, &T2, &T3, &T4] R) = dup(R<0>); + +@synopsis{List relation restricted to certain domain elements.} +@description{ +Restriction of a list relation `R` to tuples with first element in `S`. +} +@examples{ +```rascal-shell +import ListRelation; +domainR([<1,10>, <2,20>, <3,30>], {3, 1}); +``` +} +public +lrel[&T0, &T1] domainR(lrel[&T0, &T1] R, set[&T0] S) + = [ | <&T0 V0, &T1 V1> <- R, V0 in S]; + +public +lrel[&T0, &T1, &T2] domainR(lrel[&T0, &T1, &T2] R, set[&T0] S) + = [ | <&T0 V0, &T1 V1, &T2 V2> <- R, V0 in S]; + +public +lrel[&T0, &T1, &T2, &T3] domainR(lrel[&T0, &T1, &T2, &T3] R, set[&T0] S) + = [ | <&T0 V0, &T1 V1, &T2 V2, &T3 V3> <- R, V0 in S]; + +public +lrel[&T0, &T1, &T2, &T3, &T4] domainR( + lrel[&T0, &T1, &T2, &T3, &T4] R, set[&T0] S +) + = [ | <&T0 V0, &T1 V1, &T2 V2, &T3 V3, &T4 V4> <- R, V0 in S]; + +// If the restriction is specified as a list, we take the order of tuples from there +public +lrel[&T0, &T1] domainR(lrel[&T0, &T1] R, list[&T0] L) + = [ | &T0 V0 <- L, <- R]; + +public +lrel[&T0, &T1, &T2] domainR(lrel[&T0, &T1, &T2] R, list[&T0] L) + = [ | &T0 V0 <- L, <- R]; + +public +lrel[&T0, &T1, &T2, &T3] domainR(lrel[&T0, &T1, &T2, &T3] R, list[&T0] L) + = [ | &T0 V0 <- L, <- R]; + +public +lrel[&T0, &T1, &T2, &T3, &T4] domainR( + lrel[&T0, &T1, &T2, &T3, &T4] R, list[&T0] L +) + = [ | &T0 V0 <- L, <- R]; + +@synopsis{List relation excluding certain domain values.} +@description{ +List relation `R` excluding tuples with first element in `S`. +} +@examples{ +```rascal-shell +import ListRelation; +domainX([<1,10>, <2,20>, <3,30>], {3, 1}); +``` +} +public +lrel[&T0, &T1] domainX(lrel[&T0, &T1] R, set[&T0] S) + = [ | <&T0 V0, &T1 V1> <- R, V0 notin S]; + +public +lrel[&T0, &T1, &T2] domainX(lrel[&T0, &T1, &T2] R, set[&T0] S) + = [ | <&T0 V0, &T1 V1, &T2 V2> <- R, V0 notin S]; + +public +lrel[&T0, &T1, &T2, &T3] domainX(lrel[&T0, &T1, &T2, &T3] R, set[&T0] S) + = [ | <&T0 V0, &T1 V1, &T2 V2, &T3 V3> <- R, V0 notin S]; + +public +lrel[&T0, &T1, &T2, &T3, &T4] domainX( + lrel[&T0, &T1, &T2, &T3, &T4] R, set[&T0] S +) + = [ | <&T0 V0, &T1 V1, &T2 V2, &T3 V3, &T4 V4> <- R, V0 notin S]; + +@synopsis{Make sets of elements in the domain that relate to the same element in the range.} +@examples{ +```rascal-shell +import ListRelation; +legs = [<"bird", 2>, <"dog", 4>, <"human", 2>, <"spider", 8>, <"millipede", 1000>, <"crab", 8>, <"cat", 4>]; +groupDomainByRange(legs); +``` +} +public +list[list[&U]] groupDomainByRange(lrel[&U dom, &T ran] input) + = dup([[d | <&U d, r> <- input] | &T r <- input.ran]); + +@synopsis{Make sets of elements in the range that relate to the same element in the domain.} +@description{ +```rascal-shell +import ListRelation; +skins = [<"bird", "feather">, <"dog", "fur">, <"tortoise", "shell">, <"human", "skin">, <"fish", "scale">, <"lizard", "scale">, <"crab", "shell">, <"cat", "fur">]; +groupRangeByDomain(skins); +``` +} +public +list[list[&T]] groupRangeByDomain(lrel[&U dom, &T ran] input) + = dup([[r | <- input] | &U d <- input.dom]); + +// the "input[i]" trick used for set-based relations does not work here +// because [<"a",1>]["a"] does give [1], but [<1,1>][1] does not! +@synopsis{The identity list relation.} +@description{ +The identity list relation for set `S`. +} +@examples{ +```rascal-shell +import ListRelation; +ident(["mon", "tue", "wed"]); +``` +} +public lrel[&T, &T] ident(list[&T] S) = [ | V <- S]; + +@synopsis{Invert the tuples in a list relation.} +@examples{ +```rascal-shell +import ListRelation; +invert([<1,10>, <2,20>]); +``` +} +public lrel[&T1, &T0] invert(lrel[&T0, &T1] R) = R<1,0>; +public lrel[&T2, &T1, &T0] invert(lrel[&T0, &T1, &T2] R) = R<2,1,0>; +public lrel[&T3, &T2, &T1, &T0] invert(lrel[&T0, &T1, &T2, &T3] R) = R<3,2,1,0>; +public +lrel[&T4, &T3, &T2, &T1, &T0] invert(lrel[&T0, &T1, &T2, &T3, &T4] R) + = R<4,3,2,1,0>; + +@synopsis{The range is composed of all but the first element of each tuple of a list relation, uniquely.} +@description{ +The range can be seen as all the elements of in all possible images of the relation. The +result contains elements (or tuples) in the order of appearance of the original relation, +but all occurrences after the first occurrence of an element have been removed. +} +@examples{ +```rascal-shell +import ListRelation; +range([<1,10>, <2,20>]); +range([<"mon", 1>, <"tue", 2>]); +``` +} +public list[&T1] range(lrel[&T0, &T1] R) = dup(R<1>); +public lrel[&T1, &T2] range(lrel[&T0, &T1, &T2] R) = dup(R<1,2>); +public lrel[&T1, &T2, &T3] range(lrel[&T0, &T1, &T2, &T3] R) = dup(R<1,2,3>); +public +lrel[&T1, &T2, &T3, &T4] range(lrel[&T0, &T1, &T2, &T3, &T4] R) + = dup(R<1,2,3,4>); + +@synopsis{List relation restricted to certain range values.} +@description{ +Restriction of binary list relation `R` to tuples with second element in set `S`. +} +@examples{ +```rascal-shell +import ListRelation; +rangeR([<1,10>, <2,20>, <3,30>], {30, 10}); +``` +} +public +lrel[&T0, &T1] rangeR(lrel[&T0, &T1] R, set[&T1] S) + = [ | <&T0 V0, &T1 V1> <- R, V1 in S]; + +// If the restriction is specified as a list, we take the order of tuples from there +public +lrel[&T0, &T1] rangeR(lrel[&T0, &T1] R, list[&T1] L) + = [ | &T1 V1 <- L, <&T0 V0, V1> <- R]; + +@synopsis{List relation excluding certain range values.} +@description{ +Restriction of binary list relation `R` to tuples with second element not in set `S`. +} +@examples{ +```rascal-shell +import ListRelation; +rangeX([<1,10>, <2,20>, <3,30>], {30, 10}); +``` +} +public +lrel[&T0, &T1] rangeX(lrel[&T0, &T1] R, set[&T1] S) + = [ | <&T0 V0, &T1 V1> <- R, V1 notin S]; + +// Why not? +public +lrel[&T0, &T1] rangeX(lrel[&T0, &T1] R, list[&T1] S) + = [ | <&T0 V0, &T1 V1> <- R, V1 notin S]; + +@synopsis{Lists a binary list relation as a map} +@description{ +Converts a binary list relation to a map of the domain to a set of the range. +} +@examples{ +```rascal-shell +import ListRelation; +index([<1,10>, <2,20>, <3,30>, <30,10>]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java map[&K, set[&V]] index(lrel[&K, &V] R); +### |project://rascal/src/org/rascalmpl/library/Location.rsc| +@license{ + Copyright (c) 2019 SWAT.engineering + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Paul Klint - Paul.Klint@swat.engineering - SWAT.engineering} +@synopsis{Library functions for source locations.} +@description{ +The following library functions are defined for source locations: +(((TOC))) + +A source location `l` refers to a text fragment in another file or resource. To ease the description we will +talk about _`l` 's text_ instead of _the text `l` refers to_. +} +module Location + +import IO; +import List; +import Set; +import String; +import Exception; + +@synopsis{Extracts a path relative to a parent location.} +@description{ +* From `x:///a/b` and `x:///a/b/c` this makes `relative:///c`. +* If the outside does not envelop the inside, then the original loc is returned. +} +@javaClass{org.rascalmpl.library.Prelude} +java loc relativize(loc outside, loc inside); + +@synopsis{Find the first `haystack` folder the `needle` can be found in and relativize it, or fail.} +@description{ +* From `[|x:///a/b|]` as haystack and `|x:///a/b/c|` as needle this makes `|relative:///c|`. +* If none of the `haystack` locations contain the `needle`, a `PathNotFound` exception is thrown. +} +loc relativize(list[loc] haystack, loc needle) throws PathNotFound { + if (h <- haystack, loc r := relativize(h, needle), r != needle) { + return r; + } + throw PathNotFound(needle); +} + +@synopsis{Concatenate a relative path to a given surrounding path} +@description{ +* `relative` must be of scheme `relative:///` or `SchemeNotSupported` will be thrown +* ((resolve)) is the opposite of ((relativize)) +* the return value does not necessarily exist +} +loc resolve(loc outside, loc relative) = outside + relative.path + when relative.scheme == "relative"; +default loc resolve(loc _, loc relative) { + throw SchemeNotSupported(relative); +} + +@synopsis{Find the right folder in which a relative location is to be found and return the complete path} +@description{ +* `relative` must be of scheme `relative:///` +* ((resolve)) is the opposite of ((relativize)) +* if a file can not be found in any of the `haystack` folders, then `PathNotFound`` is thrown. +* if `force` is true then a location relative to the first element of the haystack will be returned, even if the file was not found anywhere in the haystack. +} +loc resolve( + list[loc] haystack, + loc relative, + bool force = false +) throws PathNotFound { + assert relative.scheme == "relative"; + assert haystack != []; + + for (loc outside <- haystack, loc candidate := resolve(outside, relative), exists(candidate)) { + return candidate; + } + + if (force && haystack != []) { + return resolve(haystack[0], relative); + } + throw PathNotFound(relative); +} + +@synopsis{Shortens an absolute path to a jar inside the local maven repository.} +@javaClass{org.rascalmpl.library.Prelude} +java loc mavenize(loc jar); + +@synopsis{If the location points to a jar file, then this modifies the scheme and the path to point _inside_ of the jar.} +@javaClass{org.rascalmpl.library.Prelude} +java loc jarify(loc jar); + +@synopsis{Check that two locations refer to the same file.} +@javaClass{org.rascalmpl.library.Prelude} +java bool isSameFile(loc l, loc r); + +@synopsis{Compare two location values lexicographically.} +@description{ +When the two locations refer to different files, their paths are compared as string. +When they refer to the same file, their offsets are compared when present. +} +@pitfalls{ +This ordering regards the location value itself as opposed to the text it refers to. +} +bool isLexicallyLess(loc l, loc r) + = isSameFile(l, r) ? (l.offset ? 0) < (r.offset ? 0) : l.top < r.top; + +@synopsis{Get the textual content a location refers to.} +str getContent(loc l) = readFile(l); + +@synopsis{Is a location textually (strictly) contained in another location?} +@description{ +Strict containment between two locations `inner` and `outer` holds when + + +- `outer` 's text begins before `inner` 's text, or +- `outer` 's text ends after `inner` 's text, or +- both. +} +@javaClass{org.rascalmpl.library.Prelude} +java bool isStrictlyContainedIn(loc inner, loc outer); + +@synopsis{Is a location textually contained in another location?} +@description{ +Containment between two locations `inner` and `outer` holds when + + +- `inner` and `outer` are equal, or +- `inner` is strictly contained in `outer`. +} +@javaClass{org.rascalmpl.library.Prelude} +java bool isContainedIn(loc inner, loc outer); + +@synopsis{Begins a location's text before (but may overlap with) another location's text?} +bool beginsBefore(loc l, loc r) = isSameFile(l, r) && l.offset < r.offset; + +@synopsis{Begins and ends a location's text before another location's text?} +@description{ +`isBefore(l, r)` holds when `l` 's text occurs textually before `r` 's text. +} +bool isBefore(loc l, loc r) = isSameFile(l, r) && l.offset + l.length <= r.offset; + +@synopsis{Occurs a location's text _immediately_ before another location's text?} +@description{ +`isImmediatelyBefore(l, r)` holds when `l` 's text occurs textually before, and is adjacent to, `r` 's text. +} +bool isImmediatelyBefore(loc l, loc r) + = isSameFile(l, r) && l.offset + l.length == r.offset; + +@synopsis{Begins a location's text after (but may overlap with) another location's text? + +Description +`beginsAfter(l, r)` holds when `l` 's text begins after `r` 's text. No assumption is made about the end of both texts. +In other words, `l` 's text may end before or after the end of `r` 's text.} +bool beginsAfter(loc l, loc r) = isSameFile(l, r) && l.offset > r.offset; + +@synopsis{Is a location's text completely after another location's text?} +bool isAfter(loc l, loc r) = isBefore(r, l); + +@synopsis{Is a location's text _immediately_ after another location's text?} +bool isImmediatelyAfter(loc l, loc r) = isImmediatelyBefore(r, l); + +@synopsis{Refer two locations to text that overlaps?} +@javaClass{org.rascalmpl.library.Prelude} +java bool isOverlapping(loc l, loc r); + +@synopsis{Compute a location that textually covers the text of a list of locations.} +@description{ +Create a new location that refers to the smallest text area that overlaps with the text of the given locations. +The given locations should all refer to the same file but they may be overlapping or be contained in each other. +} +loc cover(list[loc] locs) { + n = size(locs); + if (n == 0) { + throw IllegalArgument(locs, "Cover of empty list of locations"); + } + else if (n == 1) { + return locs[0]; + } + else { + locs = [l | l <- locs, !any(m <- locs, m != l, isContainedIn(l, m))]; + locs = sort(locs, beginsBefore); + loc first = locs[0]; + loc last = locs[-1]; + + tops = {l.top| l <- locs}; + if (size(tops) > 1) { + throw IllegalArgument( + locs, "Cover of locations with different scheme, authority or path" + ); + } + if (first.begin? && last.end?) { + return + first.top( + first.offset, + last.offset + last.length - first.offset, + , + + ); + } + else if (first.offset? && last.offset?) { + return first.top(first.offset, last.offset + last.length - first.offset); + } + else { + return first.top; + } + } +} +### |project://rascal/src/org/rascalmpl/library/Map.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Jimi van der Woning - Jimi.vanderWoning@student.uva.nl} +@synopsis{Library functions for maps.} +@description{ +The following library functions are defined for maps: +(((TOC))) +} +module Map + +@synopsis{Delete a key from a map.} +@description{ +Returns the map `m` minus the key `k`. +} +@examples{ +```rascal-shell +import Map; +delete(("apple":1,"pear":2), "apple"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java map[&K, &V] delete(map[&K, &V] m, &K k); + +@synopsis{Determine the domain (set of keys) of a map.} +@description{ +Returns the domain (set of keys) of map `M`. +} +@examples{ +```rascal-shell +import Map; +domain(("apple": 1, "pear": 2)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java set[&K] domain(map[&K, &V] M); + +@synopsis{Map restricted to certain keys.} +@description{ +Return the map `M` restricted to pairs with key in `S`. +} +@examples{ +```rascal-shell +import Map; +domainR(("apple": 1, "pear": 2, "orange": 3), {"apple", "pear"}); +``` +} +public +map[&K, &V] domainR(map[&K, &V] M, set[&K] S) + = isEmpty(M) ? M : (k: M[k]| &K k <- M, k in S); + +@synopsis{Map with certain keys excluded.} +@description{ +Return the map `M` restricted to pairs with key not in `S`. +} +@examples{ +```rascal-shell +import Map; +domainX(("apple": 1, "pear": 2, "orange": 3), {"apple", "pear"}); +``` +} +public +map[&K, &V] domainX(map[&K, &V] M, set[&K] S) + = isEmpty(M) ? M : (k: M[k]| &K k <- M, k notin S); + +@synopsis{Get a n arbitrary key from a map.} +@description{ +Returns an arbitrary key of map `M`. +} +@examples{ +```rascal-shell +import Map; +getOneFrom(("apple": 1, "pear": 2, "pineapple": 3)); +getOneFrom(("apple": 1, "pear": 2, "pineapple": 3)); +getOneFrom(("apple": 1, "pear": 2, "pineapple": 3)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java &K getOneFrom(map[&K, &V] M); + +@synopsis{Invert the (key,value) pairs in a map.} +@description{ +Returns inverted map in which each value in the old map `M` is associated with a set of key values from the old map. +Also see ((invertUnique)). +} +@examples{ +```rascal-shell +import Map; +invert(("apple": 1, "pear": 2, "orange": 1)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java map[&V, set[&K]] invert(map[&K, &V] M); + +@synopsis{Invert the (key,value) pairs in a map.} +@description{ +Returns a map with key and value inverted; the result should be a map. +If the initial map contains duplicate values, +the `MultipleKey` exception is raised since +an attempt is made to create a map where more than one +value would be associated with the same key. + +Also see ((Map-invert)) and ((module:Exception)). +} +@examples{ +```rascal-shell +import Map; +invertUnique(("apple": 1, "pear": 2, "orange": 3)); +``` +Here is an examples that generates an exception: +```rascal-shell,continue,error +invertUnique(("apple": 1, "pear": 2, "orange": 1)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java map[&V, &K] invertUnique(map[&K, &V] M); + +@synopsis{Test whether a map is empty.} +@description{ +Returns `true` if map `M` is empty, and `false` otherwise. +} +@examples{ +```rascal-shell +import Map; +isEmpty(()); +isEmpty(("apple": 1, "pear": 2, "orange": 3)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool isEmpty(map[&K, &V] M); + +@synopsis{Apply a function to all (key, value) pairs in a map.} +@description{ +Apply the functions `F` and `G` to each key/value pair in a map and return the transformed map. +} +@examples{ +```rascal-shell +import Map; +str prefix(str s) { return "X" + s; } +int incr(int x) { return x + 1; } +mapper(("apple": 1, "pear": 2, "orange": 3), prefix, incr); +``` +} +public +map[&L, &W] mapper(map[&K, &V] M, &L(&K) F, &W(&V) G) + = (F(key): G(M[key])| &K key <- M); + +@synopsis{The range (set of values that correspond to its keys) of a map.} +@description{ +Returns the range (set of values) of map `M`. +} +@examples{ +```rascal-shell +import Map; +range(("apple": 1, "pear": 2)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java set[&V] range(map[&K, &V] M); + +@synopsis{Map restricted to certain values in (key,values) pairs.} +@description{ +Returns the map restricted to pairs with values in `S`. +} +@examples{ +```rascal-shell +import Map; +rangeR(("apple": 1, "pear": 2, "orange": 3), {2, 3}); +``` +} +public +map[&K, &V] rangeR(map[&K, &V] M, set[&V] S) + = isEmpty(M) ? M : (k: M[k]| &K k <- M, M[k] in S); + +@synopsis{Map with certain values in (key,value) pairs excluded.} +@description{ +Returns the map restricted to pairs with values not in `S`. +} +@examples{ +```rascal-shell +import Map; +rangeX(("apple": 1, "pear": 2, "orange": 3), {2, 3}); +``` +} +public +map[&K, &V] rangeX(map[&K, &V] M, set[&V] S) + = isEmpty(M) ? M : (k: M[k]| &K k <- M, M[k] notin S); + +@synopsis{Number of (key, value) pairs in a map.} +@description{ +Returns the number of pairs in map `M`. +} +@examples{ +```rascal-shell +import Map; +size(("apple": 1, "pear": 2, "orange": 3)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java int size(map[&K, &V] M); + +@synopsis{Convert a map to a list of tuples.} +@examples{ +```rascal-shell +import Map; +toList(("apple": 1, "pear": 2, "orange": 3)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java list[tuple[&K, &V]] toList(map[&K, &V] M); + +@synopsis{Convert a map to a relation.} +@examples{ +```rascal-shell +import Map; +toRel(("apple": 1, "pear": 2, "orange": 3)); +``` +} +public rel[&K, &V] toRel(map[&K, set[&V]] M) = {| &K k <- M, &V v <- M[k]}; +public rel[&K, &V] toRel(map[&K, list[&V]] M) = {| &K k <- M, &V v <- M[k]}; +@javaClass{org.rascalmpl.library.Prelude} +public default java rel[&K, &V] toRel(map[&K, &V] M); + +@synopsis{Convert a map to a string.} +@examples{ +```rascal-shell +import Map; +toString(("apple": 1, "pear": 2, "orange": 3)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str toString(map[&K, &V] M); + +@synopsis{Convert a map to a indented string.} +@examples{ +```rascal-shell +import Map; +itoString(("apple": 1, "pear": 2, "orange": 3)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str itoString(map[&K, &V] M); +### |project://rascal/src/org/rascalmpl/library/Message.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Mark Hills - Mark.Hills@cwi.nl (CWI)} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@synopsis{Provides the `Message` datatype that represents error messages and warnings.} +@description{ +Messages can be used to communicate information about source texts. +They can be interpreted by IDEs to display type errors and warnings, etc. + +`Message`s are, for instance, used as additional keyword fields of +other data types (syntax trees), or collected in sets or lists of errors to +be published in an IDE. See ((util::IDEServices)). +} +module Message + +import IO; + +@synopsis{Symbolic representation of UI-facing error messages.} +@description{ +A `Message` or a `list[Message]` is typically produced by language processors such as +parsers, type checkers, static analyzers, etc. The Message data type is a simple contract between +these producers and the consumers of the messages (typically terminals, editors and IDE's). + +* The severity of an error is encoded in the constructor name: error, warning or info. +* The `msg` field is a UI-facing string that describes why something was hard or impossible to process. +* The `at` field points at the source code that was identified as a primary cause of a problem. +* The optional `causes` lists secondary causes via recursion. Each of the causes is a _conjunctive_ +cause of the primary issue. This means that if hypothetically at least one of them was resolved, the primary issue +would also be resolved. +} +@benefits{ +* The Message data type has consumers on the terminal, in editors and in the IDE. One uniform format +for all user environments. This makes error producing tools reusable in different contexts without adaptation. +} +@pitfalls{ +* One of the error constructors misses an `at` field. This constructor is _deprecated_. Error messages without +source locations are not useful in any UI context. +} +data Message (list[Message] causes = []) + = error(str msg, loc at) + | warning(str msg, loc at) + | info(str msg, loc at) + ; + +@javaClass{org.rascalmpl.library.Messages} +@synopsis{Call the standard message pretty-printing and sorting code, writing the result to a string} +java str write(list[Message] messages, loc projectRoot = |unknown:///|); + +@synopsis{Reusable message handler for commandline `main` functions} +@description{ +This function takes care of some typical responsibilities of `main` functions: +* return an `int` to signal failure (!= 0) or success (0). +* print the collected and sorted error messages to stdout. +* implement the errorsAsWarnings feature. +* implement the warningsAsErrors feature. + +With `errorsAsWarnings` we do not fail while we are still developing experimental code. The process +of testing and deployment may continue even if (fatal) errors were detected. + +With `warningsAsErrors` we can signal a higher level of stability and compliance than the default. +Every new warnings will lead to a failing build, making sure that new issues can not creep in anymore. +} +@benefits{ +* consistent error handling between different `main` functions +* consistent error printing +* consistent interpretation of errorsAsWarnings and warningsAsErrors +} +@pitfalls{ +* stdout is used to print the messages; no further processing is possible. +* have to remember to return the result of this function as the return value of `main` +} +int mainMessageHandler( + list[Message] messages, + loc projectRoot = |unknown:///|, + bool errorsAsWarnings = false, + bool warningsAsErrors = false +) { + int FAILURE = 1; + int SUCCESS = 0; + + if (errorsAsWarnings && warningsAsErrors) { + println( + "[ERROR] the error handler is confused because both errorsAsWarnings and warningsAsErrors are set to true." + ); + return FAILURE; + } + println(write(messages, projectRoot = projectRoot)); + hasErrors = hasWarnings = false; + if (messages != []) { + hasErrors = any(error(_, _) <- messages); + hasWarnings = any(warning(_, _) <- messages); + } + switch() { + case <true, _, false, _>: + return FAILURE; + case <true, _, true, _>: { + println("[INFO] errors have been de-escalated to warnings."); + return SUCCESS; + } + case <_, true, _, true>: { + println("[INFO] warnings have been escalated to errors"); + return FAILURE; + } + case <_, false, _, false>: { + return SUCCESS; + } + default: + return hasErrors ? FAILURE : SUCCESS; + } +} +### |project://rascal/src/org/rascalmpl/library/Node.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@synopsis{Library functions for nodes.} +@description{ +The following library functions are defined for nodes: +(((TOC))) +} +module Node + +@synopsis{Determine the number of children of a node.} +@examples{ +```rascal-shell +import Node; +arity("f"(10, "abc")); +arity("f"(10, "abc", false)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java int arity(node T); + +@synopsis{Get the children of a node.} +@examples{ +```rascal-shell +import Node; +getChildren("f"(10, "abc")); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java list[value] getChildren(node T); + +@synopsis{Get the keyword parameters of a node.} +@examples{ +```rascal-shell +import Node; +getKeywordParameters("f"(10, "abc", height=0)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java map[str, value] getKeywordParameters(node T); + +@deprecated{ +Use getKeywordParameters(T) +} +public map[str, value] getAnnotations(node T) = getKeywordParameters(T); + +@synopsis{Set the keyword parameters of a node.} +@examples{ +```rascal-shell +import Node; +setKeywordParameters("f"(10, "abc"), ("height":0)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public +java &T <: node setKeywordParameters( + &T <: node x, map[str, value] keywordParameters +); + +// @synopsis{Adds new keyword parameters to a node, keeping the existing ones unless there is an entry in the new map.} +// @examples{ +// ```rascal-shell +// import Node; +// mergeKeywordParameters("f"(10, "abc", width=10), ("height":0)); +// ``` +// } +// @javaClass{org.rascalmpl.library.Prelude} +// TODO: uncomment after bootstrap +// public java &T <: node mergeKeywordParameters(&T <: node x, map[str,value] keywordParameters); +@deprecated{ +Use setKeywordParameters(x, keywordParameters) +} +public +&T <: node setAnnotations(&T <: node x, map[str, value] keywordParameters) + = setKeywordParameters(x, keywordParameters); + +@synopsis{Determine the name of a node.} +@examples{ +```rascal-shell +import Node; +getName("f"(10, "abc")); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str getName(node T); + +@synopsis{Create a node given its function name and arguments.} +@examples{ +```rascal-shell +import Node; +makeNode("f", [10, "abc"]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public +java node makeNode(str N, value V..., map[str, value] keywordParameters = ()); + +@synopsis{Reset a specific keyword parameter back to their default on a node.} +@javaClass{org.rascalmpl.library.Prelude} +public java &T <: node unset(&T <: node x, str keywordParameter); + +@deprecated{ +Use unset(x, kw) +} +public +&T <: node delAnnotation(&T <: node x, str keywordParameter) + = unset(x, keywordParameter); + +@synopsis{Reset a set of keyword parameters back to their default on a node.} +public &T <: node unset(&T <: node x, set[str] keywordParameters) { + for (keywordParameter <- keywordParameters) { + x = unset(x, keywordParameter); + } + return x; +} + +@synopsis{Reset all keyword parameters back to their default.} +@javaClass{org.rascalmpl.library.Prelude} +public java &T <: node unset(&T <: node x); + +@deprecated{ +Use `unset(x)` +} +public &T <: node delAnnotations(&T <: node x) = unset(x); + +@synopsis{Recursively reset all keyword parameters of the node and its children back to their default.} +@javaClass{org.rascalmpl.library.Prelude} +public java &T unsetRec(&T x); + +@deprecated{ +Use `unsetRec(x)` +} +public &T delAnnotationsRec(&T x) = unsetRec(x); + +@synopsis{Recursively reset a specific keyword parameter of the node and its children back to its default.} +public +&T unsetRec(&T x, str keywordParameter) + = visit(x) { + case node n => unset(n, keywordParameter) + }; + +@synopsis{Recursively reset a selected set of keyword parameters of the node and its children back to their default.} +public +&T <: node unsetRec(&T <: node x, set[str] keywordParameters) + = visit(x) { + case node n: { + for (keywordParameter <- keywordParameters) + n = unset(n, keywordParameter); + insert n; + } + }; + +@javaClass{org.rascalmpl.library.Prelude} +public java node arbNode(); + +@synopsis{Convert a node to a string.} +@examples{ +```rascal-shell +import Node; +F = "f"(10, "abc", color="red", size="large"); +toString(F); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str toString(node T); + +@synopsis{Convert a node to an indented string.} +@examples{ +```rascal-shell +import Node; +F = "f"(10, "abc", color="red", size="large"); +itoString(F); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str itoString(node T); +### |project://rascal/src/org/rascalmpl/library/ParseTree.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Bas Basten - Bas.Basten@cwi.nl (CWI)} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@synopsis{Library functions for parse trees.} +@description{ +A _concrete syntax tree_ or [parse tree](http://en.wikipedia.org/wiki/Parse_tree) is an ordered, rooted tree that +represents the syntactic structure of a string according to some formal grammar. + +Most Rascal users will encounter parse trees in the form of concrete values. +Expert users may find the detailed description here useful when writing generic functions on parse trees. + +In Rascal parse trees, the interior nodes are labeled by rules of the grammar, +while the leaf nodes are labeled by terminals (characters) of the grammar. + +`Tree` is the universal parse tree data type in Rascal and can be used to represent parse trees for any language. + +* `Tree` is a subtype of the type `node` +* All types (non-terminals) declared in `syntax`, `lexical`, `layout` and `keyword` definitions are sub-types of `Tree`. +* All concrete syntax expressions produce parse trees with a type corresponding to a non-terminal. +* Trees can be annotated in various ways. Most importantly the `\loc` annotation always points to the source location of any (sub) parse tree. + +_Advanced users_ may want to create tools that analyze any parse tree, regardless of the +syntax definition that generated it, you can manipulate them on the abstract level. + +A parse tree is of type ((ParseTree-Tree)) using the auxiliary types +((ParseTree-Production)), ((ParseTree-Symbol)), ((ParseTree-Condition)), +((ParseTree-Attr)), ((ParseTree-Associativity)), ((ParseTree-CharRange)). +Effectively, 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 rule in a + syntax definition. + 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 Unicode table. Specifically the +integer value is a decimal [codepoint](https://en.wikipedia.org/wiki/Code_point). A Unicode codepoint +stands for an abstract character: an atomic unit of textual data. By the way, the first 127 characters are the +same as the ASCII 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: + +* Directly on the `Tree` level, just like any other algebraic data type. +* Using concrete syntax expressions and concrete syntax patterns. +* Using disambiguation actions (parameters of the `parse` function) + +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{ +```rascal-shell +import ParseTree; +syntax A = "a"; +// will make the following succeed: +t = parse(#A,"a"); +t := 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 +```rascal-shell +import ParseTree; +lexical B= myB:"b"; +lexical C = myC:"c" B bLabel; +// Will make the following succeed: +t = parse(#C,"cb"); +t := appl( + prod( + label( + "myC", + lex("C")), + [ + lit("c"), + label( + "bLabel", + lex("B")) + ], + {}), + [appl( + prod( + lit("c"), + [\char-class([range(99,99)])], + {}), + [char(99)]),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`. +} +@benefits{ +* Parse trees have all the necessary information in them for high-fidelity source code analysis and transformation +* Parse trees contain full definitions of the grammar rules that it applies +* Parse trees can always be "unparsed" to source text again +} +@pitfalls{ +* For historical reasons the name of the annotation is "loc" and this interferes with the Rascal keyword `loc` +for the type of source locations. Therefore the annotation name has to be escaped as `\loc` when it is declared or used. +* We are in transition from deprecating the annotation `@\loc` with the keyword field `.src=|unknown:///|`. Currently the +run-time already uses `.src` while the source code still uses `@\loc`. +} +module ParseTree + +extend List; +extend Message; +extend Type; + +import Node; +import Set; + +@synopsis{The Tree data type as produced by the parser.} +@description{ +A `Tree` defines the trees normally found after parsing; additional constructors exist for exceptional cases: + +<1> Parse tree constructor when parse succeeded. +<2> Cyclic parsetree. +<3> Ambiguous subtree. +<4> A single character. +} +data Tree (loc parseError = |unknown:///|(0, 0, <0, 0>, <0, 0>)) + //loc src = |unknown:///|(0,0,<0,0>,<0,0>) + = appl(Production prod, list[Tree] args) + // <1> + | cycle(Symbol symbol, int cycleLength) + // <2> + | amb(set[Tree] alternatives) + // <3> + | char(int character) + // <4> + ; + +@synopsis{Production in ParseTrees} +@description{ +The type `Production` is introduced in ((Library:module:Type)), see ((Type-Production)). Here we extend it with the symbols +that can occur in a ParseTree. We also extend productions with basic combinators allowing to +construct ordered and un-ordered compositions, and associativity groups. + +<1> A `prod` is a rule of a grammar, with a defined non-terminal, a list + of terminal and/or non-terminal symbols and a possibly empty set of attributes. + +<2> A `regular` is a regular expression, i.e. a repeated construct. + +<3> `priority` means operator precedence, where the order of the list indicates the binding strength of each rule; +<4> `assoc` means all alternatives are acceptable, but nested on the declared side; +<5> `reference` means a reference to another production rule which should be substituted there, + for extending priority chains and such. +<6> `error` means a node produced by error recovery. +<7> `skipped` means characters skipped during error recovery, always the last child of an `appl` with a `error` production. +} +data Production + = prod(Symbol def, list[Symbol] symbols, set[Attr] attributes) + // <1> + | regular(Symbol def) + // <2> + ; + +data Production + = \priority(Symbol def, list[Production] choices) + // <3> + | \associativity( + Symbol def, Associativity \assoc, set[Production] alternatives) + // <4> + | \reference(Symbol def, str cons) + // <5> + ; + +data Production + = \error(Symbol def, Production prod, int dot) + | \skipped(Symbol def) + ; + +@synopsis{A special exception that wraps errors that are (almost) certainly caused by unexpected parse errors} +@description{ + Certain operations will always succeed on regular parse trees but will fail when the parse tree is an error Tree + resulting from error recovery. + A typical example is `t.someField` where `t` is an error node and `someField` is a field that would normally + be present in the tree but is after the dot so it is missing. + Normally a `NoSuchField("someField")` exception would be thrown but because this problem is caused by a parse error, + the original exception is wrapped like this: `ParseErrorRecovery(NoSuchField("someField"), t.src)`. +} +@Benefits{ +using try/catch you can make a language processor robust against (deeply nested) recovered parse errors without scattering or tangling error handling code everywhere. +} +@pitfalls{ +it is advised to try/catch these exception high up in the call graph of your language processor, otherwise you'll have to write try/catch in many different places +} +data RuntimeException = ParseErrorRecovery(RuntimeException trigger, loc src); + +@synopsis{Attributes in productions.} +@description{ +An `Attr` (attribute) documents additional semantics of a production rule. Neither tags nor +brackets are processed by the parser generator. Rather downstream processors are +activated by these. Associativity is a parser generator feature though. +} +data Attr + = \bracket() + | \assoc(Associativity \assoc) + ; + +@synopsis{Associativity attribute.} +@description{ +Associativity defines the various kinds of associativity of a specific production. +} +data Associativity + = \left() + | \right() + | \assoc() + | \non-assoc() + ; + +@synopsis{Character ranges and character class} +@description{ +* `CharRange` defines a range of characters. +* A `CharClass` consists of a list of characters ranges. +} +data CharRange = range(int begin, int end); + +alias CharClass = list[CharRange]; + +@synopsis{Symbols that can occur in a ParseTree} +@description{ +The type `Symbol` is introduced in ((Library:module:Type)), see ((Type-Symbol)), to represent the basic Rascal types, +e.g., `int`, `list`, and `rel`. Here we extend it with the symbols that may occur in a ParseTree. + +<1> The `start` symbol wraps any symbol to indicate that it is a start symbol of the grammar and + may occur at the root of a parse tree. +<2> Context-free non-terminal +<3> Lexical non-terminal +<4> Layout symbols +<5> Terminal symbols that are keywords +<6> Parameterized context-free non-terminal +<7> Parameterized lexical non-terminal +<8> Terminal. +<9> Case-insensitive terminal. +<10> Character class +<11> Empty symbol +<12> Optional symbol +<13> List of one or more symbols without separators +<14> List of zero or more symbols without separators +<15> List of one or more symbols with separators +<16> List of zero or more symbols with separators +<17> Alternative of symbols +<18> Sequence of symbols +<19> Conditional occurrence of a symbol. +} +data Symbol // <1> + = \start(Symbol symbol); + +// These symbols are the named non-terminals. +data Symbol + = \sort(str name) + // <2> + | \lex(str name) + // <3> + | \layouts(str name) + // <4> + | \keywords(str name) + // <5> + | \parameterized-sort(str name, list[Symbol] parameters) + // <6> + | \parameterized-lex(str name, list[Symbol] parameters) + // <7> + ; + +// These are the terminal symbols. +data Symbol + = \lit(str string) + // <8> + | \cilit(str string) + // <9> + | \char-class(list[CharRange] ranges) + // <10> + ; + +// These are the regular expressions. +data Symbol + = \empty() + // <11> + | \opt(Symbol symbol) + // <12> + | \iter(Symbol symbol) + // <13> + | \iter-star(Symbol symbol) + // <14> + | \iter-seps(Symbol symbol, list[Symbol] separators) + // <15> + | \iter-star-seps(Symbol symbol, list[Symbol] separators) + // <16> + | \alt(set[Symbol] alternatives) + // <17> + | \seq(list[Symbol] symbols) + // <18> + ; + +data Symbol // <19> + = \conditional(Symbol symbol, set[Condition] conditions); + +bool subtype(Symbol::\sort(_), Symbol::\adt("Tree", _)) = true; + +@synopsis{Datatype for declaring preconditions and postconditions on symbols} +@description{ +A `Condition` can be attached to a symbol; it restricts the applicability +of that symbol while parsing input text. For instance, `follow` requires that it +is followed by another symbol and `at-column` requires that it occurs +at a certain position in the current line of the input text. +} +data Condition + = \follow(Symbol symbol) + | \not-follow(Symbol symbol) + | \precede(Symbol symbol) + | \not-precede(Symbol symbol) + | \delete(Symbol symbol) + | \at-column(int column) + | \begin-of-line() + | \end-of-line() + | \except(str label) + ; + +@synopsis{Nested priority is flattened.} +Production priority( + Symbol s, + [*Production a, priority(Symbol _, list[Production] b), *Production c] +) + = priority(s, a + b + c); + +@synopsis{Normalization of associativity.} +@description{ +* The ((Type-choice)) constructor under associativity is flattened. +* Nested (equal) associativity is flattened. +* ((ParseTree-priority)) under an associativity group defaults to choice. +} +Production associativity( + Symbol s, Associativity as, + {*Production a, choice(Symbol t, set[Production] b)} +) + = associativity(s, as, a + b); + +Production associativity( + Symbol rhs, Associativity a, + {associativity(rhs, Associativity b, set[Production] alts), *Production rest} +) + = associativity(rhs, a, rest + alts); + +// the nested associativity, even if contradictory, is lost +Production associativity( + Symbol s, Associativity as, + {*Production a, priority(Symbol t, list[Production] b)} +) + = associativity(s, as, {*a, *b}); + +@synopsis{Annotate a parse tree node with a source location.} +@description{ +A generated ((parser)) will produce ((Tree)) instances annotated with @\loc. In this way every node knows its own precise +range in the file, _and_ the file it originally came from. The ((reposition)) function +can simulate the same behavior without erasing other information (keyword parameters) that was produced after parsing. + +It is here, with ((parser)), ((parsers)) and ((reposition)), that location information is given its exact semantics for parse ((Tree))s: +* The URI points to a single file location that is the source (or target) for the current parse tree. +* Right after parsing and repositioning, the URI is the same for all \@loc annotation in a single ((Tree)) instance. +However, after tree rewriting this is not the case anymore. +* The `offset` is _zero-based_, inclusive, and is increasing from left to right, as long as the tree has not changed yet. +The offset of the very first character in a file is `0`. +* The `length` is always zero or positive. The length of a character (Unicode codepoint) is always 1, even if it is a control +code like `\n` or `\r`. Even `\t` has length `1`! +* The `begin.line` is _one-based_, inclusive, and increasing from top to bottom, as long as the tree has not changed yet. This follows the +POSIX convention that the first line on a screen or a punch card is labeled with `1`. +* The `begin.column` is _zero-based_, inclusive, and increasing from left-to-right, as long as the tree has not changed yet. +The column is reset to `0` on `\r` and `\n` characters. Zero based columns are also a POSIX convention. It is sometimes motivated +by the `|` bar cursor being _before_ the first character initially. +* The `end.line` is _one-based_ and inclusive, always larger or equal than `begin.line`. +* The `end.column` is _zero-based_ and inclusive, and _not_ always larger or equal than `begin.column`. That's true only if `begin.line == end.line`. +} +@benefits{ +* @\loc can be used to point to the origins of trees, even if rewritten parse trees are composed of values +from different sources, their @\loc value will explain where they come from. This can be used to construct +debugging interfaces for DSLs and PLs, for example. +* @\loc contains offset/length and line/column information to cater for all kinds of different ways that editors work. +* @\loc follows POSIX conventions to help in minimizing off-by-one errors when mapping to editor APIs +* @\loc indexes work on the basic concept of an "abstract character", namely Unicode codepoints. The character is +what most easily relates to what a users sees as a character on the screen. +} +@pitfalls{ +* @\loc is based on Unicode's abstract characters, a.k.a. codepoints. If your editor is byte-based or follows another character +encoding than the 24-bit integer codepoints (e.g. java/javascript 16-bit characters), then you need smart just-in-time bidirectional +conversion methods to make sure selection and highlighting ranges (for example) are always exact. +* If a concrete character ("grapheme") on screen is composed of several abstract characters ("codepoints"), then the @\loc +character metaphor breaks. It depends on how the editor internally handles graphemes and on the way it is connected to Rascal +what the effect for the user is. +* @\loc annotations make ((Tree)) instances _unique_ ,where otherwise they could be semantically and syntactically equivalent. +Therefore if you want to test for ((Tree)) (in)equality, always use `t1 := t2` and `t1 !:= t2`. Pattern matching already automatically +ignores @\loc annotations and whitespace and comments. +* Annotated trees are strictly too big for optimal memory usage. Often `@\loc` is the first and only annotation, so it introduces a map for keyword parameters +for every node. Also more nodes are different, impeding in optimal reference sharing. If you require long time storage of many +parse trees it may be useful to strip them of annotations for selected categories of nodes, using ((reposition)). +} anno + loc + Tree + @ + \loc +; + +@synopsis{Parse input text (from a string or a location) and return a parse tree.} +@description{ +* Parse a string and return a parse tree. +* Parse a string and return a parse tree, `origin` defines the original location of the input. +* Parse the contents of resource input and return a parse tree. + +The parse either throws ParseError exceptions or returns parse trees of type ((Tree)). + +The `allowAmbiguity` flag dictates the behavior of the parser in the case of ambiguity. When `allowAmbiguity=true` +the parser will construct ambiguity clusters (local sets of parse trees where the input string is ambiguous). If it is `false` +the parser will throw an `Ambiguous` exception instead. An `Ambiguous` exception is comparable to a ParseError exception then. +The latter option terminates much faster, i.e. always in cubic time, and always linear in the size of the intermediate parse graph, +while constructing ambiguous parse forests may grow to O(n^p+1), where p is the length of the longest production rule and n +is the length of the input. + +The `maxAmbDepth` parameter is used to limit the depth of ambiguity clusters. If the depth of a cluster exceeds this value, a random +alternative is chosen and the rest of the alternatives are ignored. So for instance when `maxAmbDepth` is set to 1, the first level of +ambiguities is produced normally, but no ambiguities within ambiguities will be produced. This feature is primarily useful when using +error recovery (see below) as error recovery tends to generate many nested ambiguities. Without limiting the depth of ambiguities, the parser +parse forests generated by error recovery can easily become too large to process. + +The `allowRecovery` can be set to `true` to enable error recovery. This is an experimental feature. +When error recovery is enabled, the parser will attempt to recover from parse errors and continue parsing. +If successful, a parse tree with error and skipped productions is returned (see the definition of `Production` above). +The `util::ErrorRecovery` module contains a number of functions to analyze trees with errors, for example `hasErrors`, `getSkipped`, and `getErrorText`. +Note that the resulting parse forest can contain a lot of ambiguities. Any code that processes error trees must be aware of this, +for instance a simple traversal of all subtrees will be too expensive in most cases. `disambiguateParseErrors` can be used to +efficiently prune the forest and leave a tree with a single (or even zero) errors based on simple heuristics, but these heuristics +are somewhat arbitrary so the usability of this function is limited. + +The `filters` set contains functions which may be called optionally after the parse algorithm has finished and just before +the Tree representation is built. The set of functions contain alternative functions, only on of them is successfully applied +to each node in a tree. If such a function fails to apply, the other ones are tried. There is no fixed-point computation, so +composed filters must be added to the set of filters programmatically. Post-parse filtering is best done at this stage and +not later on the Tree representation for efficiency reasons. Namely, the size of the parse graph before Tree construction +is still cubic due to "binarized" sharing of intermediate nodes, while after Tree construction the forest may obtain +a size in O(n^p+1) where n is the length of the input and p is the length of the longest syntax rule. Filtering using +the `filters` parameter, on the other hand, may very well cut the forest quickly down to even a linear size and result in +an efficient overall parsing algorithm. + +The `hasSideEffects` flag is normally set to false. When the `filters` functions have side-effects to +remove ambiguity, this option must be set to `true` to ensure correct behavior. A side-effect of filter functions is +typically the construction of a symbol table and the removal (see [[Statements/Filter]]) of syntax trees which refer to +undefined symbols. In such a case `hasSideEffects` must be set to `true` for correctness' sake. If its set to `false` +then the algorithm assumes tree construction is context-free and it can memoize the results of shared intermediate graph nodes. +The tree construction algorithm is effectively always worst case +polynomial in O(n^p+1) --p being the length of the longest syntax rule-- when `hasSideEffects` is true, but may be linear when set +to false. So this is quite an important flag to consider. +} +@examples{ +```rascal-shell +lexical Number = [0-9]+; +syntax Exp + = Number + | left Exp "+" Exp + ; +import ParseTree; +``` +Seeing that `parse` returns a parse tree: +```rascal-shell,continue +parse(#Exp, "2+3"); +``` +Catching a parse error: +```rascal-shell,continue +import IO; +try { + Exp e = parse(#Exp, "2@3"); +} +catch ParseError(loc l): { + println("I found a parse error at line , column "); +} +``` +} +&T <: Tree parse( + type[&T <: Tree] begin, + str input, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +) + = parser( + begin, + allowAmbiguity = allowAmbiguity, + maxAmbDepth = maxAmbDepth, + allowRecovery = allowRecovery, + maxRecoveryAttempts = maxRecoveryAttempts, + maxRecoveryTokens = maxRecoveryTokens, + hasSideEffects = hasSideEffects, + filters = filters + )( + input, |unknown:///| + ); + +&T <: Tree parse( + type[&T <: Tree] begin, + str input, + loc origin, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +) + = parser( + begin, + allowAmbiguity = allowAmbiguity, + maxAmbDepth = maxAmbDepth, + allowRecovery = allowRecovery, + maxRecoveryAttempts = maxRecoveryAttempts, + maxRecoveryTokens = maxRecoveryTokens, + hasSideEffects = hasSideEffects, + filters = filters + )( + input, origin + ); + +&T <: Tree parse( + type[&T <: Tree] begin, + loc input, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +) + = parser( + begin, + allowAmbiguity = allowAmbiguity, + maxAmbDepth = maxAmbDepth, + allowRecovery = allowRecovery, + maxRecoveryAttempts = maxRecoveryAttempts, + maxRecoveryTokens = maxRecoveryTokens, + hasSideEffects = hasSideEffects, + filters = filters + )( + input, input + ); + +@synopsis{Generates a parser from an input grammar.} +@description{ +This builtin function wraps the Rascal parser generator by transforming a grammar into a parsing function. + +The resulting parsing function has the following overloaded signature: + + * Tree parse(str input, loc origin); + * Tree parse(loc input, loc origin); + +So the parse function reads either directly from a str or via the contents of a loc. It also takes a `origin` parameter +which leads to the prefix of the `src` fields of the resulting tree. + +The parse function behaves differently depending of the given keyword parameters: + * `allowAmbiguity`: if true then no exception is thrown in case of ambiguity and a parse forest is returned. if false, + the parser throws an exception during tree building and produces only the first ambiguous subtree in its message. + if set to `false`, the parse constructs trees in linear time. if set to `true` the parser constructs trees in polynomial time. + * `maxAmbDepth`: can be used to limit the maximum depth of ambiguity clusters. For instance, when set to 1, no ambiguities within ambiguities are produced. + When set to 0, all ambiguity clusters will be removed. When set to -1 no ambiguity filtering will take place. Note that ambiguity pruning + is done by dropping all but the first alternative (which is more or less random but repeatable). + * 'allowRecovery`: ***experimental*** if true, the parser tries to recover when it encounters a parse error. if a parse error is encountered that can be recovered from, + special `error` and `skipped` productions are included in the resulting parse tree. More documentation will be added here when this feature matures. + Note that if `allowRecovery` is set to true, the resulting tree can still contain ambiguity nodes related to recovered parse errors, even if `allowAmbiguity` + is set to false. When a 'regular` (non-error) ambiguity is found an exception is still thrown in this case. + * `maxRecoveryAttempts`: *** experimental *** the maximum number of recovery attempts to make when a parse error is encountered. + Only relevant when `allowRecovery` is set to true. + * `maxRecoveryTokens`: *** experimental *** the maximum number of tokens considered on each recovery attempt. + Only relevant when `allowRecovery` is set to true. + * `hasSideEffects`: if false then the parser is a lot faster when constructing trees, since it does not execute the parse _actions_ in an + interpreted environment to make side effects (like a symbol table) and it can share more intermediate results as a result. +} +@javaClass{org.rascalmpl.library.Prelude} +java &T(value input, loc origin) parser( + type[&T] grammar, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); + +@javaClass{org.rascalmpl.library.Prelude} +@synopsis{Generates a parser function that can be used to find the left-most deepest ambiguous sub-sentence.} +@benefits{ +* Instead of trying to build a polynomial-sized parse forest, this function only builds the smallest part of +the tree that exhibits ambiguity. This can be done very quickly, while the whole forest could take minutes to hours to construct. +* Use this function for ambiguity diagnostics and regression testing for ambiguity. +} +@pitfalls{ +* The returned sub-tree usually has a different type than the parameter of the type[] symbol that was passed in. +The reason is that sub-trees typically have a different non-terminal than the start non-terminal of a grammar. +} +java Tree(value input, loc origin) firstAmbiguityFinder( + type[Tree] grammar, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); + +@synopsis{Generates parsers from a grammar (reified type), where all non-terminals in the grammar can be used as start-symbol.} +@description{ +This parser generator behaves the same as the `parser` function, but it produces parser functions which have an additional +nonterminal parameter. This can be used to select a specific non-terminal from the grammar to use as start-symbol for parsing. +} +@javaClass{org.rascalmpl.library.Prelude} +java &U(type[&U] nonterminal, value input, loc origin) parsers( + type[&T] grammar, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); + +@javaClass{org.rascalmpl.library.Prelude} +@synopsis{Generates a parser function that can be used to find the left-most deepest ambiguous sub-sentence.} +@benefits{ +* Instead of trying to build a polynomial-sized parse forest, this function only builds the smallest part of +the tree that exhibits ambiguity. This can be done very quickly, while the whole forest could take minutes to hours to construct. +* Use this function for ambiguity diagnostics and regression testing for ambiguity. +} +@pitfalls{ +* The returned sub-tree usually has a different type than the parameter of the type[] symbol that was passed in. +The reason is that sub-trees typically have a different non-terminal than the start non-terminal of a grammar. +} +java Tree(type[Tree] nonterminal, value input, loc origin) firstAmbiguityFinders( + type[Tree] grammar, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); + +@synopsis{Parse the input but instead of returning the entire tree, return the trees for the first ambiguous substring.} +@description{ +This function is similar to the ((parse)) function in its functionality. However, in case of serious ambiguity parse +could be very slow. This function is much faster, because it does not try to construct an entire forest and thus avoids +the cost of constructing nested ambiguity clusters. + +If the input sentence is not ambiguous after all, simply the entire tree is returned. +} +Tree firstAmbiguity(type[Tree] begin, str input) + = firstAmbiguityFinder(begin)(input, |unknown:///|); + +Tree firstAmbiguity(type[Tree] begin, loc input) + = firstAmbiguityFinder(begin)(input, input); + +@javaClass{org.rascalmpl.library.Prelude} +@synopsis{Generate a parser and store it in serialized form for later reuse.} +@description{ +The stored parsers would be able to be recovered later using ((loadParsers)). + +For any concrete grammar, a.k.a. reified syntax type, `g` it holds that: +* after `storeParsers(g, file);` +* then `g = loadParsers(file);` +* and given `h = parsers(g);` +* then for all valid `nt`, `input` and `origin`: `g(nt, input, origin) == h(nt, input, origin)` + +In other words, a loaded parser function behaves exactly as a freshly generated parser +for the same grammar, if (and only if) it was stored for the same grammar value. +} +@benefits{ +* storing parsers is to cache the work of reifing a grammar, and generating a parser from that grammar. +* stored parsers are nice for deployment scenarios where the language is fixed and efficiency is appreciated. +} +@pitfalls{ +* caching parsers with `storeParsers` is your responsibility; the cache is not cleaned automatically when the grammar changes. +} +java void storeParsers(type[Tree] grammar, loc saveLocation); + +@javaClass{org.rascalmpl.library.Prelude} +@synopsis{Load a previously serialized parser from disk for usage} +@description{ +For any concrete grammar, a.k.a. reified syntax type, `g` it holds that: +* after `storeParsers(g, file);` +* then `g = loadParsers(file);` +* and given `h = parsers(g);` +* then for all valid `nt`, `input` and `origin`: `g(nt, input, origin) == h(nt, input, origin)` + +In other words, a loaded parser function behaves exactly as a freshly generated parser +for the same grammar, if (and only if) it was stored for the same grammar value. +} +@examples{ +First we store a parser: +```rascal-shell +import ParseTree; +syntax E = E "+" E | "e"; +storeParsers(#E, |memory://test-tmp/E.parsers|) +``` + +Here we show a new shell does not even know about the grammar: +```rascal-shell,errors +#E +``` + +Then in a next run, we load the parser and use it: +```rascal-shell +import ParseTree; +p = loadParsers(|memory://test-tmp/E.parsers|); +p(type(sort("E"), ()), "e+e", |src:///|); +``` +} +@benefits{ +* loaded parsers can be used immediately without the need of loading and executing a parser generator. +} +@pitfalls{ +* reifiying types (use of `#`) will trigger the loading of a parser generator anyway. You have to use +this notation for types to avoid that: `type(\start(sort("MySort")), ())` to avoid the computation for `#start[A]` +} +java &U(type[&U] nonterminal, value input, loc origin) loadParsers( + loc savedParsers, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); + +@synopsis{Load a previously serialized parser, for a specific non-terminal, from disk for usage} +@description{ +This loader behaves just like ((loadParsers)), except that the resulting parser function is already +bound to a specific non-terminal. +} +@javaClass{org.rascalmpl.library.Prelude} +java &U(value input, loc origin) loadParser( + type[&U] nonterminal, + loc savedParsers, + bool allowAmbiguity = false, + int maxAmbDepth = 2, + bool allowRecovery = false, + int maxRecoveryAttempts = 30, + int maxRecoveryTokens = 3, + bool hasSideEffects = false, + set[Tree(Tree)] filters = {} +); + +@synopsis{Yield the string of characters that form the leafs of the given parse tree.} +@description{ +`unparse` is the inverse function of ((ParseTree-parse)), i.e., for every syntactically correct string `TXT` of +type `S`, the following holds: +```rascal +unparse(parse(#S, TXT)) == TXT +``` +} +@examples{ +```rascal-shell +syntax Exp = Exp "+" Exp | Number; +lexical Number = [0-9]+; +import ParseTree; +``` +First parse an expression, this results in a parse tree. Then unparse this parse tree: +```rascal-shell,continue +unparse(parse(#Exp, "2+3")); +``` +} +str unparse(Tree tree) = ""; + +@javaClass{org.rascalmpl.library.Prelude} +java str printSymbol(Symbol sym, bool withLayout); + +@javaClass{org.rascalmpl.library.Prelude} +@synopsis{Implode a parse tree according to a given (ADT) type.} +@description{ +Given a grammar for a language, its sentences can be parsed and the result is a parse tree +(or more precisely a value of type `Tree`). For many applications this is sufficient +and the results are achieved by traversing and matching them using concrete patterns. + +In other cases, the further processing of parse trees is better done in a more abstract form. +The [abstract syntax](http://en.wikipedia.org/wiki/Abstract_syntax) for a language is a +data type that is used to represent programs in the language in an _abstract_ form. +Abstract syntax has the following properties: + +* It is "abstract" in the sense that it does not contain textual details such as parentheses, + layout, and the like. +* While a language has one grammar (also known as, _concrete syntax_) it may have several abstract syntaxes + for different purposes: type analysis, code generation, etc. + + +The function `implode` bridges the gap between parse tree and abstract syntax tree. +Given a parse tree and a Rascal type it traverses them simultaneously and constructs +an abstract syntax tree (a value of the given type) as follows: + +* Literals, layout and empty (i.e. ()) nodes are skipped. + +* Regular */+ lists are imploded to `list`s or `set`s depending on what is + expected in the ADT. + +* Ambiguities are imploded to `set`s. + +* If the expected type is `str` the tree is unparsed into a string. This happens for both + lexical and context-free parse trees. + +* If a tree's production has no label and a single AST (i.e. non-layout, non-literal) argument + (for instance, an injection), the tree node is skipped, and implosion continues + with the lone argument. The same applies to bracket productions, even if they + are labeled. + +* If a tree's production has no label, but more than one argument, the tree is imploded + to a tuple (provided this conforms to the ADT). + +* Optionals are imploded to booleans if this is expected in the ADT. + This also works for optional literals, as shown in the example below. + +* An optional is imploded to a list with zero or one argument, iff a list + type is expected. + +* If the argument of an optional tree has a production with no label, containing + a single list, then this list is spliced into the optional list. + +* For trees with (cons-)labeled productions, the corresponding constructor + in the ADT corresponding to the non-terminal of the production is found in + order to make the AST. + +* If the provided type is `node`, (cons-)labeled trees will be imploded to untyped `node`s. + This means that any subtrees below it will be untyped nodes (if there is a label), tuples of + nodes (if a label is absent), and strings for lexicals. + +* Unlabeled lexicals are imploded to str, int, real, bool depending on the expected type in + the ADT. To implode lexical into types other than str, the PDB parse functions for + integers and doubles are used. Boolean lexicals should match "true" or "false". + NB: lexicals are imploded this way, even if they are ambiguous. + +* If a lexical tree has a cons label, the tree imploded to a constructor with that name + and a single string-valued argument containing the tree's yield. + + +An `IllegalArgument` exception is thrown if during implosion a tree is encountered that cannot be +imploded to the expected type in the ADT. As explained above, this function assumes that the +ADT type names correspond to syntax non-terminal names, and constructor names correspond +to production labels. Labels of production arguments do not have to match with labels + in ADT constructors. + +Finally, source location fields are propagated as keyword fields on constructor ASTs. +To access them, the user is required to explicitly declare a keyword field on all +ADTs used in implosion. In other words, for every ADT type `T`, add: + +```rascal-commands +data T(loc location=|unknown:///|); +``` +} +@examples{ +Here are some examples for the above rules. + +* Example for rule 5. Given the grammar +```rascal +syntax IDTYPE = Id ":" Type; +syntax Decls = decls: "declare" {IDTYPE ","}* ";"; +``` +`Decls` will be imploded as: +```rascal +data Decls = decls(list[tuple[str,Type]]); +``` +(assuming Id is a lexical non-terminal). +* Example for rule 6. Given the grammar +```rascal +syntax Formal = formal: "VAR"? {Id ","}+ ":" Type; +``` +The corresponding ADT could be: +```rascal +data Formal = formal(bool, list[str], Type); +``` +* Example for rule 8. Given the grammar +```rascal +syntax Tag = "[" {Modifier ","}* "]"; +syntax Decl = decl: Tag? Signature Body; +``` +In this case, a `Decl` is imploded into the following ADT: +```rascal +data Decl = decl(list[Modifier], Signature, Body); +``` +* Example for rule 9. Given the grammar +```rascal +syntax Exp = left add: Exp "+" Exp; +``` +Can be imploded into: +```rascal +data Exp = add(Exp, Exp); +``` +} +java &T <: value implode(type[&T <: value] t, Tree tree); + +@synopsis{Tree search result type for ((treeAt)).} +data TreeSearchResult[&T <: Tree] + = treeFound(&T tree) + | treeNotFound() + ; + +@synopsis{Select the innermost Tree of a given type which is enclosed by a given location.} +@description{ + +} +TreeSearchResult[&T <: Tree] treeAt( + type[&T <: Tree] t, loc l, Tree a: appl(_, _) +) { + if ((a@\loc)?, al := a@\loc, al.offset <= l.offset, al.offset + al.length >= l.offset + l.length) + { + for (arg <- a.args, TreeSearchResult[&T <: Tree] r: treeFound(&T <: Tree _) := treeAt(t, l, arg)) { + return r; + } + + if (&T <: Tree tree := a) { + return treeFound(tree); + } + } + return treeNotFound(); +} + +default TreeSearchResult[&T <: Tree] treeAt( + type[&T <: Tree] t, loc l, Tree root +) + = treeNotFound(); + +bool sameType(label(_, Symbol s), Symbol t) = sameType(s, t); +bool sameType(Symbol s, label(_, Symbol t)) = sameType(s, t); +bool sameType(Symbol s, conditional(Symbol t, _)) = sameType(s, t); +bool sameType(conditional(Symbol s, _), Symbol t) = sameType(s, t); +bool sameType(Symbol s, s) = true; +default bool sameType(Symbol s, Symbol t) = false; + +@synopsis{Determine if the given type is a non-terminal type.} +bool isNonTerminalType(Symbol::\sort(str _)) = true; +bool isNonTerminalType(Symbol::\lex(str _)) = true; +bool isNonTerminalType(Symbol::\layouts(str _)) = true; +bool isNonTerminalType(Symbol::\keywords(str _)) = true; +bool isNonTerminalType( + Symbol::\parameterized-sort(str _, list[Symbol] _) +) + = true; +bool isNonTerminalType(Symbol::\parameterized-lex(str _, list[Symbol] _)) + = true; +bool isNonTerminalType(Symbol::\start(Symbol s)) = isNonTerminalType(s); +default bool isNonTerminalType(Symbol s) = false; + +private alias NewLineChar = [\n]; + +@synopsis{Re-compute and overwrite origin locations for all sub-trees of a ((Tree))} +@description{ +This function takes a ((Tree)) and overwrites the old \loc annotations of every subtree +with fresh locations. The new locations are as-if the file was parsed again from the unparsed result: +the locations describe the left-to-right order of the sub-trees again exactly, and they are all +from the same top-level location (read "file"). + +Typically, with the default options, this algorithm changes _nothing_ in a ((Tree)) which +has just been produced by the parser. It will rebuild the tree and recompute the exact +locations as they were originally. However, there are many reasons why the (location) fields +in a ((Tree)) are not at all anymore what they were just after parsing: +1. subtrees may have been removed +2. subtrees may have been relocated to different parts of the tree; +2. subtrees may have been introduced from other source files +3. subtrees may have been introduced from concrete syntax expressions in Rascal code. +4. other algorithms may have added more keyword fields, for example fully resolved qualified names, +resolved types, error messages or future computations (closures). +5. location fields themselves may have been lost accidentally when rewriting trees with `visit` +6. etc. + +Some downstream algorithms (e.g. ((HiFiLayoutDiff)) ) require source locations to be consistent with the current actual position +of every source tree. ((reposition)) provides this contract. Even if one of the above transformations have happened, +after ((reposition)) every node has an accurate position with respect to the hypothetical file contents that would be generated +if the tree is unparsed (written to a string or a file). + +Next to this feature, ((reposition)) may add locations to ((Tree)) nodes which were not annotated +initially by the ((parser)): layout nodes, literal nodes, and sub-lexical nodes. Some algorithms on +parse trees (like formatting), require more detailed location information than provided by the ((parser)): +* markLexical=true, ensures the sub-structure of lexicals is annotated as well. +* markLayout=true, ensures annotating layout nodes and their sub-structure as well. +* markLit=true, ensures literal trees and case-insensitive literal trees are annotated as well. +* markAmb=true, ensures ambiguity nodes are annotated. NB: the sub-structure of a cluster is always annotated according to the other flags. +* etc. every kind of node has a "mark" flag for completeness sake. + +Finally, ((reposition)) can be used to removed superfluous locations from ((Tree)) nodes. Every node which +originally had a position will lose it unless ((reposition)) is configured to recompute it. + +By default ((reposition)) simulates the behavior of a ((parser)) exactly. Reparsing the +yield of a tree should always produce the exact same locations as ((reposition)) does. +} +@benefits{ +* Unlike reparsing, ((reposition)) will maintain all other keyword parameters of ((Tree)) nodes, like resolved qualified names and type attributes. +* Can be used to erase superfluous annotations for memory efficiency, while keeping the essential ones. +* The default mark options simulate the behavior of ((parser)) functions. +} +&T <: Tree reposition( + &T <: Tree tree, + loc file = tree@\loc.top, + bool \markStart = true, + bool \markSyntax = true, + bool \markLexical = true, + bool \markSubLexical = true, + bool \markRegular = true, + bool \markLayout = true, + bool \markSubLayout = true, + bool \markLit = false, + bool \markSubLit = false, + bool \markAmb = false, + bool \markCycle = false, + bool \markChar = false +) { + // the cur variables are shared state by the `rec` local function that recurses over the entire tree + int curOffset = 0; + int curLine = 1; + int curColumn = 0; + + @synopsis{Check if this rule is configured to be annotated} + default bool doAnno(Production _) = false; + bool doAnno(prod(\lex(_), _, _)) = markLexical; + bool doAnno(prod(\label(_, \lex(_)), _, _)) = markLexical; + bool doAnno(prod(\parameterized-lex(_, _), _, _)) = markLexical; + bool doAnno(prod(\label(_, \parameterized-lex(_, _)), _, _)) = markLexical; + bool doAnno(prod(\layouts(_), _, _)) = markLayout; + bool doAnno(prod(\label(_, \layouts(_)), _, _)) = markLayout; + bool doAnno(prod(\sort(_), _, _)) = markSyntax; + bool doAnno(prod(\label(_, \sort(_)), _, _)) = markSyntax; + bool doAnno(prod(\parameterized-sort(_, _), _, _)) = markSyntax; + bool doAnno(prod(\label(_, \parameterized-sort(_, _)), _, _)) = markSyntax; + bool doAnno(\regular(_)) = markRegular; + bool doAnno(prod(\lit(_), _, _)) = markLit; + bool doAnno(prod(\cilit(_), _, _)) = markLit; + bool doAnno(prod(\start(_), _, _)) = markStart; + + @synopsis{Check if sub-structure of this rule is configured to be annotated} + default bool doSub(Production _) = true; + bool doSub(prod(\lex(_), _, _)) = \markSubLexical; + bool doSub(prod(\label(_, lex(_)), _, _)) = \markSubLexical; + bool doSub(prod(\layouts(_), _, _)) = \markSubLayout; + bool doSub(prod(\label(_, \layouts(_)), _, _)) = \markSubLayout; + bool doSub(prod(\lit(_), _, _)) = \markSubLit; + bool doSub(prod(\cilit(_), _, _)) = \markSubLit; + + // the character nodes drive the actual current position: offset, line and column + Tree rec(Tree t: char(int ch), bool _sub) { + beginOffset = curOffset; + beginLine = curLine; + beginColumn = curColumn; + + curOffset += 1; + curColumn += 1; + + switch(t) { + case NewLineChar _: { + curLine += 1; + curColumn = 0; + } + } + + Tree washCC(Tree x) = x; + + // workaround for issue #2342 + return + markChar + ? washCC(char(ch))[@\loc = file( + beginOffset, + 1, + , + + )] + : washCC(char(ch)); + } + + // cycles take no space + Tree rec(cycle(Symbol s, int up), bool _sub) + = markCycle + ? cycle(s, up)[@\loc = file(curOffset, 0, , )] + : cycle(s, up); + + // application nodes always have children to traverse, to get to the individual characters eventually + // different types of nodes lead to annotation, or not, depending on the parameters of ((reposition)) + Tree rec(appl(Production prod, list[Tree] args), bool sub) { + beginOffset = curOffset; + beginLine = curLine; + beginColumn = curColumn; + + // once `sub` is false, going down, we can never turn it on again + newArgs = [mergeRec(a, sub && doSub(prod)) | a <- args]; + + return + (sub && doAnno(prod)) + ? appl(prod, newArgs)[@\loc = file( + beginOffset, + curOffset - beginOffset, + , + + )] + : appl(prod, newArgs); + } + + // ambiguity nodes are simply choices between alternatives which each receive their own positions. + Tree rec(amb(set[Tree] alts), bool sub) { + newAlts = {mergeRec(a, sub)| a <- alts}; + + // inherit the outermost positions from one of the alternatives, since they are all the same by definition. + Tree x = getFirstFrom(newAlts); + return markAmb && x@\loc? ? amb(newAlts)[@\loc = x@\loc] : amb(newAlts); + } + + @synopsis{Recurse, but not without recovering all other keyword parameters except "src" a.k.a. @\loc from the original.} + Tree mergeRec(Tree t, bool sub) { + oldParams = getKeywordParameters(t); + t = rec(t, sub); + newParams = getKeywordParameters(t); + mergedParams = (oldParams - ("src" : |unknown:///|)) + newParams; + return setKeywordParameters(t, mergedParams); + } + + // we start recursion at the top, not forgetting to merge its other keyword fields + return mergeRec(tree, true); +} +### |project://rascal/src/org/rascalmpl/library/Prelude.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@synopsis{All basic utility functions in one handy module to import} +@description{ +Unlike the module name suggests the Prelude module is _not_ automatically imported when Rascal is started. +All it is, is a handy combination of extended modules that will provide the utility functions most +Rascal programmers need. + +Prelude combines the following modules: + +* ((Library:module:Boolean)) +* ((Library:module:DateTime)) +* ((Library:module:Exception)) +* ((Library:module:Grammar)) +* ((Library:module:IO)) +* ((Library:module:List)) +* ((Library:module:ListRelation)) +* ((Library:module:Map)) +* ((Library:module:Node)) +* ((Library:module:ParseTree)) +* ((Library:module:Relation)) +* ((Library:module:Set)) +* ((Library:module:String)) +* ((Library:module:Type)) +* ((Library:module:ValueIO)) +} +@examples{ +```rascal-shell +import Prelude; +println("Hello World"); // from IO +size([1,2,3]) // from List +size({1,2,1}) // from Set +``` +} +@benefits{ +* Prelude makes all the feature of the extended modules transitively available to an importing module. +} +@pitfalls{ +* Prelude combines many many function names and so the namespace of modules that import it is a bit crowded. +} +module Prelude + +extend Boolean; +extend DateTime; +extend Exception; +extend Grammar; +extend IO; +extend List; +extend ListRelation; +extend Map; +extend Node; +extend ParseTree; +extend Relation; +extend Set; +extend String; +extend Type; +extend ValueIO; +### |project://rascal/src/org/rascalmpl/library/Relation.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@synopsis{Library functions for relations.} +@description{ +The following library functions are defined for relations: +(((TOC))) +} +module Relation + +@synopsis{Return the set of all elements in any tuple in a relation.} +@examples{ +```rascal-shell +import Relation; +carrier({<1,10>, <2,20>}); +carrier({<1,10,100,1000>, <2,20,200,2000>}); +``` +} +public set[&T] carrier(rel[&T, &T] R) { + return R<0> + R<1>; +} + +public set[&T] carrier(rel[&T, &T, &T] R) { + return (R<0> + R<1>) + R<2>; +} + +public set[&T] carrier(rel[&T, &T, &T, &T] R) { + return ((R<0> + R<1>) + R<2>) + R<3>; +} + +public set[&T] carrier(rel[&T, &T, &T, &T, &T] R) { + return (((R<0> + R<1>) + R<2>) + R<3>) + R<4>; +} + +@synopsis{A relation restricted to certain element values in tuples.} +@description{ +Returns relation `R` restricted to tuples with elements in set `S`. +} +@examples{ +```rascal-shell +import Relation; +carrierR({<1,10>, <2,20>, <3,30>}, {10, 1, 20}); +``` +} +public rel[&T, &T] carrierR(rel[&T, &T] R, set[&T] S) { + return {| <&T V0, &T V1> <- R, V0 in S, V1 in S}; +} + +public rel[&T, &T, &T] carrierR(rel[&T, &T, &T] R, set[&T] S) { + return {| <&T V0, &T V1, &T V2> <- R, V0 in S, V1 in S, V2 in S}; +} + +public rel[&T, &T, &T, &T] carrierR(rel[&T, &T, &T, &T] R, set[&T] S) { + return + {| <&T V0, &T V1, &T V2, &T V3> <- R, V0 in S, V1 in S, V2 in S, V3 in S}; +} + +public rel[&T, &T, &T, &T, &T] carrierR(rel[&T, &T, &T, &T, &T] R, set[&T] S) { + return + { + | <&T V0, &T V1, &T V2, &T V3, &T V4> <- R, V0 in S, V1 in S, V2 in S, V3 in S, V4 in S + }; +} + +@synopsis{A relation excluding tuples that contain certain element values.} +@examples{ +```rascal-shell +import Relation; +carrierX({<1,10>, <2,20>, <3,30>}, {10, 1, 20}); +``` +} +@synopsis{A relation excluded tuples containing certain values.} +@description{ +Returns relation `R` excluding tuples with some element in `S`. +} +@examples{ +```rascal-shell +import Relation; +carrierX({<1,10>, <2,20>, <3,30>}, {10, 1, 20}); +``` +} +public rel[&T, &T] carrierX(rel[&T, &T] R, set[&T] S) { + return {| <&T V0, &T V1> <- R, V0 notin S, V1 notin S}; +} + +public rel[&T, &T, &T] carrierX(rel[&T, &T, &T] R, set[&T] S) { + return {| <&T V0, &T V1, &T V2> <- R, V0 notin S, V1 notin S, V2 notin S}; +} + +public rel[&T, &T, &T, &T] carrierX(rel[&T, &T, &T, &T] R, set[&T] S) { + return + { + | <&T V0, &T V1, &T V2, &T V3> <- R, V0 notin S, V1 notin S, V2 notin S, V3 notin S + }; +} + +public rel[&T, &T, &T, &T, &T] carrierX(rel[&T, &T, &T, &T, &T] R, set[&T] S) { + return + { + | <&T V0, &T V1, &T V2, &T V3, &T V4> <- R, V0 notin S, V1 notin S, V2 notin S, V3 notin S, V4 notin S + }; +} + +@synopsis{Complement of a relation.} +@description{ +Given a relation `R` a new relation `U` can be constructed that contains +all possible tuples with element values that occur at corresponding tuple positions in `R`. +The function `complement` returns the complement of `R` relative to `U`, in other words: `U - R`. +} +@examples{ +```rascal-shell +import Relation; +``` +Declare `R` and compute corresponding `U`: +```rascal-shell,continue +R = {<1,10>, <2, 20>, <3, 30>}; +U = domain(R) * range(R); +``` +Here is the complement of `R` computed in two ways: +```rascal-shell,continue +U - R; +complement({<1,10>, <2, 20>, <3, 30>}); +``` +} +public rel[&T0, &T1] complement(rel[&T0, &T1] R) { + return (domain(R) * range(R)) - R; +} + +public rel[&T0, &T1, &T2] complement(rel[&T0, &T1, &T2] R) { + return + {| &T0 V0 <- R<0>, &T1 V1 <- R<1>, &T2 V2 <- R<2>, notin R}; +} + +public rel[&T0, &T1, &T2, &T3] complement(rel[&T0, &T1, &T2, &T3] R) { + return + { + | &T0 V0 <- R<0>, &T1 V1 <- R<1>, &T2 V2 <- R<2>, &T3 V3 <- R<3>, notin R + }; +} + +public rel[&T0, &T1, &T2, &T3, &T4] complement(rel[&T0, &T1, &T2, &T3, &T4] R) { + return + { + | &T0 V0 <- R<0>, &T1 V1 <- R<1>, &T2 V2 <- R<2>, &T3 V3 <- R<3>, &T4 V4 <- R<4>, notin R + }; +} + +@synopsis{Domain of a relation: a set consisting of the first element of each tuple.} +@examples{ +```rascal-shell +import Relation; +domain({<1,10>, <2,20>}); +domain({<"mon", 1>, <"tue", 2>}); +``` +} +public set[&T0] domain(rel[&T0, &T1] R) { + return R<0>; +} + +public set[&T0] domain(rel[&T0, &T1, &T2] R) { + return R<0>; +} + +public set[&T0] domain(rel[&T0, &T1, &T2, &T3] R) { + return R<0>; +} + +public set[&T0] domain(rel[&T0, &T1, &T2, &T3, &T4] R) { + return R<0>; +} + +@synopsis{Relation restricted to certain domain elements.} +@description{ +Restriction of a relation `R` to tuples with first element in `S`. +} +@examples{ +```rascal-shell +import Relation; +domainR({<1,10>, <2,20>, <3,30>}, {3, 1}); +``` +} +public rel[&T0, &T1] domainR(rel[&T0, &T1] R, set[&T0] S) { + return {| <&T0 V0, &T1 V1> <- R, V0 in S}; +} + +rel[&T, &U] domainR(rel[&T, &U] R, bool(&T) accept) + = {| <- R, accept(t)}; + +public rel[&T0, &T1, &T2] domainR(rel[&T0, &T1, &T2] R, set[&T0] S) { + return {| <&T0 V0, &T1 V1, &T2 V2> <- R, V0 in S}; +} + +public rel[&T0, &T1, &T2, &T3] domainR(rel[&T0, &T1, &T2, &T3] R, set[&T0] S) { + return {| <&T0 V0, &T1 V1, &T2 V2, &T3 V3> <- R, V0 in S}; +} + +public +rel[&T0, &T1, &T2, &T3, &T4] domainR(rel[&T0, &T1, &T2, &T3, &T4] R, set[&T0] S) { + return {| <&T0 V0, &T1 V1, &T2 V2, &T3 V3, &T4 V4> <- R, V0 in S}; +} + +@synopsis{Relation excluding certain domain values.} +@description{ +Relation `R` excluded tuples with first element in `S`. +} +@examples{ +```rascal-shell +import Relation; +domainX({<1,10>, <2,20>, <3,30>}, {3, 1}); +``` +} +public rel[&T0, &T1] domainX(rel[&T0, &T1] R, set[&T0] S) { + return {| <&T0 V0, &T1 V1> <- R, V0 notin S}; +} + +public rel[&T0, &T1, &T2] domainX(rel[&T0, &T1, &T2] R, set[&T0] S) { + return {| <&T0 V0, &T1 V1, &T2 V2> <- R, V0 notin S}; +} + +public rel[&T0, &T1, &T2, &T3] domainX(rel[&T0, &T1, &T2, &T3] R, set[&T0] S) { + return {| <&T0 V0, &T1 V1, &T2 V2, &T3 V3> <- R, V0 notin S}; +} + +public +rel[&T0, &T1, &T2, &T3, &T4] domainX(rel[&T0, &T1, &T2, &T3, &T4] R, set[&T0] S) { + return {| <&T0 V0, &T1 V1, &T2 V2, &T3 V3, &T4 V4> <- R, V0 notin S}; +} + +@synopsis{Make sets of elements in the domain that relate to the same element in the range.} +@examples{ +```rascal-shell +import Relation; +legs = {<"bird", 2>, <"dog", 4>, <"human", 2>, <"spider", 8>, <"millipede", 1000>, <"crab", 8>, <"cat", 4>}; +groupDomainByRange(legs); +``` +} +public set[set[&U]] groupDomainByRange(rel[&U dom, &T ran] input) { + return (i: (input)[i]| i <- input.ran)<1>; +} + +@synopsis{Make sets of elements in the range that relate to the same element in the domain.} +@description{ +```rascal-shell +import Relation; +skins = {<"bird", "feather">, <"dog", "fur">, <"tortoise", "shell">, <"human", "skin">, <"fish", "scale">, <"lizard", "scale">, <"crab", "shell">, <"cat", "fur">}; +groupRangeByDomain(skins); +``` +} +public set[set[&T]] groupRangeByDomain(rel[&U dom, &T ran] input) { + return (i: input[i]| i <- input.dom)<1>; +} + +@synopsis{The identity relation.} +@description{ +The identity relation for set `S`. +} +@examples{ +```rascal-shell +import Relation; +ident({"mon", "tue", "wed"}); +``` +} +public rel[&T, &T] ident(set[&T] S) { + return {| V <- S}; +} + +@synopsis{Invert the tuples in a relation.} +@examples{ +```rascal-shell +import Relation; +invert({<1,10>, <2,20>}); +``` +} +public rel[&T1, &T0] invert(rel[&T0, &T1] R) { + return R<1,0>; +} + +public rel[&T2, &T1, &T0] invert(rel[&T0, &T1, &T2] R) { + return R<2,1,0>; +} + +public rel[&T3, &T2, &T1, &T0] invert(rel[&T0, &T1, &T2, &T3] R) { + return R<3,2,1,0>; +} + +public rel[&T4, &T3, &T2, &T1, &T0] invert(rel[&T0, &T1, &T2, &T3, &T4] R) { + return R<4,3,2,1,0>; +} + +@synopsis{The range (i.e., all but the first element of each tuple) of a relation.} +@examples{ +```rascal-shell +import Relation; +range({<1,10>, <2,20>}); +range({<"mon", 1>, <"tue", 2>}); +``` +} +public set[&T1] range(rel[&T0, &T1] R) { + return R<1>; +} + +public rel[&T1, &T2] range(rel[&T0, &T1, &T2] R) { + return R<1,2>; +} + +public rel[&T1, &T2, &T3] range(rel[&T0, &T1, &T2, &T3] R) { + return R<1,2,3>; +} + +public rel[&T1, &T2, &T3, &T4] range(rel[&T0, &T1, &T2, &T3, &T4] R) { + return R<1,2,3,4>; +} + +@synopsis{Relation restricted to certain range values.} +@description{ +Restriction of binary relation `R` to tuples with second element in set `S`. +} +@examples{ +```rascal-shell +import Relation; +rangeR({<1,10>, <2,20>, <3,30>}, {30, 10}); +``` +} +public rel[&T0, &T1] rangeR(rel[&T0, &T1] R, set[&T1] S) { + return {| <&T0 V0, &T1 V1> <- R, V1 in S}; +} + +@synopsis{Relation excluding certain range values.} +@description{ +Restriction of binary relation `R` to tuples with second element not in set `S`. +} +@examples{ +```rascal-shell +import Relation; +rangeX({<1,10>, <2,20>, <3,30>}, {30, 10}); +``` +} +public rel[&T0, &T1] rangeX(rel[&T0, &T1] R, set[&T1] S) { + return {| <&T0 V0, &T1 V1> <- R, V1 notin S}; +} + +@synopsis{Indexes a binary relation as a map} +@description{ +Converts a binary relation to a map of the domain to a set of the range. +} +@examples{ +```rascal-shell +import Relation; +index({<1,10>, <2,20>, <3,30>, <30,10>}); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java map[&K, set[&V]] index(rel[&K, &V] R); +### |project://rascal/src/org/rascalmpl/library/Set.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +@synopsis{Library functions for sets.} +@usage{ + +} +@description{ +The following library functions are defined for sets: +(((TOC))) +} +module Set + +import Exception; +import List; +import util::Math; + +@synopsis{Classify elements in a set.} +@examples{ +We classify animals by their number of legs. +```rascal-shell +import Set; +``` +Create a map from animals to number of legs. +```rascal-shell,continue +legs = ("bird": 2, "dog": 4, "human": 2, "snake": 0, "spider": 8, "millipede": 1000, "crab": 8, "cat": 4); +``` +Define function `nLegs` that returns the number of legs for each animal (or `0` when the animal is unknown): +```rascal-shell,continue +int nLegs(str animal){ + return legs[animal] ? 0; +} +``` +Now classify a set of animals: +```rascal-shell,continue +classify({"bird", "dog", "human", "spider", "millipede", "zebra", "crab", "cat"}, nLegs); +``` +} +public +map[&K, set[&V]] classify(set[&V] input, &K(&V) getClass) + = toMap({| e <- input}); + +@synopsis{Group elements in a set given an equivalence function.} +@examples{ +We classify animals by their number of legs. +```rascal-shell +import Set; +``` +Create a map from animals to number of legs. +```rascal-shell,continue +legs = ("bird": 2, "dog": 4, "human": 2, "snake": 0, "spider": 8, "millipede": 1000, "crab": 8, "cat": 4); +``` +Define function `nLegs` that returns the number of legs fro each animal (or `0` when the animal is unknown): +```rascal-shell,continue +int nLegs(str animal){ + return legs[animal] ? 0; +} +bool similar(str a, str b) = nLegs(a) == nLegs(b); +``` +Now group a set of animals: +```rascal-shell,continue +group({"bird", "dog", "human", "spider", "millipede", "zebra", "crab", "cat"}, similar); +``` +WARNING: check compiler. +} +public set[set[&T]] group(set[&T] input, bool(&T a, &T b) similar) { + sinput = sort(input, bool (&T a, &T b) { + return similar(a, b) ? false : a < b; + }); + lres = while(!isEmpty(sinput)) { + h = head(sinput); + sim = h + takeWhile(tail(sinput), bool (&T a) { + return similar(a, h); + }); + append toSet(sim); + sinput = drop(size(sim), sinput); + } + return toSet(lres); +} + +@synopsis{Map set elements to a fixed index.} +@examples{ +```rascal-shell +import Set; +index({"elephant", "zebra", "snake"}); +``` +} +public map[&T, int] index(set[&T] s) { + sl = toList(s); + return (sl[i]: i| i <- index(sl)); +} + +@synopsis{Test whether a set is empty.} +@description{ +Yields `true` if `s` is empty, and `false` otherwise. +} +@examples{ +```rascal-shell +import Set; +isEmpty({1, 2, 3}); +isEmpty({}); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool isEmpty(set[&T] st); + +@synopsis{Apply a function to all set elements and return set of results.} +@description{ +Return a set obtained by applying function `fn` to all elements of set `s`. +} +@examples{ +```rascal-shell +import Set; +int incr(int x) { return x + 1; } +mapper({1, 2, 3, 4}, incr); +``` +} +public set[&U] mapper(set[&T] st, &U(&T) fn) { + return {fn(elm)| &T elm <- st}; +} + +@synopsis{Determine the largest element of a set.} +@examples{ +```rascal-shell +import Set; +max({1, 3, 5, 2, 4}); +max({"elephant", "zebra", "snake"}); +``` +} +public &T max(set[&T] st) { + = takeOneFrom(st); + return ( h | e > it ? e : it | e <- t ); +} + +@synopsis{Smallest element of a set.} +@examples{ +```rascal-shell +import Set; +min({1, 3, 5, 2, 4}); +min({"elephant", "zebra", "snake"}); +``` +} +@synopsis{Determine the smallest element of a set.} +@examples{ +```rascal-shell +import Set; +min({1, 3, 5, 4, 2}); +``` +} +public &T min(set[&T] st) { + = takeOneFrom(st); + return ( h | e < it ? e : it | e <- t ); +} + +@synopsis{Determine the powerset of a set.} +@description{ +Returns a set with all subsets of `s`. +} +@examples{ +```rascal-shell +import Set; +power({1,2,3,4}); +``` +} +public set[set[&T]] power(set[&T] st) { + // the power set of a set of size n has 2^n-1 elements + // so we enumerate the numbers 0..2^n-1 + // if the nth bit of a number i is 1 then + // the nth element of the set should be in the + // ith subset + stl = [*st]; + i = 0; + res = while(i < pow(2, size(st))) { + j = i; + elIndex = 0; + sub = while(j > 0) { + ; + if (j mod 2 == 1) { + append stl[elIndex]; + } + elIndex += 1; + j /= 2; + } + append {*sub}; + i += 1; + } + return {*res}; +} + +@synopsis{The powerset (excluding the empty set) of a set value.} +@description{ +Returns all subsets (excluding the empty set) of `s`. +} +@examples{ +```rascal-shell +import Set; +power1({1,2,3,4}); +``` +} +public set[set[&T]] power1(set[&T] st) = power(st) - {{}}; + +@synopsis{Apply a function to successive elements of a set and combine the results.} +@description{ +Apply the function `fn` to successive elements of set `s` starting with `unit`. +} +@examples{ +```rascal-shell +import Set; +int add(int x, int y) { return x + y; } +reducer({10, 20, 30, 40}, add, 0); +``` +} +@deprecated{Use a reducer expression instead, such as `(init | fn(it,e) | e <- st)`.} +public +&T reducer(set[&T] st, &T(&T, &T) fn, &T unit) + = ( unit | fn(it, elm) | elm <- st ); + +public &T reducer(set[&T] _: {}) { + throw EmptySet(); +} + +@synopsis{Determine the number of elements in a set.} +@examples{ +```rascal-shell +import Set; +size({1,2,3,4}); +size({"elephant", "zebra", "snake"}); +size({}); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java int size(set[&T] st); + +public (&T <: num) sum(set[(&T <: num)] _: {}) { + throw ArithmeticException( + "For the empty set it is not possible to decide the correct precision to return.\n + 'If you want to call sum on empty set, use sum({0.000}+st) or sum({0r} +st) or sum({0}+st) + 'to make the set non-empty and indicate the required precision for the sum of the empty set + " + ); +} + +@synopsis{Sum the elements of a set.} +@examples{ +```rascal-shell +import Set; +sum({3, 1, 4, 5}); +sum({3, 1.5, 4, 5}); +``` +} +public default (&T <: num) sum({(&T <: num) e, *(&T <: num) r}) = ( e | it + i | i <- r ); + +@synopsis{Pick an arbitrary element from a set.} +@description{ +This _randomly_ picks one element from a set, unless the set is empty. + +:::warning +Use ((getSingleFrom)) if you want the element from a singleton set. ((getOneFrom)) will silently +continue even if there are more element present, which can be a serious threat to the validity of your +analysis algorithm (arbitrary data is not considered). +::: +} +@examples{ +```rascal-shell +import Set; +getOneFrom({"elephant", "zebra", "snake"}); +getOneFrom({"elephant", "zebra", "snake"}); +getOneFrom({"elephant", "zebra", "snake"}); +getOneFrom({"elephant", "zebra", "snake"}); +``` +} +@benefits{ +* Random sampling can be an effective test input selection strategy. +} +@pitfalls{ +* The name ((getOneFrom)) does not convey randomness. +* ((getOneFrom)) drops all the other elements. +If you are sure there is only one element and you need it, then use ((getSingleFrom)). It will fail fast if your assumption is wrong. +* If you need more then one element, then repeatedly calling ((getOneFrom)) will be expensive. Have a look at ((util::Sampling)) for more effective +sampling utilities. +} +@javaClass{org.rascalmpl.library.Prelude} +public java &T getOneFrom(set[&T] st); + +@synopsis{Get "first" element from a set.} +@description{ +Get "first" element of a set. Of course, sets are unordered and do not have a first element. +However, we could assume that sets are internally ordered in some way and this ordering is reproducible (it's deterministic up to hashing collisions). +Applying `getFirstFrom` on the same set will always returns the same element. + +:::warning +Use ((getSingleFrom)) if you want the element from a singleton set. ((getFirstFrom)) will silently +continue even if there are more element present, which can be a serious threat to the validity of your +analysis algorithm (arbitrary data is not considered). +::: +} +@benefits{ +This function helps to make set-based code more deterministic, for instance, for testing purposes. +} +@pitfalls{ +* The deterministic order is _undefined_. This means it may be stable between runs, but not between releases of Rascal. +* There are much better ways to iterate over the elements of a set: + * Use the `<-` enumerator operator in a `for` loop or a comprehension. + * Use list matching +* ((getFirstFrom)) drops all the other elements + * If you are sure there is only one element and you need it, then use ((getSingleFrom)). It will fail fast if your assumption is wrong. +} +@javaClass{org.rascalmpl.library.Prelude} +public java &T getFirstFrom(set[&T] st); + +@synopsis{Get the only element from a singleton set.} +@description{ +Get the only element of a singleton set. This fails with a ((CallFailed)) exception when the set is not a singleton. +} +@benefits{ +* ((getSingleFrom)) fails _fast_ if the assumption (parameter `st` is a singleton) is not met. +* If a binary relation `r` is injective (i.e. it models a function where each key only has one value) then all projections `r[key]` should produce singleton values: `{v}`. +Using ((getSingleFrom)) to get the element out makes sure we fail fast in case our assumptions were wrong, or they have changed. +* Never use ((getFirstFrom)) or ((takeOneFrom)) if you can use ((getSingleFrom)). +} +@pitfalls{ +* ((CallFailed)) exceptions are sometimes hard to diagnose. Look at the stack trace to see that it was ((getSingleFrom)) +that caused it, and then look at the parameter of ((CallFailed)) to see that the set was not a singleton. +} +public &T getSingleFrom(set[&T] st) = getFirstFrom(st) + when size(st) == 1; + +// TODO temporary? replacement due to unexplained behaviour of compiler +//public &T getFirstFrom({&T f, *&T _}) = f; +//public &T getFirstFrom(set[&T] _:{}) { throw EmptySet(); } +@synopsis{Remove an arbitrary element from a set, returns the element and a set without that element.} +@description{ +Remove an arbitrary element from set `s` and return a tuple consisting of the element and a set without that element. + Also see ((Set-getOneFrom)). +} +@examples{ +```rascal-shell +import Set; +takeOneFrom({1, 2, 3, 4}); +takeOneFrom({1, 2, 3, 4}); +takeOneFrom({1, 2, 3, 4}); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java tuple[&T, set[&T]] takeOneFrom(set[&T] st); + +@synopsis{Remove "first" element from a set, returns the element and a set without that element.} +@description{ +element of a set. +} +public tuple[&T, set[&T]] takeFirstFrom({&T f, *&T r}) = ; +public tuple[&T, set[&T]] takeFirstFrom(set[&T] _: {}) { + throw EmptySet(); +} + +@synopsis{Convert a set to a list.} +@examples{ +```rascal-shell +import Set; +toList({1, 2, 3, 4}); +toList({"elephant", "zebra", "snake"}); +``` +Note that the same result can be obtained using splicing: +```rascal-shell,continue +s = {1,2,3,4}; +l = [*s]; +``` +} +@pitfalls{ +Recall that the elements of a set are unordered and that there is no guarantee in which order the set elements will be placed in the resulting list. +} +public list[&T] toList(set[&T] st) = [*st]; + +@synopsis{Convert a set of tuples to a map; each key is associated with a set of values.} +@description{ +Convert a set of tuples to a map in which the first element of each tuple +is associated with the set of second elements of all tuples with the same first element. +} +@examples{ +```rascal-shell +import Set; +toMap({<"a", 1>, <"b", 2>, <"a", 10>}); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java map[&A, set[&B]] toMap(rel[&A, &B] st); + +@synopsis{Convert a set of tuples to a map (provided that there are no multiple keys).} +@description{ +Convert a set of tuples to a map. The result should be a legal map (i.e., without multiple keys). +} +@examples{ +```rascal-shell +import Set; +toMapUnique({<"a", 1>, <"b", 2>, <"c", 10>}); +``` +Now explore an erroneous example: +```rascal-shell,continue,error +toMapUnique({<"a", 1>, <"b", 2>, <"a", 10>}); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java map[&A, &B] toMapUnique(rel[&A, &B] st) throws MultipleKey; + +@synopsis{Convert a set to a string.} +@examples{ +```rascal-shell +import Set; +toString({1, 2, 3}); +toString({"elephant", "zebra", "snake"}); +``` +} +@pitfalls{ +Recall that the elements of a set are unordered and that there is no guarantee in which order the set elements will be placed in the resulting string. +} +@javaClass{org.rascalmpl.library.Prelude} +public java str toString(set[&T] st); + +@synopsis{Convert a set to an indented string.} +@examples{ +```rascal-shell +import Set; +toString({1, 2, 3}); +toString({"elephant", "zebra", "snake"}); +``` +} +@pitfalls{ +Recall that the elements of a set are unordered and that there is no guarantee in which order the set elements will be placed in the resulting string. +} +@javaClass{org.rascalmpl.library.Prelude} +public java str itoString(set[&T] st); + +@synopsis{Sort the elements of a set. + +Sort the elements of a set: + +* Use the built-in ordering on values to compare list elements. +* Give an additional `lessThan` function that will be used to compare elements. + +This function `lessThan` (<) function should implement a strict partial order, meaning: + +* that it is not reflexive, i.e. never `a < a` +* is anti-symmetric, i.e. never `a < b && b < a`. +* is transitive, i.e. if `a < b` and `b < c` then `a < c`.} +@examples{ +```rascal-shell +import Set; +import String; +sort({10, 4, -2, 11, 100, 5}); +fruits = {"mango", "strawberry", "pear", "pineapple", "banana", "grape", "kiwi"}; +sort(fruits); +sort(fruits, bool(str a, str b){ return size(a) > size(b); }); +``` +} +public list[&T] sort(set[&T] s) = sort(s, bool (&T a, &T b) { + return a < b; + }); + +@javaClass{org.rascalmpl.library.Prelude} +public java list[&T] sort(set[&T] l, bool(&T a, &T b) less); + +@synopsis{Produce the smallest `k` elements of a set as sorted by the `less` function} +@description{ +This function is fast if `k` is relatively small, say 10 out of a 1000 elements. +It operates in O(n*k) time where n is the size of the set. + +If `k` is a larger value, say `k > 10`, then it's perhaps better to just sort the entire set +using the asymptotically faster (n*log^2(n)) sort function and take the first `k` elements of the resulting list. + +If `k` is a negative number, `top` will return the largest `abs(k)` elements of the set instead of the smallest. +} +@javaClass{org.rascalmpl.library.Prelude} +public java list[&T] top(int k, set[&T] l, bool(&T a, &T b) less); + +public list[&T] top(int k, set[&T] l) = top(k, l, bool (&T a, &T b) { + return a < b; + }); + +@synopsis{Flatten a set of sets into a single set.} +public set[&T] union(set[set[&T]] sets) = {*s| s <- sets}; + +@synopsis{Compute the Jaccard similarity between two sets.} +real jaccard(set[value] x, set[value] y) = (1. * size(x & y)) / size(x + y); +### |project://rascal/src/org/rascalmpl/library/String.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Vadim Zaytsev - vadim@grammarware.net - SWAT, CWI} +@synopsis{Library functions for strings.} +@description{ +The following library functions are defined for strings: +(((TOC))) +} +module String + +extend Exception; +import List; +import ParseTree; + +@synopsis{All functions in this module that have a charset parameter use this as default.} +private str DEFAULT_CHARSET = "UTF-8" ; + +@synopsis{Center a string in given space.} +@description{ +* Center string `s` in string of length `n` using spaces. +* Center string `s` in string of length `n` using `pad` as padding character. +} +@examples{ +```rascal-shell +import String; +center("abc", 10); +center("abc", 10, "x"); +``` +} +public str center(str s, int n) { + return format(s, "center", n, " "); +} + +public str center(str s, int n, str pad) { + return format(s, "center", n, pad); +} + +@synopsis{Return character in a string by its index position.} +@description{ +Return the character at position `i` in string `s` as integer character code. +Also see ((String-stringChar)) that converts character codes back to string. +} +@examples{ +```rascal-shell +import String; +charAt("abc", 0); +stringChar(charAt("abc", 0)); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java int charAt(str s, int i) throws IndexOutOfBounds; + +@synopsis{Return characters of a string.} +@description{ +Return a list of the characters of `s` as integer character codes. +Also see ((String-stringChars)) that converts character codes back to string. +} +@examples{ +```rascal-shell +import String; +chars("abc"); +stringChars(chars("abc")) == "abc"; +``` +} +public list[int] chars(str s) = [charAt(s, i) | i <- [0..size(s)]]; + +@synopsis{Check that a string contains another string.} +@description{ +Check whether the string `find` occurs as substring in the string `subject`. +} +@examples{ +```rascal-shell +import String; +contains("abracadabra", "bra"); +contains("abracadabra", "e"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool contains(str input, str find); + +@synopsis{Replace escaped characters by the escaped character itself (using Rascal escape conventions).} +str deescape(str s) { + res = visit(s) { + case /^\\ \\]>/ => c + case /^\\t/ => "\t" + case /^\\n/ => "\n" + case /^\\f/ => "\f" + case /^\\b/ => "\b" + case /^\\u/ + => stringChar(toInt("0x")) + case /^\\U/ + => stringChar(toInt("0x")) + case /^\\a/ => stringChar(toInt("0x")) + } + ; + return res; +} + +@synopsis{Check whether a string ends with a given substring.} +@description{ +Yields `true` if string `subject` ends with the string `suffix`. +} +@examples{ +```rascal-shell +import String; +endsWith("Hello.rsc", ".rsc"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool endsWith(str subject, str suffix); + +@synopsis{Replace single characters in a string.} +@description{ +Return a copy of `subject` in which each single character key in replacements +has been replaced by its associated value. +} +@examples{ +```rascal-shell +import String; +import IO; +escape("abracadabra", ("a" : "AA", "c" : "C")); +L = escape("\"Good Morning\", he said", ("\"": "\\\"")); +println(L); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str escape(str subject, map[str, str] mapping); + +@synopsis{Find all occurrences of a string in another string.} +@description{ +Find all occurrences of string `find` in string `subject`. +The result is a (possible empty) list of positions where `find` matches. + +See also ((findFirst)) and ((findLast)). +} +@examples{ +```rascal-shell +import String; +findAll("abracadabra", "a"); +findAll("abracadabra", "bra"); +findAll("abracadabra", "e"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java list[int] findAll(str subject, str find); + +@synopsis{Find the first occurrence of a string in another string.} +@description{ +Find the first occurrence of string `find` in string `subject`. +The result is either a position in `subject` or `-1` when `find` is not found. + +Also see ((findAll)) and ((findLast)). +} +@examples{ +```rascal-shell +import String; +findFirst("abracadabra", "a"); +findFirst("abracadabra", "bra"); +findFirst("abracadabra", "e"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java int findFirst(str subject, str find); + +@synopsis{Find the last occurrence of a string in another string.} +@description{ +Find the last occurrence of string `find` in string `subject`. +The result is either a position in `subject` or `-1` when `find` is not found. + +Also see ((findAll)) and ((findFirst)). +} +@examples{ +```rascal-shell +import String; +findLast("abracadabra", "a"); +findLast("abracadabra", "bra"); +findLast("abracadabra", "e"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java int findLast(str subject, str find); + +@synopsis{Check whether a string is empty.} +@description{ +Returns `true` if string `s` is empty. +} +@examples{ +```rascal-shell +import String; +isEmpty(""); +isEmpty("abc"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool isEmpty(str s); + +@synopsis{Generate a arbitrary string.} +@description{ +Returns a string of maximum `n` length, with arbitrary characters. +} +@examples{ +```rascal-shell +import String; +arbString(3); +arbString(10); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str arbString(int n); + +@synopsis{Left alignment of string in given space.} +@description{ +* Left align string `s` in string of length `n` using spaces. +* Left align string `s` in string of length `n` using `pad` as pad character. +} +@examples{ +```rascal-shell +import String; +left("abc", 10); +left("abc", 10, "x"); +``` +} +public str left(str s, int n) { + return format(s, "left", n, " "); +} + +public str left(str s, int n, str pad) { + return format(s, "left", n, pad); +} + +@synopsis{Remove all whitespace from a string.} +@description{ +Return a copy of `subject` in which all occurrences of Unicode whitespace characters have been removed. +} +@examples{ +```rascal-shell +import String; +removeWhitespace("\rabra\ncada bra\t"); +removeWhitespace("Uni\u1680code") +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str removeWhitespace(str subject); + +@synopsis{Replace all occurrences of a string in another string.} +@description{ +Return a copy of `subject` in which all occurrences of `find` (if any) have been replaced by `replacement`. +Also see ((replaceFirst)) and ((replaceLast)). +} +@examples{ +```rascal-shell +import String; +replaceAll("abracadabra", "a", "A"); +replaceAll("abracadabra", "ra", "RARA"); +replaceAll("abracadabra", "cra", "CRA"); +``` +} +@pitfalls{ +Note that `find` is a string (as opposed to, for instance, a regular expression in Java). +} +@javaClass{org.rascalmpl.library.Prelude} +public java str replaceAll(str subject, str find, str replacement); + +@synopsis{Replace the first occurrence of a string in another string.} +@description{ +Return a copy of `subject` in which the first occurrence of `find` (if it exists) has been replaced by `replacement`. +Also see ((replaceAll)) and ((replaceLast)). +} +@examples{ +```rascal-shell +import String; +replaceFirst("abracadabra", "a", "A"); +replaceFirst("abracadabra", "ra", "RARA"); +replaceFirst("abracadabra", "cra", "CRA"); +``` +} +@pitfalls{ +Note that `find` is a string (as opposed to, for instance, a regular expression in Java). +} +@javaClass{org.rascalmpl.library.Prelude} +public java str replaceFirst(str subject, str find, str replacement); + +@synopsis{Replace the last occurrence of a string in another string.} +@description{ +Return a copy of `subject` in which the last occurrence of `find` (if it exists) has been replaced by `replacement`. +Also see ((replaceFirst)) and ((replaceLast)). +} +@examples{ +```rascal-shell +import String; +replaceLast("abracadabra", "a", "A"); +replaceLast("abracadabra", "ra", "RARA"); +replaceLast("abracadabra", "cra", "CRA"); +``` +} +@pitfalls{ +Note that `find` is a string (as opposed to, for instance, a regular expression in Java). +} +@javaClass{org.rascalmpl.library.Prelude} +public java str replaceLast(str subject, str find, str replacement); + +@synopsis{Return a string with all characters in reverse order.} +@description{ +Returns string with all characters of string `s` in reverse order. +} +@examples{ +```rascal-shell +import String; +reverse("abc"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str reverse(str s); + +@synopsis{Right align s in string of length n using space.} +@examples{ +```rascal-shell +import String; +right("abc", 10); +right("abc", 10, "x"); +``` +} +@synopsis{Right alignment of a string value in a given space.} +@description{ +* Right align string `s` in string of length `n` using spaces. +* Right align string `s` in string of length `n` using `pad` as pad character. +} +@examples{ +```rascal-shell +import String; +right("abc", 10); +right("abc", 10, "x"); +``` +} +public str right(str s, int n) { + return format(s, "right", n, " "); +} + +public str right(str s, int n, str pad) { + return format(s, "right", n, pad); +} + +@synopsis{Determine length of a string value.} +@description{ +Returns the length (number of characters) in string `s`. +} +@examples{ +```rascal-shell +import String; +size("abc"); +size(""); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java int size(str s); + +@synopsis{Check whether a string starts with a given prefix.} +@description{ +Yields `true` if string `subject` starts with the string `prefix`. +} +@examples{ +```rascal-shell +import String; +startsWith("Hello.rsc", "Hell"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java bool startsWith(str subject, str prefix); + +@synopsis{Convert a character code into a string.} +@javaClass{org.rascalmpl.library.Prelude} +public java str stringChar(int char) throws IllegalArgument; + +@synopsis{Convert a list of character codes into a string.} +@javaClass{org.rascalmpl.library.Prelude} +public java str stringChars(list[int] chars) throws IllegalArgument; + +@synopsis{Check that a given integer value is a valid Unicode code point.} +@javaClass{org.rascalmpl.library.Prelude} +public java bool isValidCharacter(int ch); + +@synopsis{Extract a substring from a string value.} +@description{ +* Yields substring of string `s` from index `begin` to the end of the string. +* Yields substring of string `s` from index `begin` to (but not including) index `end`. +} +@examples{ +```rascal-shell +import String; +substring("abcdef", 2); +substring("abcdef", 2, 4); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str substring(str s, int begin); + +@javaClass{org.rascalmpl.library.Prelude} +public java str substring(str s, int begin, int end); + +@synopsis{Convert a string value to integer.} +@description{ +* Converts string `s` to integer. +* Convert string `s` to integer using radix `r`. + + +Throws `IllegalArgument` when `s` cannot be converted. +} +@examples{ +```rascal-shell +import String; +toInt("11"); +toInt("11", 8); +``` +Now try an erroneous argument: +```rascal-shell,continue,error +toInt("abc"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java int toInt(str s) throws IllegalArgument; + +@javaClass{org.rascalmpl.library.Prelude} +public java int toInt(str s, int r) throws IllegalArgument; + +@synopsis{Convert the characters in a string value to lower case.} +@description{ +Convert all characters in string `s` to lowercase. Also see ((toUpperCase)). +} +@examples{ +```rascal-shell +import String; +toLowerCase("AaBbCc123"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str toLowerCase(str s); + +@synopsis{Convert a string value to real.} +@description{ +Converts string `s` to a real. Throws `IllegalArgument` when `s` cannot be converted. +} +@examples{ +```rascal-shell,error +import String; +toReal("2.5e-3"); +toReal("123"); +toReal("abc"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java real toReal(str s); + +@synopsis{Convert the characters in a string value to upper case.} +@description{ +Converts all characters in string `s` to upper case. + +Also see ((toLowerCase)). +} +@examples{ +```rascal-shell +import String; +toUpperCase("AaBbCc123"); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str toUpperCase(str s); + +@synopsis{Returns string with leading and trailing whitespace removed.} +@examples{ +```rascal-shell +import String; +trim(" jelly +beans "); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +public java str trim(str s); + +@synopsis{Squeeze repeated occurrences of characters.} +@description{ +Squeeze repeated occurrences in `src` of characters in `charSet` removed. +See http://commons.apache.org/lang/api-2.6/index.html?org/apache/commons/lang/text/package-summary.html[Apache] +for the allowed syntax in `charSet`. +} +@examples{ +```rascal-shell +import String; +squeeze("hello", "el"); +// the other squeeze function uses character class types instead: +squeeze("hello", "el") == squeeze("hello", #[el]); +``` +} +@javaClass{org.rascalmpl.library.Prelude} +@deprecated{Use the other squeeze function that accepts Rascal character class syntax.} +public java str squeeze(str src, str charSet); + +@synopsis{Squeeze repeated occurrences of characters.} +@description{ +Squeeze repeated occurrences in `src` of characters, if they are a member of `&CharClass`, removed. + +* `src` is any string +* `&CharClass` is a reified character class type such as `[a-z]` (a type that is a subtype of the class of all characters `![]`) +* To pass in a char-class type used the type reifier operator: `#[a-z]` or `#![]` +} +@benefits{ +* to squeeze all characters use the universal character class: `#![]` (the negation of the empty class). +* this function is type-safe; you can only pass in correct reified character classes like `#[A-Za-z]`. +} +@pitfalls{ +* `![]` excludes the 0'th unicode character, so we can not squeeze the unicode codepoint `0` using this function. +If you really need to squeeze 0 then it's best to write your own: +```rascal +visit (x) { + case /+/ => "\a00" when dot == "\a00" +} +```` +* Do not confuse the character `0` (codepoint 48) with the zero codepoint: `#[0] != #[\a00]` +} +@examples{ +```rascal-shell +import String; +squeeze("hello", #[el]); +``` +} +public +str squeeze(str src, type[&CharClass] _: type[![]] _) + = visit(src) { + case /+/ => c when &CharClass _ := Tree::char(charAt(c, 0)) + }; + +@synopsis{Split a string into a list of strings based on a literal separator.} +@javaClass{org.rascalmpl.library.Prelude} +public java list[str] split(str sep, str src); + +@javaClass{org.rascalmpl.library.Prelude} +public java str capitalize(str src); + +@javaClass{org.rascalmpl.library.Prelude} +public java str uncapitalize(str src); + +@synopsis{Base-64 encode the characters of a string.} +@description{ + Convert the characters of a string to a list of bytes using UTF-8 encoding and then encode these bytes using base-64 encoding + as defined by RFC 4648: https://www.ietf.org/rfc/rfc4648.txt. +} +@javaClass{org.rascalmpl.library.Prelude} +public +java str toBase64( + str src, + str charset = DEFAULT_CHARSET, + bool includePadding = true +); + +@synopsis{Decode a base-64 encoded string.} +@description{ + Convert a base-64 encoded string to bytes and then convert these bytes to a string using the specified character set. + The base-64 encoding used is defined by RFC 4648: https://www.ietf.org/rfc/rfc4648.txt. +} +@javaClass{org.rascalmpl.library.Prelude} +public java str fromBase64(str src, str charset = DEFAULT_CHARSET); + +@synopsis{Base-32 encode the characters of a string.} +@description{ + Convert the characters of a string to a list of bytes using UTF-8 encoding and then encode these bytes using base-32 encoding + as defined by RFC 4648: https://www.ietf.org/rfc/rfc4648.txt. +} +@javaClass{org.rascalmpl.library.Prelude} +public +java str toBase32( + str src, + str charset = DEFAULT_CHARSET, + bool includePadding = true +); + +@synopsis{Decode a base-32 encoded string.} +@description{ + Convert a base-32 encoded string to bytes and then convert these bytes to a string using the specified character set. + The base-32 encoding used is defined by RFC 4648: https://www.ietf.org/rfc/rfc4648.txt. +} +@javaClass{org.rascalmpl.library.Prelude} +public java str fromBase32(str src, str charset = DEFAULT_CHARSET); + +@synopsis{Word wrap a string to fit in a certain width.} +@description{ +Inserts newlines in a string in order to fit the string in a certain width. It only breaks on spaces (' '). +} +@javaClass{org.rascalmpl.library.Prelude} +public java str wrap(str src, int wrapLength); + +/* + * Return string of length n, with s placed according to dir (left/center/right) and padded with pad. + * Used to implement:left, center and right above. + */@javaClass{org.rascalmpl.library.Prelude} +private java str format(str s, str dir, int n, str pad); + +@synopsis{Determine if a string matches the given (Java-syntax) regular expression.} +@javaClass{org.rascalmpl.library.Prelude} +@deprecated{ +Use `/re/ := s` instead +} +public java bool rexpMatch(str s, str re); + +@synopsis{Convert a string value to a (source code) location.} +@description{ +* Converts string `s` to a location. +* If the scheme is not provided, it is assumed to be `cwd`. +} +@examples{ +```rascal-shell +import String; +toLocation("http://grammarware.net"); +toLocation("document.xml"); +``` +} +@deprecated{Use ((lang::paths::Windows::parseWindowsPath)) for example; toLocation does not handle all intricacies of path notation.} +public +loc toLocation(str s) + = (/\:\/\// := s) ? |://< cdr >| : |cwd:///< s >|; + +@synopsis{Substitute substrings in a string based on a substitution map from location to string.} +@examples{ +```rascal-shell +import String; +substitute("abc", (|stdin:///|(1,1): "d")) +``` +} +str substitute(str src, map[loc, str] s) { + int shift = 0; + str subst1(str src, loc x, str y) { + delta = size(y) - x.length; + src = src[0..x.offset + shift] + y + src[x.offset + x.length + shift..]; + shift += delta; + return src; + } + order = sort([k | k <- s], bool (loc a, loc b) { + return a.offset < b.offset; + }); + return ( src | subst1(it, x, s[x]) | x <- order ); +} + +@synopsis{Indent a block of text} +@description{ +Every line in `content` will be indented using the characters +of `indentation`. +} +@benefits{ +* This operation executes in constant time, independent of the size of the content +or the indentation. +* Indent is the identity function if `indentation == ""` +} +@pitfalls{ +* This function works fine if `indentation` is not spaces or tabs; but it does not make much sense. +} +@javaClass{org.rascalmpl.library.Prelude} +java str indent(str indentation, str content, bool indentFirstLine = false); +### |project://rascal/src/org/rascalmpl/library/Type.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl} +@synopsis{Rascal's type system, implemented in Rascal itself.} +@description{ +The goal of this module is to provide: + +* reflection capabilities that are useful for deserialization and validation of data, and +* to provide the basic building blocks for syntax trees (see ((module:ParseTree))), +* provide the implementations of Rascal's type _lattice_: ((subtype)), ((glb)), ((lub)) and ((intersects)) for reified type Symbols + +The following definition is built into Rascal: +```rascal +data type[&T] = type(Symbol symbol, map[Symbol, Production] definitions); +``` + +For values of type `type[...]` the static and dynamic type systems satisfy three additional constraints over the rules of type-parameterized data types: +1. For any type `T`: `#T` has type `type[T]` +2. For any type T and any value of `type[T]`, namely `type(S, D)` it holds that S is the symbolic representation of type `T` using the ((Type-Symbol)) type, and +3. ... `D` holds all the necessary data and syntax rules required to form values of type `T`. + +In other words, the `#` operator will always produce a value of `type[&T]`, where `&T` is bound to the type that was reified _and_ said value will contain the full grammatical definition for what was bound to `&T`. + +The ((subtype)) relation of Rascal has all the mathematical properties of a _finite lattice_; where ((lub)) implements the _join_ and ((glb)) implements the _meet_ operation. +This is a core design principle of Rascal with the following benefits: +* Type inference has a guaranteed least or greatest solution, always. This means that constraints are always solvable in an unambiguous manner. +* A _principal type_ can always be computed, which is a most precise and unique solution of a type inference problem. Without the lattice, solution candidates could become incomparable and thus ambiguous. Without +this principal type property, type inference is unpredictable for programmers. +* Solving type inference constraints can be implemented efficiently. The algorithm, based on ((lub)) and ((glb)), makes progress _deterministically_ and does not require backtracking +to find better solutions. Since the lattice is not very deep, fixed-point solutions are always found quickly. + +Much of the aspects of the ((subtype)) lattice are derived from the fact that Rascal's values are _immutable_ or _readonly_. This typically allows for containers +to be co-variant in their type parameters: `list[int] <: list[value]`. Because all values in Rascal are immutable, and implemented using persistent data-structures, +its type literals do not feature annotations for co- and contra-variance. We can assume co-variance practically everywhere (even for function parameters). + +Function types in Rascal are special because functions are always _openly extensible_, as are the modules they are contained in. This means that the parameter +types of functions can also be extended, and thus they are co-variant (accepting more rather than fewer kinds of data). Contra-variance is still also allowed, +of course, and so we say function parameter types are "variant" in both directions. ((lub)) has been hard-wired to choose the more general (co-variant) +solutions for function parameter types to reflect the general applicability of the openly extensible functions. +} +@examples{ +```rascal-shell +import Type; +#int +#rel[int,int] +data B = t(); +#B +syntax A = "a"; +#A; +type(\int(),()) +``` +} +module Type + +@synopsis{A Symbol represents a Rascal Type.} +@description{ +Symbols are values that represent Rascal's types. These are the atomic types. +We define here: + +<1> Atomic types. +<2> Labels that are used to give names to symbols, such as field names, constructor names, etc. +<3> Composite types. +<4> Parameters that represent a type variable. + +In ((module:ParseTree)), see ((ParseTree-Symbol)), +Symbols will be further extended with the symbols that may occur in a parse tree. +} +data Symbol + // <1> + = \int() + | \bool() + | \real() + | \rat() + | \str() + | \num() + | \node() + | \void() + | \value() + | \loc() + | \datetime() + ; + +data Symbol // <2> + = \label(str name, Symbol symbol); + +data Symbol + // <3> + = \set(Symbol symbol) + | \tuple(list[Symbol] symbols) + | \list(Symbol symbol) + | \map(Symbol from, Symbol to) + | \adt(str name, list[Symbol] parameters) + | \cons(Symbol \adt, str name, list[Symbol] parameters) + | \alias(str name, list[Symbol] parameters, Symbol aliased) + | \func(Symbol ret, list[Symbol] parameters, list[Symbol] kwTypes) + | \reified(Symbol symbol) + ; + +data Symbol // <4> + = \parameter(str name, Symbol bound); + +@synopsis{rel types are syntactic sugar for sets of tuples.} +Symbol \rel(list[Symbol] symbols) = \set(\tuple(symbols)); + +@synopsis{lrel types are syntactic sugar for lists of tuples.} +Symbol \lrel(list[Symbol] symbols) = \list(\tuple(symbols)); + +@synopsis{Overloaded/union types are always reduced to the least upper bound of their constituents.} +@description{ +This semantics of overloading in the type system is essential to make sure it remains a _lattice_. +} +Symbol overloaded(set[Symbol] alternatives) + = ( \void() | lub(it, a) | Symbol a <- alternatives ); + +@synopsis{A production in a grammar or constructor in a data type.} +@description{ +Productions represent abstract (recursive) definitions of abstract data type constructors and functions: + +* `cons`: a constructor for an abstract data type. +* `func`: a function. +* `choice`: the choice between various alternatives. +* `composition`: composition of two productions. + +In ParseTree, see ((ParseTree-Production)), +Productions will be further extended and will be used to represent productions in syntax rules. +} +data Production + = \cons( + Symbol def, list[Symbol] symbols, list[Symbol] kwTypes, set[Attr] attributes + ) + | \choice(Symbol def, set[Production] alternatives) + | \composition(Production lhs, Production rhs) + ; + +@synopsis{Attributes register additional semantics annotations of a definition.} +data Attr = \tag(value \tag); + +@synopsis{Transform a function with varargs (`...`) to a normal function with a list argument.} +Symbol \var-func(Symbol ret, list[Symbol] parameters, Symbol varArg) + = \func ( + ret, + parameters + \list(varArg), + [] + ); + +@synopsis{Normalize the choice between alternative productions.} +@description{ +The following normalization rules canonicalize grammars to prevent arbitrary case distinctions later +Nested choice is flattened. + +The below code replaces the following code for performance reasons in compiled code: +```rascal +Production choice(Symbol s, {*Production a, choice(Symbol t, set[Production] b)}) + = choice(s, a+b); +``` +} +Production choice(Symbol s, set[Production] choices) { + if (!any(choice(Symbol _, set[Production] _) <- choices)) { + fail choice; + } + else { + // TODO: this does not work in interpreter and typechecker crashes on it (both related to the splicing) + //return choice(s, { *(choice(Symbol t, set[Production] b) := ch ? b : {ch}) | ch <- choices }); + bool changed = false; + set[Production] new_choices = {}; + for (Production ch <- choices) { + if (choice(Symbol _, set[Production] b) := ch) { + changed = true; + new_choices += b; + } + else { + new_choices += ch; + } + } + + if (changed) { + return choice(s, new_choices); + } + else { + fail choice; + } + } +} + +@synopsis{Subtype is the core implementation of Rascal's type lattice.} +@description{ +The following graph depicts Rascal's type lattice for a number of example types, including: +* all the builtin types, with `value` as maximum and `void` as the minimum of the lattice +* parameterized container types (co-variant) +* a function type example (variant in parameter positions, both directions) +* a data type `Exp` +* a reified type `type[value]` and `type[int]` (co-variant) +```rascal-prepare +import Type; +data Exp = \int(int i); +allTypes = {#int, #bool, #real, #rat, #str, #num, #node, #void, #value, #loc, #datetime, #set[int], #set[value], #rel[int, int], #rel[value,value], #lrel[int, int], #lrel +[value,value], #list[int], #list[value], #map[str, int], #map[str, value], #Exp, #int(int), #int(num), #int(value), #value(value), #type[int], #type[value]}; +import analysis::graphs::Graph; +typeLattice = { <"", ""> | <- allTypes * allTypes, subtype(t1, t2)}; +import vis::Graphs; +graph(transitiveReduction(typeLattice<1,0>), cfg=cytoGraphConfig(\layout=defaultDagreLayout())); +``` +} +@examples{ +```rascal-shell +import Type; +subtype(#int, #value) +subtype(#value, #int) +``` +} +bool subtype(type[&T] t, type[&U] u) = subtype(t.symbol, u.symbol); + +@synopsis{This function documents and implements the subtype relation of Rascal's type system.} +@javaClass{org.rascalmpl.library.Type} +java bool subtype(Symbol s, Symbol t); + +@synopsis{Checks if two types have a non-empty intersection, meaning there exists values which types with both as a supertype} +@description{ +Consider `tuple[int, value]` and `tuple[value, int]` which are not subtypes of one another, +but all the values in their intersection, `tuple[int, int]` belong to both types. + +Type intersection is important for the type-checking of pattern matching, and since function parameters +are patterns in Rascal, also for the type-checking of function parameters. Pattern matching between two +types is possible as long as the intersection is non-empty. This is true if one is a sub-type of other, +or vice verse: then the intersection _is_ the subtype. However, the above tuple example also shows +there can be non-empty intersections when the types are not sub-types. +} +@javaClass{org.rascalmpl.library.Type} +java bool intersects(Symbol s, Symbol t); + +bool intersects(type[&T] t, type[&U] u) = intersects(t.symbol, u.symbol); + +@synopsis{Check if two types are comparable, i.e., one is a subtype of the other or vice versa.} +bool comparable(Symbol s, Symbol t) = subtype(s, t) || subtype(t, s); + +bool comparable(type[value] s, type[value] t) + = comparable(s.symbol, t.symbol); + +@synopsis{Check if two types are equivalent, i.e. they are both subtypes of each other.} +bool equivalent(Symbol s, Symbol t) = subtype(s, t) && subtype(t, s); + +bool equivalent(type[value] s, type[value] t) + = equivalent(s.symbol, t.symbol); + +@synopsis{Strict structural equality between values.} +@description{ +The difference between `eq` and `==` is that no implicit coercions are done between values of incomparable types +at the top-level. + +The `==` operator, for convenience, equates `1.0` with `1` but not `[1] with [1.0]`, which can be annoying +when writing consistent specifications. The new number system that is coming up will not have these issues. +} +@examples{ +```rascal-shell +import Type; +1 == 1.0 +eq(1,1.0) +``` +} +@javaClass{org.rascalmpl.library.Type} +java bool eq(value x, value y); + +@synopsis{The least upper bound (lub) of two types is the common ancestor in the type lattice that is lowest.} +@description{ +This function implements the lub operation in Rascal's type system, via unreifying the Symbol values +and calling into the underlying run-time Type implementation. +} +@examples{ +```rascal-shell +import Type; +lub(#tuple[int,value], #tuple[value, int]) +lub(#int, #real) +``` +} +@javaClass{org.rascalmpl.library.Type} +java Symbol lub(Symbol s1, Symbol s2); + +type[value] lub(type[&T] t, type[&T] u) = type(lub(t.symbol, u.symbol), ()); + +@synopsis{The greatest lower bound (glb) between two types, i.e. a common descendant of two types in the lattice which is largest.} +@description{ +This function implements the glb operation in Rascal's type system, via unreifying the Symbol values +and calling into the underlying run-time Type implementation. +} +@examples{ +```rascal-shell +import Type; +glb(#tuple[int,value], #tuple[value, int]) +glb(#int, #real) +``` +} +@javaClass{org.rascalmpl.library.Type} +java Symbol glb(Symbol s1, Symbol s2); + +type[value] glb(type[&T] t, type[&T] u) = type(glb(t.symbol, u.symbol), ()); + +data Exception = typeCastException(Symbol from, type[value] to); + +&T typeCast(type[&T] typ, value v) { + if (&T x := v) { + return x; + } + throw typeCastException(typeOf(v), typ); +} + +@synopsis{Dynamically instantiate an data constructor of a given type with the given children and optional keyword arguments.} +@description{ +This function will build a constructor if the definition exists and the parameters fit its description, or throw an exception otherwise. + +This function can be used to validate external data sources against a data type such as XML, JSON and YAML documents. +} +@javaClass{org.rascalmpl.library.Type} +java &T make(type[&T] typ, str name, list[value] args); + +@javaClass{org.rascalmpl.library.Type} +java &T make( + type[&T] typ, str name, list[value] args, map[str, value] keywordArgs +); + +@synopsis{Returns the dynamic type of a value as a ((Type-Symbol)).} +@description{ +As opposed to the # operator, which produces the type of a value statically, this +function produces the dynamic type of a value, represented by a symbol. +} +@examples{ +```rascal-shell +import Type; +value x = 1; +typeOf(x) +``` +} +@benefits{ +* constructing a reified type from a dynamic type is possible: +```rascal-shell,continue +type(typeOf(x), ()) +``` +} +@pitfalls{ +Note that the `typeOf` function does not produce definitions, like the +reify operator `#` does, since values may escape the scope in which they've been constructed. +} +@javaClass{org.rascalmpl.library.Type} +java Symbol typeOf(value v); +### |project://rascal/src/org/rascalmpl/library/ValueIO.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Mark Hills - Mark.Hills@cwi.nl (CWI)} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@synopsis{Library functions for reading and writing values in textual and binary format.} +module ValueIO + +import Type; + +@synopsis{Read a value from a binary file in PBF format.} +public value readValueFile(loc file) { + return readBinaryValueFile(#value, file); +} + +@synopsis{Get length of a file in number of bytes.} +@javaClass{org.rascalmpl.library.Prelude} +public java int getFileLength(loc file); + +@synopsis{Read a typed value from a binary file.} +@javaClass{org.rascalmpl.library.Prelude} +public java &T readBinaryValueFile(type[&T] result, loc file); + +public value readBinaryValueFile(loc file) { + return readBinaryValueFile(#value, file); +} + +@synopsis{Read a typed value from a text file.} +@javaClass{org.rascalmpl.library.Prelude} +public java &T readTextValueFile(type[&T] result, loc file); + +public value readTextValueFile(loc file) { + return readTextValueFile(#value, file); +} + +@synopsis{If you have written a file containing reified types, then you can use this function + to read them back.} +public &T readTextValueFileWithEmbeddedTypes(type[&T] result, loc file) { + return + readTextValueFile( + type(result.symbol, result.definitions + #Symbol.definitions + #Production.definitions), + file + ); +} + +@synopsis{Parse a textual string representation of a value.} +public value readTextValueString(str input) { + return readTextValueString(#value, input); +} + +@synopsis{Parse a textual string representation of a value and validate it against the given type.} +@javaClass{org.rascalmpl.library.Prelude} +public java &T readTextValueString(type[&T] result, str input); + +@synopsis{Write a value to a file using an efficient binary file format.} +@javaClass{org.rascalmpl.library.Prelude} +public +java void writeBinaryValueFile(loc file, value val, bool compression = true); + +@synopsis{Write a value to a file using a textual file format.} +@javaClass{org.rascalmpl.library.Prelude} +public java void writeTextValueFile(loc file, value val); +### |project://rascal/src/org/rascalmpl/library/analysis/diff/edits/ExecuteTextEdits.rsc| +module analysis::diff::edits::ExecuteTextEdits + +extend analysis::diff::edits::TextEdits; + +import DateTime; +import IO; +import List; +import String; + +@synopsis{Execute file changes, including in-file edits if present.} +@deprecated{Replaced by ((executeFileSystemChanges)) due to a rename of the concept.} +void executeDocumentEdits(list[FileSystemChange] edits) { + executeFileSystemChanges(edits); +} + +@synopsis{Execute file changes, including in-file edits if present.} +void executeFileSystemChanges(list[FileSystemChange] edits) { + for (e <- edits) { + executeFileSystemChange(e); + } +} + +void executeFileSystemChange(removed(loc f)) { + remove(f.top); +} + +void executeFileSystemChange(created(loc f)) { + writeFile(f, ""); +} + +void executeFileSystemChange(renamed(loc from, loc to)) { + move(from.top, to.top, overwrite = true); +} + +void executeFileSystemChange(changed(loc file)) { + setLastModified(file, now()); +} + +@synopsis{Edit a file according to the given ((TextEdit)) instructions} +void executeFileSystemChange(changed(loc file, list[TextEdit] edits)) { + str content = readFile(file); + + content = executeTextEdits(content, edits); + + writeFile(file.top, content); +} + +str executeTextEdits(str content, list[TextEdit] edits) { + // assert isSorted(edits, less=bool (TextEdit e1, TextEdit e2) { + // return e1.range.offset < e2.range.offset; + // }); + int cursor = 0; + + // linear-time streamed reconstruction of the entire text + return + "<for (replace(loc range, str repl) <- edits) {>"; +} +### |project://rascal/src/org/rascalmpl/library/analysis/diff/edits/FileSystemChanges.rsc| +module analysis::diff::edits::FileSystemChanges + +@synopsis{Representing file (and directory) change events.} +@description{ +A ((FileSystemChange)) describes what happened, or what will happen, to a file or directory: +* ((removed)) +* ((Library:analysis::diff::edits::FileSystemChanges-created)) +* ((renamed)) +* or ((changed)) + +FileSystemChanges are an intermediate contract between the sources and the targets +of changes to files and directories on a file system. Typical sources are +((IO::watch)) or source-to-source refactoring tools like "rename". Typical targets +are preview UI and patch execution tools in the UI or on the commandline, +or the listeners of ((IO::watch)) that can trigger incremental re-compilation or re-loading +features. + +The location scheme which is passed to the respective constructors of ((FileSystemChange)) +must have the ((IO-watching)) and ((IO-writing)) ((IOCapability)). Otherwise +((Exception-IO)) errors can be expected. + +For detailed changes _within_ files, see ((TextEdits)). +} +@benefits{ +* Implementations of file watchers can use ((FileSystemChanges-FileSystemChange)) to report what happened. +* Implementations of file and directory diff tools can represent the differences +using ((FileSystemChange)) +} +@pitfalls{ +* To represent the internal differences of files, have a look at ((TextEdits)) instead. +* Note that the concept of "file", as used here in the fields names of ((FileSystemChange)), +includes "directories". This is because directories are indeed a kind of files +from the OS perspective, and because it allows us to unify the two concepts here. +} +data FileSystemChange + = removed(loc file) + | created(loc file) + | renamed(loc from, loc to) + | changed(loc file) + ; +### |project://rascal/src/org/rascalmpl/library/analysis/diff/edits/HiFiLayoutDiff.rsc| +@synopsis{Compare equal-modulo-layout parse trees and extract the exact whitespace text edits that will format the original file.} +@description{ +This algorithm is the final component of a declarative high fidelity source code formatting pipeline. + +We have the following assumptions: +1. One original text file exists. +2. One ((ParseTree)) of the original file to be formatted, containing all original layout and source code comments and case-insensitive literals in the exact order of the original text file. In other words, +nothing may have happened to the parse tree after parsing. +3. One ((ParseTree)) of the _same_ file, but formatted (using a formatting algorithm like ((Tree2Box)) `|` ((Box2Text)), or string templates, and then re-parsing). This is typically obtained by +translating the tree to a `str` using some formatting tools, and then reparsing the file. +4. Typically comments and specific capitalization of case-insensitive literals have been lost in step 3. +5. We use ((analysis::diff::edits::TextEdits)) to communicate the effect of formatting to the IDE context. +} +@pitfalls{ +* if `originalTree !:= formattedTree` the algorithm will produce junk. It will break the syntactical correctness of the source code and forget source code comments. +* if comments are not marked with `@category("Comment")` in the original grammar, then this algorithm can not recover them. +} +@benefits{ +* Recovers source code comments which have been lost during earlier steps in the formatting pipeline. This makes losing source code comments an independent concern of a declarative formatter. +* Recovers the original capitalization of case-insensitive literals which may have been lost during earlier steps in the formatting pipeline. +* Can standardize the layout of case insensitive literals to ALLCAPS, all lowercase, or capitalized. Or can leave the literal as it was formatted by an earlier stage. +* Is agnostic towards the design of earlier steps in the formatting pipeline, so lang as `formattedTree := originalTree`. This means that +the pipeline may change layout (whitespace and comments and capitalization of case-insensitive literals), but nothing else. +} +module analysis::diff::edits::HiFiLayoutDiff + +extend analysis::diff::edits::HiFiTreeDiff; +import ParseTree; +// this should not be necessary because imported by HiFiTreeDiff +import String; +// this should not be be necessary because imported by HiFiTreeDiff +import lang::rascal::grammar::definition::Characters; +import IO; + +@synopsis{Normalization choices for case-insensitive literals.} +data CaseInsensitivity + = toLower() + | toUpper() + | toCapitalized() + | asIs() + | asFormatted() + ; + +@synopsis{Extract TextEdits for the differences in whitespace between two otherwise identical ((ParseTree))s.} +@description{ +See ((HiFiLayoutDiff)). +} +list[TextEdit] layoutDiff( + Tree original, + Tree formatted, + bool recoverComments = true, + CaseInsensitivity ci = asIs() +) { + assert + original := formatted + : "nothing except layout and keyword fields may be different for layoutDiff to work correctly. "; + + @synopsis{rec is the recursive workhorse, doing a pairwise recursion over the original and the formatted tree} + @description{ + We recursively skip over every "equal" pairs of nodes, until we detect two different _layout_ nodes. The original location + of that node and the new contents of the formatted node is used to construct a replace ((TextEdit)), and + optionally the original layout is inspected for source code comments which may have been lost. Literals are skipped + explicitly to avoid arbitrary edits for case insensitive literals, and to safe some time. + } + // if layout differences are detected, here we produce a `replace` node: + list[TextEdit] rec( + t: appl(prod(Symbol tS, _, _), list[Tree] tArgs), + // layout is not necessarily parsed with the same rules (i.e. comments are lost!) + u: appl(prod(Symbol uS, _, _), list[Tree] uArgs) + ) + = [replace(t@\loc, repl) + | tArgs != uArgs, str repl := (recoverComments ? learnComments(t, u) : ""), repl != ""/* do not edit anything if nothing has changed */ ] + when delabel(tS) is layouts, delabel(uS) is layouts, tArgs != uArgs; + + // matched literal trees generate empty diffs + list[TextEdit] rec( + appl(prod(lit(_), _, _), list[Tree] _), appl(prod(lit(_), _, _), list[Tree] _) + ) + = []; + + // matched case-insensitive literal trees generate empty diffs such that the original is maintained. + // however, we also offer some convenience functionality to standardize their formatting right here. + list[TextEdit] rec( + t: appl(prod(cilit(_), _, _), list[Tree] _), + u: appl(prod(cilit(_), _, _), list[Tree] _) + ) { + str yield = ""; + + switch(ci) { + case asIs(): + return []; + case asFormatted(): + return [replace(t@\loc, result) | str result := "", result != yield]; + case toUpper(): + return + [replace(t@\loc, result) | str result := toUpperCase(yield), result != yield]; + case toLower(): + return + [replace(t@\loc, result) | str result := toLowerCase(yield), result != yield]; + case toCapitalized(): + return + [replace(t@\loc, result) | str result := capitalize(yield), result != yield]; + default: + throw "unexpected option: "; + } + } + + list[TextEdit] rec(char(_), char(_)) = []; + + list[TextEdit] rec(cycle(Symbol _, int _), cycle(_, _)) = []; + + // recurse through the entire parse tree to collect layout edits: + default list[TextEdit] rec( + Tree t: appl(Production p, list[Tree] argsA), appl(p/* must be the same by the above assert */ , list[Tree] argsB) + ) + = [*rec(argsA[i], argsB[i]) | i <- [0..size(argsA)]]; + + // first add required locations to layout nodes + // TODO: check if indeed repositioning is never needed + // original = reposition(original, markLit=true, markLayout=true, markSubLayout=true); + return rec(original, formatted); +} + +private Symbol newlineClass = \char-class([range(10, 10)]) ; + +@synopsis{Make sure the new layout still contains all the source code comments of the original layout} +@description{ +This algorithm uses the `@category(/[cC]omments/)` tag to detect source code comments inside layout substrings. If the original +layout contains comments, we re-introduce the comments at the expected level of indentation. New comments present in the +replacement are kept and will overwrite any original comments. + +There are corner cases with respect to the original comments: +* the single line comment that does not end with a newline itself, yet it must always end with a newline after it. +* multiple single line comments after each other + +Then there are corner cases with respect to the replacement whitespace: +* the last line of the replacement whitespace is special. This is the indentation to use for all comments. +* but there could be no newlines in the replacement whitespace; and still there is a single line comment to be included. +Now we need to infer an indentation level for what follows the comment from "thin air". +} +@benefits{ +* if comments are kepts and formatted by tools like Tree2Box, then this algorithm does not overwrite these. +* if comments were completely lost, then this algorithm _always_ puts them back (under assumptions of ((layoutDiff))) +* recovered comments are indented according to the indentation discovered in the _formatted_ replacement tree. +} +@pitfalls{ +* if comments are not marked with `@category("Comment")` in the original grammar, then this algorithm recovers nothing. +} +private str learnComments(Tree original, Tree replacement) { + bool mustEndWithNewline(lit("\n")) = true; + bool mustEndWithNewline(conditional(Symbol s, _)) = mustEndWithNewline(s); + + // if a comment can not contain newline characters, but everything else, then it must be followed by one: + bool mustEndWithNewline(\iter(Symbol cc: \char-class(_))) + = intersection(cc, newlineClass) != newlineClass; + bool mustEndWithNewline(\iter-star(Symbol cc: \char-class(_))) + = intersection(cc, newlineClass) != newlineClass; + default bool mustEndWithNewline(_) = false; + + originalComments + = [ + | /c: appl( + prod(_, [*_, Symbol lastSym], {\tag("category"(/^[Cc]omment$/)), *_}), _ + ) := original, str s := ""]; + + if (originalComments == []) { + // if the original did not contain comments, stick with the replacements + return ""; + } + replacementComments + = ["" + | /c: appl(prod(_, _, {\tag("category"(/^[Cc]omment$/)), *_}), _) + := replacement]; + + if (replacementComments != []) { + // if the replacement contains comments, we assume they've been accurately retained by a previous stage (like Tree2Box): + return ""; + } + // At this point, we know that: + // (a) comments are not present in the replacement and + // (b) they used to be there in the original. + // So the old comments are going to be copied to the new output. + // But, we want to indent them using the style of the replacement. + // The last line of the replacement string typically has the indentation for the construct that follows: + // | // a comment + // | if (true) { + // ^^^^ + // newIndent + // + // However, if the replacement string is on a single line, then we don't have the indentation + // for the string on the next line readily available. In this case we indent the next line + // to the start column of the replacement layout, as a proxy. + str replString = ""; + str newIndent = split("\n", replString)[-1] ? ""; + + if (/\n/ !:= replString) { + // no newline in the repl string, so no indentation available for what follows the comment... + newIndent = "<for (_ <- [0..replacement@\loc.begin.column]) {> <}>"; + } + // we always place sequential comments vertically, because we don't know if we are dealing + // we a single line comment that has to end with newline by follow restriction or by a literal "\n". + // TODO: a deeper analysis of the comment rule that's in use could also be used to discover this. + str trimmedOriginals + = "<for ( <- originalComments) {><if (newLine) {> + '<}><}>"; + + // we wrap the comment with the formatted whitespace to assure the proper indentation + // of its first line, and the proper indentation of what comes after this layout node + return + replString + + indent(newIndent, trimmedOriginals, indentFirstLine = false) + + newIndent; +} + +private Symbol delabel(label(_, Symbol t)) = t; +private default Symbol delabel(Symbol x) = x; +### |project://rascal/src/org/rascalmpl/library/analysis/diff/edits/HiFiTreeDiff.rsc| +@license{ +Copyright (c) 2018-2023, NWO-I Centrum Wiskunde & Informatica +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +} +@synopsis{Infer ((TextEdit)) from the differences between two parse ((ParseTree::Tree))s} +@description{ +This module provides an essential building block for creating high-fidelity source-to-source code transformations. +It is common for industrial use cases of source-to-source transformation to extract +a list of text edits programmatically using parse tree pattern matching. This way the +changes are made on the textual level, with less introduction of noise and fewer removals +of valuable layout (indentation) and source code comments. + +The construction of such high-fidelity edit lists can be rather involved because it tangles +and scatters a number of concerns: +1. syntax-directed pattern matching +2. string substitution; construction of the rewritten text + * retention of layout and in particular indentation + * retention of source code comments + * retention of specific case-insensitive keyword style + * syntactic correctness of the result; especially in relation to list separators there are many corner-cases to thing of + +On the other hand, ParseTree to ParseTree rewrites are much easier to write and get correct. +They are "syntax directed" via the shape of the tree that follows the grammar of the language. +Some if not all of the above aspects are tackled by the rewriting mechanism with concrete patterns. +Especially the corner cases w.r.t. list separators are all handled by the rewriting mechanisms. +Also the rules are in "concrete syntax", on both the matching and the substitution side. So they are +readable for all who know the object language. The rules guarantee syntactic correctness of the +rewritten source code. However, rewrite rules do quite some noisy damage to the layout, indentation +and comments, of the result. + +With this module we bring these two modalities of source-to-source transformations together: +1. The language engineer uses concrete syntax rewrite rules to derive a new ParseTree from the original; +2. We run ((treeDiff)) to obtain a set of minimal text edits; +3. We apply the text edits to the editor contents or the file system. +} +@benefits{ +* Because the derived text edits change fewer characters, the end result is more "hifi" than simply +unparsing the rewritten ParseTree. More comments are retained and more indentation is kept the same. More +case-insensitive keywords retain their original shape. +* At the same time the rewrite rules are easier to maintain as they remain "syntax directed". +* Changes to the grammar will be picked up when checking all source and target patterns. +* The diff algorithm uses cross-cutting information from the parse tree (what is layout and what not, + what is case-insensitive, etc.) which would otherwise have to be managed by the language engineer in _every rewrite rule_. +* The diff algorithm understands what indentation is and brings new sub-trees to the original level +of indentation (same as the sub-trees they are replacing) +* Typically the algorithm's run-time is linear in the size of the tree, or better. Same for memory usage. +} +@pitfalls{ +* ((treeDiff)) only works under the assumption that the second tree was derived from the first +by applying concrete syntax rewrite rules in Rascal. If there is no origin relation between the two +then its heuristics will not work. The algorithm could degenerate to substituting the entire file, +or worse it could degenerate to an exponential search for commonalities in long lists. +* ((treeDiff))'s efficiency is predicated on the two trees being derived from each other in main memory of the currently running JVM. +This way both trees will share pointers where they are the same, which leads to very efficient equality +testing. If the trees are first independently serialized to disk and then deserialized again, and then ((treeDiff)) is called, +this optimization is not present and the algorithm will perform (very) poorly. +* Substitution patterns should be formatted as best as possible. The algorithm will not infer +spacing or relative indentation inside of the substituted subtree. It will only infer indentation +for the entire subtree. Another way of resolving this is using a code formatter on the substituted patterns. +} +module analysis::diff::edits::HiFiTreeDiff + +extend analysis::diff::edits::TextEdits; + +import List; +import Location; +import ParseTree; +import String; +import util::Math; + +@synopsis{Detects minimal differences between parse trees and makes them explicit as ((TextEdit)) instructions.} +@description{ +This is a "diff" algorithm of two parse trees to generate a ((TextEdit)) script that applies the differences on +the textual level, _with minimal collateral damage in whitespace_. This is why it is called "HiFi": minimal unnecessary +noise introduction to the original file. It also tries to conserve source code comments; where still possible. + +The resulting ((TextEdit))s are an intermediate representation for making changes in source code text files. +They can be executed independently via ((ExecuteTextEdits)), or interactively via ((IDEServices)), or LanguageServer features. + +This top-down diff algorithm takes two arguments: +1. an _original_ parse tree for a text file, +2. and a _derived_ parse tree that is mostly equal to the original but has pieces of it substituted or rewritten. + +From the tree node differences between these two trees, ((TextEdit))s are derived such that: +* when the edited source text is parsed again, the resulting tree would match the derived tree. +However, the parsed tree could be different from the derived tree in terms of whitespace, indentation and case-insensitive literals (see below). +* when tree nodes (grammar rules) are equal, smaller edits are searched by pair-wise comparison of the children +* differences between respective layout or (case insensitive) literal nodes are always ignored +* when lists have changed, careful editing of possible separators ensures syntactic correctness +* when new sub-trees are inserted, the replacement will be at the same indentation level as the original. +* when case-insensitive literals have been changed under a grammar rule that remained the same, no edits are produced. + +The function comes in handy when we use Rascal to rewrite parse trees, and then need to communicate the effect +back to the IDE (for example using ((util::IDEServices)) or `util::LanguageServer` interfaces). We use +((ExecuteTextEdits)) to _test_ the effect of ((TextEdits)) while developing a source-to-source transformation. +} +@benefits{ +* This function allows the language engineer to work in terms of abstract and concrete syntax trees while manipulating source text. The +((TextEdit))s intermediate representation bridge the gap to the minute details of IDE interaction such as "undo" and "preview" features. +* Text editing is fraught with details of whitespace, comments, list separators; all of which are handled here by +the exactness of syntactic and semantic knowledge of the parse trees. +* Where possible the algorithm also retains the capitalization of case-insensitive literals. +* The algorithm retrieves and retains indentation levels from the original tree, even if sub-trees in the +derived tree have mangled indentation. This allows us to ignore the indentation concern while thinking of rewrite +rules for source-to-source transformation, and focus on the semantic effect. +* The algorithm inherits source code comments from the original, wherever sub-trees of the original and the +rewritten tree still line up. +} +@pitfalls{ +* If the first argument is not an original parse tree, then basic assumptions of the algorithm fail and it may produce erroneous text edits. +* If the second argument is not derived from the original, then the algorithm will produce a single text edit to replace the entire source text. +* If the second argument was not produced from the first in the same JVM memory, it will not share many pointers to equal sub-trees +and the performance of the algorithm will degenerate quickly. +* If the parse tree of the original does not reflect the current state of the text in the file, then the generated text edits will do harm. +* If the original tree is not annotated with source locations, the algorithm fails. +* Both parse trees must be type correct, e.g. the number of symbols in a production rule, must be equal to the number of elements of the argument list of ((appl)). +* This algorithm does not work with ambiguous (sub)trees. +* When large sub-trees or sub-lists are moved to other parts of the tree, comment inheritance is not possible anymore. +} +@examples{ +If we rewrite parse trees, this can be done with concrete syntax matching. +The following example swaps the if-branch with the else-branch in Pico: + +```rascal-shell +import lang::pico::\syntax::Main; +import IO; +import analysis::diff::edits::ExecuteTextEdits; +import analysis::diff::edits::TextEdits; +import analysis::diff::edits::HiFiTreeDiff; +// an example Pico program: +writeFile(|tmp:///example.pico|, + "begin + ' declare + ' a : natural, + ' b : natural; + ' if a then + ' a := b + ' else + ' b := a + ' fi + 'end"); +import ParseTree; +original = parse(#start[Program], |tmp:///example.pico|); +// match and replace all conditionals +rewritten = visit(original) { + case (Statement) `if then <{Statement ";"}* ifBranch> else <{Statement ";"}* elseBranch> fi` + => (Statement) `if then + ' <{Statement ";"}* elseBranch> + 'else + ' <{Statement ";"}* ifBranch> + 'fi` +} +// Check the result as a string. It worked, but we see some collateral damage in whitespace (indentation). +"" +// Now derive text edits from the two parse trees: +edits = treeDiff(original, rewritten); +// Wrap them in a single document edit +edit = changed(original@\loc.top, edits); +// Apply the document edit on disk: +executeDocumentEdits([edit]); +// and when we read the result back, we see the transformation succeeded, and indentation was not lost: +readFile(tmp://example.pico|); +// It's also possible to directly rewrite the original string, for debugging purposes: +executeTextEdits("", treeDiff(original, rewritten)) +``` +} +// equal trees generate empty diffs (note this already ignores whitespace differences because non-linear matching ignores layout nodes) +list[TextEdit] treeDiff(Tree a, a) = []; + +// skip production labels of original rules when diffing, to be able to focus on the Symbol constructor for downstream case-distinction +list[TextEdit] treeDiff( + Tree t: appl( + prod(label(_, Symbol s), list[Symbol] syms, set[Attr] attrs), list[Tree] args + ), Tree u +) + = treeDiff(appl(prod(s, syms, attrs), args)[@\loc = t@\loc ? |bla:///|], u); + +// skip production labels of original rules when diffing, to be able to focus on the Symbol constructor for downstream case-distinction +list[TextEdit] treeDiff( + Tree t, + Tree u: appl( + prod(label(_, Symbol s), list[Symbol] syms, set[Attr] attrs), list[Tree] args + ) +) + = treeDiff(t, appl(prod(s, syms, attrs), args)[@\loc = u@\loc ? |bla:///|]); + +// matched layout trees generate empty diffs such that the original is maintained +list[TextEdit] treeDiff( + appl(prod(layouts(_), _, _), list[Tree] _), + appl(prod(layouts(_), _, _), list[Tree] _) +) + = []; + +// matched literal trees generate empty diffs +list[TextEdit] treeDiff( + appl(prod(lit(str l), _, _), list[Tree] _), + appl(prod(lit(l), _, _), list[Tree] _) +) + = []; + +// matched case-insensitive literal trees generate empty diffs such that the original is maintained +list[TextEdit] treeDiff( + appl(prod(cilit(str l), _, _), list[Tree] _), + appl(prod(cilit(l), _, _), list[Tree] _) +) + = []; + +// different lexicals generate small diffs even if the parent is equal. This avoids extremely small edits within the boundaries of single identifiers. +list[TextEdit] treeDiff( + t: appl(prod(lex(str l), _, _), list[Tree] _), + r: appl(prod(lex(l), _, _), list[Tree] _) +) + = [replace(t@\loc, learnIndentation(t@\loc, "", ""))] + when t != r; + +// When the productions are different, we've found an edit, and there is no need to recurse deeper. +default list[TextEdit] treeDiff( + t: appl(Production p: prod(_, _, _), list[Tree] _), + r: appl(Production q: !p, list[Tree] _) +) + = [replace(t@\loc, learnIndentation(t@\loc, "", ""))]; + +// If list production are the same, then the element lists can still be of different length +// and we switch to listDiff which has different heuristics than normal trees to detect large identical sublists. +list[TextEdit] treeDiff( + Tree t: appl(Production p: regular(Symbol reg), list[Tree] aElems), + appl(p, list[Tree] bElems) +) + = listDiff(t@\loc, seps(reg), aElems, bElems); + +// When the productions are equal, but the children may be different, we dig deeper for differences +default list[TextEdit] treeDiff( + t: appl(Production p, list[Tree] argsA), appl(p, list[Tree] argsB) +) + = [*treeDiff(a, b) | <- zip2(argsA, argsB)]; + +@synopsis{decide how many separators we have} +private int seps(\iter-seps(_, list[Symbol] s)) = size(s); +private int seps(\iter-star-seps(_, list[Symbol] s)) = size(s); +private default int seps(Symbol _) = 0; + +@synopsis{List diff finds minimal differences between the elements of two lists.} +@description{ +This algorithm uses heuristics to avoid searching for the largest common sublist all too often. +Also it minimized the sublists that largest common sublist is executed on. + +1. Since many patches to parse tree lists typically only change a prefix or a postfix, and we +can detect this quickly, we first extract patches for those instances. +2. It is also fast and easy to detect unchanged prefixes and postfixes, so by focusing +on the changes parts in the middle we generate more instances of case 1. +3. Another simple and quick case is when simply all elements are different (the prefix==the list==the postfix) +3. What we are left with is either an empty list and we are done, or a more complex situation +where we apply the "findEqualSubList" algorithm, which splits the list in three parts: + * two unequal prefixes + * two equal sublists in the middle + * two unequal postfixes +4. the algorithm then concatenates the diffs by recursing to step 1 on the prefixes and the diffs by recursing to step 1. on the postfixes +5. two empty lists terminate the recursion, +} +list[TextEdit] listDiff( + loc span, int seps, list[Tree] originals, list[Tree] replacements +) { + edits = []; + + // this algorithm isolates commonalities between the two lists + // by handling different special cases. It continues always with + // what is left to be different. By maximizing commonalities, + // the edits are minimized. Note that we float on source location parameters + // not only for the edit locations but also for sub-tree identity. + + = trimEqualElements(span, originals, replacements); + + + = commonSpecialCases(span, seps, originals, replacements); + edits += specialEdits; + + equalSubList = findEqualSubList(originals, replacements); + + // by using the (or "a") largest common sublist as a pivot to divide-and-conquer + // to the left and right of it, we minimize the number of necessary + // edit actions for the entire list. + if (equalSubList != [], [*preO, *equalSubList, *postO] := originals, [*preR, *equalSubList, *postR] := replacements) + { + // TODO: what about the separators? + // we align the prefixes and the postfixes and + // continue recursively. + return + edits + + listDiff(beginCover(span, preO), seps, preO, preR) + + listDiff(endCover(span, postO), seps, postO, postR); + } + else if (originals == [], replacements == []) { + return edits; + } + else { + // here we know there are no common elements anymore, only a common amount of different elements + common = min([size(originals), size(replacements)]); + + return + edits + // first the minimal length pairwise replacements, essential for finding accidental commonalities + + [*treeDiff(a, b) + | <- zip2(originals[..common], replacements[..common])] + // then we either remove the tail that became shorter: + + [replace(cover([after(last@\loc), cover(originals[common + 1..])]), "") + | size(originals) > size(replacements), [*_, last] := originals[..common]] + // or we add new elements to the end, while inheriting indentation from the originals: + + [replace( + after(span), + learnIndentation(span, yield(replacements[common..]), yield(originals)) + ) | size(originals) < size(replacements)]; + } +} + +@synopsis{Finds the largest sublist that occurs in both lists} +@description{ +Using list matching and backtracking, this algorithm detects which common +sublist is the largest. It assumes ((trimEqualElements)) has happened already, +and thus there are interesting differences left, even if we remove any equal +sublist. + +Note that this is not a general algorithm for Largest Common Subsequence (LCS), since it +uses particular properties of the relation between the original and the replacement list: +* New elements are never equal to old elements (due to source locations) +* Equal prefixes and postfixes may be assumed to be maximal sublists as well (see above). +* Candidate equal sublists always have consecutive source locations from the origin. +} +list[Tree] findEqualSubList([*Tree sub], [*_, *sub, *_]) = sub; +list[Tree] findEqualSubList([*_, *Tree sub, *_], [*sub]) = sub; +list[Tree] findEqualSubList([*_, p, *Tree sub, q, *_], [*_, !p, *sub, !q, *_]) + = sub; +default list[Tree] findEqualSubList(list[Tree] _orig, list[Tree] _repl) + = []; + +@synopsis{trips equal elements from the front and the back of both lists, if any.} +tuple[loc, list[Tree], list[Tree]] trimEqualElements( + loc span, [Tree a, *Tree aPostfix], [a, *Tree bPostfix] +) + = trimEqualElements(endCover(span, aPostfix), aPostfix, bPostfix); + +tuple[loc, list[Tree], list[Tree]] trimEqualElements( + loc span, [*Tree aPrefix, Tree a], [*Tree bPrefix, a] +) + = trimEqualElements(beginCover(span, aPrefix), aPrefix, bPrefix); + +default tuple[loc, list[Tree], list[Tree]] trimEqualElements( + loc span, list[Tree] a, list[Tree] b +) + = ; + +// only one element removed in front, then we are done +tuple[list[TextEdit], list[Tree], list[Tree]] commonSpecialCases( + loc span, 0, [Tree a, *Tree tail], [*tail] +) + = <[replace(a@\loc, "")], [], []>; + +// only one element removed in front, plus 1 separator, then we are done because everything is the same +tuple[list[TextEdit], list[Tree], list[Tree]] commonSpecialCases( + loc span, 1, [Tree a, Tree _sep, Tree tHead, *Tree tail], [tHead, *tail] +) + = <[replace(fromUntil(a, tHead), "")], [], []>; + +// only one element removed in front, plus 1 separator, then we are done because everything is the same +tuple[list[TextEdit], list[Tree], list[Tree]] commonSpecialCases( + loc span, 3, + [Tree a, Tree _l1, Tree _sep, Tree _l2, Tree tHead, *Tree tail], + [tHead, *tail] +) + = <[replace(fromUntil(a, tHead), "")], [], []>; + +// singleton replacement +tuple[list[TextEdit], list[Tree], list[Tree]] commonSpecialCases( + loc span, int _, [Tree a], [Tree b] +) + = ; + +default tuple[list[TextEdit], list[Tree], list[Tree]] commonSpecialCases( + loc span, int _, list[Tree] a, list[Tree] b +) + = <[], a, b>; + +@synopsis{convenience overload for shorter code} +private +loc fromUntil(Tree from, Tree until) + = fromUntil(from@\loc, until@\loc); + +@synopsis{Compute location span that is common between an element and a succeeding element} +@description{ +The resulting loc is including the `from` but excluding the `until`. It goes right +up to `until`. +```ascii-art + [from] gap [until] + <---------> +```` +} +private +loc fromUntil(loc from, loc until) + = from.top(from.offset, until.offset - from.offset); +private int end(loc src) = src.offset + src.length; + +private loc after(loc src) = src(end(src), 0); + +private loc endCover(loc span, []) = span(span.offset + span.length, 0); +private loc endCover(loc span, [Tree x]) = x@\loc; +private default loc endCover(loc span, list[Tree] l) = cover(l); + +private loc beginCover(loc span, []) = span(span.offset, 0); +private loc beginCover(loc span, [Tree x]) = x@\loc; +private default loc beginCover(loc span, list[Tree] l) = cover(l); + +private +loc cover(list[Tree] elems: [_, *_]) + = cover([e@\loc | Tree e <- elems, e@\loc?]); + +@synopsis{yield a consecutive list of trees} +private str yield(list[Tree] elems) = "<for (e <- elems) {><}>"; + +@synopsis{Make sure the substitution is at least as far indented as the original} +@description{ +This algorithm ignores the first line, since the first line is always preceded by the layout of a parent node. + +Then it measures the depth of indentation of every line in the original, and takes the minimum. +That minimum indentation is stripped off every line that already has that much indentation in the replacement, +and then _all_ lines are re-indented with the discovered minimum. +} +private str learnIndentation(loc span, str replacement, str original) { + list[str] indents(str text) + = [indent | /^[^\ \t]/ <- split("\n", text)]; + + origIndents = indents(original); + replLines = split("\n", replacement); + + if (replLines == []) { + return ""; + } + minIndent = ""; + + if ([_] := origIndents) { + // only one line. have to invent indentation from span + minIndent = "<for (_ <- [0..(span.begin?) ? span.begin.column : 0]) {> <}>"; + } + else { + // we skip the first line for learning indentation, because that one would typically be embedded in a previous line. + minIndent = sort(origIndents[1..])[0] ? ""; + } + + // we remove the leading spaces _up to_ the minimal indentation of the original, + // keep the rest of the indentation from the replacement (if any is left), and then the actual content. + // that entire multiline result is then lazily indented with the minimal indentation we learned from the original. + return + indent( + minIndent, + "<for (/^$/ <- replLines) {> + '<}>"[..-1] + ); +} +### |project://rascal/src/org/rascalmpl/library/analysis/diff/edits/TextEdits.rsc| +@license{ +Copyright (c) 2022, NWO-I Centrum Wiskunde & Informatica (CWI) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +} +@synopsis{Intermediate representation for file creation, removal and changing, including textual (string) rewriting.} +@description{ +((DocumentEdit))s can be produced by source-to-source transformation tools, and then executed +via ((executeDocumentEdits)) in the REPL or ((applyDocumentsEdits)) by the IDE. +} +@benefits{ +* Document edits can be attached to ((data:CodeAction))s and error ((util::IDEServices-Message))s, to achieve interactive +source code rewriting utilities. +* Document edits can be tested via ((executeDocumentEdits)) +* Document edits can be "high fidelity", avoiding unnecessary damage to a source text. +} +@pitfalls{ +* Code edits depend on a specific state of the source file that may be transient while editing. Use the ((CodeAction)) interface +to avoid racing for the state of the source file. +} +module analysis::diff::edits::TextEdits + +extend analysis::diff::edits::FileSystemChanges; + +@synopsis{For compatibility we rename FileSystemChange to DocumentEdit} +@deprecated{It's better to use ((FileSystemChange)) for future compatibility.} +alias DocumentEdit = FileSystemChange; + +@synopsis{For some FileSystemChanges we know exactly what happened.} +data FileSystemChange = changed(loc file, list[TextEdit] edits); + +@synopsis{Shorthand for file changes.} +FileSystemChange changed(list[TextEdit] edits: [replace(loc l, str _), *_]) + = changed(l.top, edits); + +@synopsis{String rewriting operations} +@description{ +The core operation is to replace a substring with another. +The replace operator uses a `loc` value to point to a range inside a string, +and a `str` as its replacement. +} +@benefits{ +* backends have to implement only one ((TextEdit)) operation to be complete. +* use the ((delete)), ((insertBefore)) and ((insertAfter)) "macros" for convenience and brevity. +} +data TextEdit = replace(loc range, str replacement); + +@synopsis{Deletion is replacement with an empty string.} +TextEdit delete(loc range) = replace(range, ""); + +@synopsis{Inserting before a given range.} +@benefits{ +* Use `separator="\n"` to introduce a newline after the inserted text instead of a space. +} +TextEdit insertBefore(loc range, str insertion, str separator = " ") + = (range.begin?) + ? replace( + range.top(range.offset, 0, range.begin, range.begin), + "" + ) + : replace(range.top(range.offset, 0), ""); + +@synopsis{Inserting after a given range.} +@benefits{ +* Use `separator="\n"` to introduce a newline after the inserted text instead of a space. +} +TextEdit insertAfter(loc range, str insertion, str separator = " ") + = (range.end?) + ? replace( + range.top(range.offset + range.length, 0, range.end, range.end), + "" + ) + : replace( + range.top(range.offset + range.length, 0), "" + ); +### |project://rascal/src/org/rascalmpl/library/analysis/flow/ControlFlow.rsc| +@synopsis{Intermediate notation for control flow graphs} +@description{ +Control flow graphs are a unifying concept for units of executable +code in programming languages. This module defines a common +intermediate representation which is designed to be produced from ((data:analysis::m3::Core-M3)) +models and ((module:analysis::m3::AST)) for real programming languages. If (and only if) the translation +is faithful to the semantics of the respective programming language, +then downstream analyses and visualizations are accurate. +} +module analysis::flow::ControlFlow + +@synopsis{control points in source code} +@description{ +Control points in executable units of code are either straightline +code (block), or forks. Each executable unit has an entry and an exit +node. This is the simplest model for control flow nodes which may hold +all the possible structures we find in real executable units, but it +does require an analysis which resolves the locations of each block +and the labels which are used to jump to. +} +data ControlNode + = \block(loc id) + // intermediate nodes in an executable unit + | \entry(loc id) + // start node of an executable unit + | \exit(loc id) + // exit node of an executable unit or a join point + ; + +@synopsis{identify control edges} +@description{ +A control edge goes from ControlEdge to ControlEdge and is identified +by the condition which activates it. For normal structured control +flow (`choice`) like if, while and do-while this is a boolean condition going either +left (true) or right (false). We also have edges labeled by `case` (data) +and edges which are unconditional (`jump`). + +Each edge is identified by +a location which should resolve to the identifying source code. For +`choice` this would be the code of the conditional, for `case` the label +of the code to jump to and for `jump` the code of the jump instruction. Note that +edge identification is redundant information, making it easier to index +back into [M3]((data:analysis::m3::Core::M3)) models or M3 ((module:analysis::m3::AST)) models. +} +data ControlEdge + = \choice(loc id, bool condition) + // if-then-else, while, do-while + | \case(loc id) + // switch case + | \jump(loc id) + // fall-through, break, continue, goto, return, yield + ; + +alias ControlFlow = rel[ControlNode from, ControlEdge edge, ControlNode to]; + +data CFG + = cfg( + loc id, + ControlFlow graph = {}, + ControlNode \start = entry(id), + ControlNode end = exit(id)); +### |project://rascal/src/org/rascalmpl/library/analysis/flow/ObjectFlow.rsc| +@synopsis{Intermediate Language and Basic Algorithms for object flow analysis} +@description{ +The object flow language from the Tonella and Potrich book +"Reverse Engineering Object Oriented Code" is an intermediate +representation for object flow. We may translate for example +Java to this intermediate language and then analyze object flow +based on the simpler language. + +The implementation in this file is intended to work with ((data:analysis::m3::Core-M3)) models +} +@bibliography{ +@book{tonella, + author = {Tonella, Paolo and Potrich, Alessandra}, + title = {Reverse Engineering of Object Oriented Code (Monographs in Computer Science)}, + year = {2004}, + isbn = {0387402950}, + publisher = {Springer-Verlag New York, Inc.}, + address = {Secaucus, NJ, USA}, +} +} +module analysis::flow::ObjectFlow + +import List; +extend analysis::graphs::Graph; + +data FlowProgram = flowProgram(set[FlowDecl] decls, set[FlowStm] statements); + +public loc emptyId = |id:///| ; + +@synopsis{Figure 2.1} +data FlowDecl + = attribute(loc id) + | method(loc id, list[loc] formalParameters) + | constructor(loc id, list[loc] formalParameters) + ; + +@synopsis{Figure 2.1} +data FlowStm + = newAssign(loc target, loc class, loc ctor, list[loc] actualParameters) + | assign(loc target, loc cast, loc source) + | call( + loc target, loc cast, loc receiver, loc method, list[loc] actualParameters + ) + ; + +alias OFG = rel[loc from, loc to]; + +@synopsis{Figure 2.2} +OFG buildFlowGraph(FlowProgram p) + = { + | newAssign(_, _, c, as) <- p.statements, constructor(c, fps) <- p.decls, i <- index(as) + } + + {| newAssign(x, cl, _, _) <- p.statements} + + {| assign(x, _, y) <- p.statements} + + { + | call(_, _, _, m, as) <- p.statements, method(m, fps) <- p.decls, i <- index(as) + } + + {| call(_, _, y, m, _) <- p.statements} + + {| call(x, _, _, m, _) <- p.statements, x != emptyId}; + +@synopsis{Section 2.4} +rel[loc, &T] propagate(OFG g, rel[loc, &T] gen, rel[loc, &T] kill, bool back) { + rel[loc, &T] IN = {}; + rel[loc, &T] OUT = gen + (IN - kill); + if (!back) { + g = g; + } + solve(IN,OUT) { + // Tonella would say: + // IN = { | n <- carrier(g), p <- g[n], \o <- OUT[p] }; <==> + // IN = { | n <- carrier(g), p <- g[n], \o <- OUT[p] }; <==> + // IN = { | <- g, p <- g[n], \o <- OUT[p] }; <==> + // IN = { | <- g, \o <- OUT[p] }; <==> + IN = g o OUT; + OUT = gen + (IN - kill); + } + + return OUT; +} +### |project://rascal/src/org/rascalmpl/library/analysis/formalconcepts/CXTIO.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module analysis::formalconcepts::CXTIO + +import IO; +import String; +import List; +import Set; +import analysis::formalconcepts::FCA; + +@synopsis{Read object attribute in .cxt format.} +public FormalContext[str, str] readCxt(loc input) { + list[str] d = readFileLines(input); + int nRows = toInt(d[2]); + int nCols = toInt(d[3]); + int theStart = 5 + nRows + nCols; + list[str] e = tail(d, size(d) - theStart); + int idx = 5; + map[str, set[str]] vb = (); + for (str f <- e) { + set[str] b = {d[5 + nRows + i]| int i <- [0,1.. size(f)], charAt(f, i) == 88}; + vb[d[idx]] = b; + idx = idx + 1; + } + return toFormalContext(vb); +} +### |project://rascal/src/org/rascalmpl/library/analysis/formalconcepts/FCA.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl (CWI)} +@synopsis{Library for Formal Concept Analysis} +@description{ +Formal Concept Analysis is a somewhat ubiquitous tool in software analysis projects. +It can be used to find latent groups of objects that share the same attributes in a dataset. +Typically, we apply `FCA` to a relation `rel[&O objects, &A attributes]`, which represents +extracted source code artifacts and their attributes. +} +module analysis::formalconcepts::FCA + +import Set; +import Map; +import Relation; +import lang::dot::Dot; + +// import analysis::formalconcepts::Types; +public alias FormalContext[&Object, &Attribute] = rel[&Object, &Attribute]; +public alias Concept[&Object, &Attribute] = tuple[set[&Object] objects, set[&Attribute] attributes]; +public alias ConceptLattice[&Object, &Attribute] = rel[Concept[&Object, &Attribute], Concept[&Object, &Attribute]]; + +public alias Object2Attributes[&Object, &Attribute] = map[&Object, set[&Attribute]]; +public alias Attribute2Objects[&Attribute, &Object] = map[&Attribute, set[&Object]]; + +@synopsis{Computes Concept Lattice given the Object Attribute Relation.} +public +ConceptLattice[&Object, &Attribute] fca( + FormalContext[&Object, &Attribute] fc +) { + rel[set[&Attribute], set[&Attribute]] lat = createAttributeLattice(fc); + return + {<, > + | <set[&Attribute] c1, set[&Attribute] c2> <- lat + }; +} + +@synopsis{Computes Dot Graph from Concept Lattice.} +public DotGraph toDot(ConceptLattice[&Object, &Attribute] cl) { + return toDot(cl, true); +} + +public DotGraph toDot(ConceptLattice[&Object, &Attribute] cl, bool lab) { + map[Concept[&Object, &Attribute], int] z = makeNodes(cl); + set[Concept[&Object, &Attribute]] d = domain(z); + Stms nodes = []; + for (Concept[&Object, &Attribute] c <- d) { + nodes += compose(c, z, lab); + } + Stms edges = [E("\"\"", "\"\"") | x <- cl]; + return + digraph( + "fca", + [ + NODE([ + <"style", "filled">, + <"fillcolor", "cornsilk">, + <"fontcolor", "blue">, + <"shape", "ellipse"> + ]) + ] + + nodes + + edges + ); +} + +public Dotline toDotline(ConceptLattice[&Object, &Attribute] cl) { + return ; +} + +public Outline toOutline(ConceptLattice[&Object, &Attribute] cl) { + map[Concept[&Object, &Attribute], int] z = makeNodes(cl); + set[Concept[&Object, &Attribute]] d = domain(z); + Outline r = (z[c]: ["", ""]| Concept[&Object, &Attribute] c <- d); + return r; +} + +public +FormalContext[&Object, &Attribute] toFormalContext( + Object2Attributes[&Object, &Attribute] objects +) { + return + { + | &Object object <- domain(objects), &Attribute attribute <- objects[object] + }; +} + +public +FormalContext[&Object, &Attribute] toFormalContext( + Attribute2Objects[&Object, &Attribute] attributes +) { + return + { + | &Attribute attribute <- domain(attributes), &Object object <- attributes[attribute] + }; +} + +set[&T] intersection(set[set[&T]] st) { + set[&T] result = isEmpty(st) ? {} : getOneFrom(st); + for (set[&T] elm <- st) { + result = result & elm; + } + return result; +} + +set[&T] union(set[set[&T]] st) { + set[&T] result = {}; + for (set[&T] elm <- st) { + result += elm; + } + return result; +} + +bool isSubset(set[set[&T]] candidate, set[&T] s) { + for (set[&T] c <- candidate) + if (s < c) + return true; + return false; +} + +@javaClass{org.rascalmpl.library.analysis.formalconcepts.FCA} +java set[&Attribute] sigma( + FormalContext[&Object, &Attribute] fc, set[&Object] objects +); + +//= objects == {} ? fc<1> : { a | a <- fc<1>, all(obj <- objects, in fc)}; +@javaClass{org.rascalmpl.library.analysis.formalconcepts.FCA} +java set[&Object] tau( + FormalContext[&Object, &Attribute] fc, set[&Attributes] attributes +); + +//= attributes == {} ? fc<0> : { ob | ob <- fc<0>, all(a <- attributes, in fc)}; +set[set[&T]] maxincl(set[set[&T]] c) { + return {s| set[&T] s <- c, !isSubset(c, s)}; +} + +rel[set[&Attribute], set[&Attribute]] createAttributeLattice( + FormalContext[&Object, &Attribute] fc +) { + set[&Object] G = domain(fc); + set[&Attribute] M = range(fc); + set[set[&Attribute]] layer = {M}; + set[set[&Attribute]] B = {sigma(fc, {g})| g <- G}; + rel[set[&Attribute], set[&Attribute]] r = {}; + while(!isEmpty(layer) && layer != {{}}) { + set[set[&Attribute]] nextLayer = {}; + for (set[&Attribute] m <- layer) { + set[set[&Attribute]] cover = maxincl({b & m| set[&Attribute] b <- B, (b & m) < m}); + for (set[&Attribute] cov <- cover) + r += {}; + nextLayer += cover; + } + layer = nextLayer; + } + return r; +} + +map[Concept[&Object, &Attribute], int] makeNodes( + ConceptLattice[&Object, &Attribute] q +) { + set[Concept[&Object, &Attribute]] c = carrier(q); + int i = 0; + map[Concept[&Object, &Attribute], int] r = (); + for (Concept[&Object, &Attribute] b <- c) { + if (!(r[b])?) { + r[b] = i; + i = i + 1; + } + } + return r; +} + +set[&Attribute] addConcept( + ConceptLattice[&Object, &Attribute] q, Concept[&Object, &Attribute] c +) { + set[Concept[&Object, &Attribute]] parents = range(domainR(q, {c})); + return c[1] - union({p[1]| Concept[&Object, &Attribute] p <- parents}); +} + +Stm compose( + Concept[&Object, &Attribute] c, map[Concept[&Object, &Attribute], int] z, + bool lab +) { + return N("\"\"", lab ? [<"label", "" >] : []); +} + +@synopsis{Write relation in `.dot` format.} +public str toDotString(ConceptLattice[&Object, &Attribute] q) { + DotGraph d = toDot(q); + return toString(d); +} +### |project://rascal/src/org/rascalmpl/library/analysis/grammars/Ambiguity.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Bas Basten - Bas.Basten@cwi.nl - CWI} +module analysis::grammars::Ambiguity + +import ParseTree; +import ValueIO; +import Message; +import List; +import Set; +import Grammar; +import lang::rascal::format::Grammar; + +public list[Message] diagnose(Tree t) { + return [*findCauses(x) | x <- {a| /Tree a: amb(_) := t}]; +} + +public list[Message] diagnose(str amb) { + return diagnose(readTextValueString(#Tree, amb)); +} + +public list[Message] findCauses(Tree a) { + return + [ + info( + "Ambiguity cluster with alternatives", + a@\loc ? |dunno:///| + ) + ] + + [*findCauses(x, y) + | [*_, Tree x, *_, Tree y, *_] := toList(a.alternatives), true/* workaround alert*/ ]; +} + +public list[Message] findCauses(Tree x, Tree y) { + pX = {p| /Production p := x}; + pY = {p| /Production p := y}; + list[Message] result = []; + + if (pX == pY) { + result + += [ + info("The alternatives use the same productions", x@\loc ? |dunno:///|) + ]; + } + else { + result + += [info( + "Production unique to the one alternative: ;", + x@\loc ? |dunno:///| + ) | p <- pX - pY]; + result + += [info( + "Production unique to the other alternative: ;", + x@\loc ? |dunno:///| + ) | p <- pY - pX]; + } + + if (appl(prodX, _) := x, appl(prodY, _) := y) { + if (prodX == prodY) { + result + += [ + info( + "The alternatives have the same production at the top: ", + x@\loc ? |dunno:///| + ) + ]; + } + else { + result + += [ + info( + "The alternatives have different productions at the top, one has + ' + 'while the other has + ' ", + x@\loc ? |dunno:///| + ) + ]; + } + } + result += deeperCauses(x, y, pX, pY); + result += reorderingCauses(x, y); + result += verticalCauses(x, y, pX, pY); + + return result; +} + +public +list[Message] verticalCauses( + Tree x, Tree y, set[Production] pX, set[Production] pY +) { + return exceptAdvise(x, pX, pY) + exceptAdvise(y, pY, pX); +} + +public +list[Message] exceptAdvise(Tree x, set[Production] _, set[Production] pY) { + result = []; + if (appl(p, argsX) := x) { + if (i <- index(argsX), appl(apX, _) := argsX[i], apX notin pY) { + labelApX = "labelX"; + + if (prod(label(l, _), _, _) := apX) { + labelApX = l; + } + else { + result + += [ + warning( + "You should give this production a good label: + ' ]", + x@\loc ? |dunno:///| + ) + ]; + } + + result + += [ + error( + "To fix this issue, you could restrict the nesting of + ' + 'under + ' + 'using the ! operator on argument : ! + 'However, you should realize that you are introducing a restriction that makes the language smaller", + x@\loc ? |dunno:///| + ) + ]; + } + } + return result; +} + +public +list[Message] deeperCauses( + Tree x, Tree y, set[Production] pX, set[Production] pY +) { + // collect lexical trees + rX + = {| /t: appl(prod(\lex(_), _, _), _) := x} + + {| /t: appl(prod(label(_, \lex(_)), _, _), _) := x}; + rY + = {| /t: appl(prod(\lex(_), _, _), _) := y} + + {| /t: appl(prod(label(_, \lex(_)), _, _), _) := y}; + + // collect literals + lX = {| /t: appl(prod(lit(_), _, _), _) := x}; + lY = {| /t: appl(prod(lit(_), _, _), _) := y}; + + // collect layout + laX + = {| /t: appl(prod(layouts(_), _, _), _) := x} + + {| /t: appl(prod(label(_, layouts(_)), _, _), _) := x}; + laY + = {| /t: appl(prod(layouts(_), _, _), _) := y} + + {| /t: appl(prod(label(_, layouts(_)), _, _), _) := y}; + + result = []; + + if (rX<0> != rY<0> || lX<1> != lY<1>) { + result + += [ + info( + "The alternatives have different lexicals/literals/layout", + x@\loc ? |dunno:///| + ) + ]; + result + += [info( + "Unique lexical to the one: ;", t[0]@\loc ? |dunno:///| + ) | t <- (rX - rY), p := t[0].prod]; + result + += [info( + "Unique lexical to the other: ;", t[0]@\loc ? |dunno:///| + ) | t <- (rY - rX), p := t[0].prod]; + result + += [info( + "Unique literal to the one: ", + x@\loc ? |dunno:///| + ) | t <- lX - lY]; + result + += [info( + "Unique literal to the other: ", + x@\loc ? |dunno:///| + ) | t <- lY - lX]; + result + += [info( + "Unique layout to the one: ", + x@\loc ? |dunno:///| + ) | t <- laX - laY]; + result + += [info( + "Unique layout to the other: ", + x@\loc ? |dunno:///| + ) | t <- laY - laX]; + + // literals that became lexicals and vice versa + result + += [error( + "You might reserve from , i.e. using a reject (reserved keyword).", + r@\loc ? |dunno:///| + ) | <- rX o lY]; + result + += [error( + "You might reserve from , i.e. using a reject (reserved keyword).", + r@\loc ? |dunno:///| + ) | <- rY o lX]; + + // lexicals that overlap position, but are shorter (longest match issue) + for ( <- rX, <- rY, tX != tY) { + tXl = tX@\loc; + tYl = tY@\loc; + + // <--------> + // <---> + if (tXl.begin >= tYl.begin && tXl.end <= tYl.end) { + result + += error( + " is overlapping with , add follow restrictions or a symbol table!", + tXl + ); + } + // <---> + // <--------> + if (tYl.begin >= tXl.begin && tYl.end <= tXl.end) { + result + += error( + " is overlapping with , add follow/precede restrictions!", tXl + ); + } + // <-----> + // <-----> + if (tXl.end >= tYl.begin && tXl.end <= tYl.end) { + result + += error( + " is overlapping with , add follow/precede restrictions!", tXl + ); + } + // <----> + // <-----> + if (tXl.begin >= tYl.begin && tXl.begin <= tYl.end) { + result + += error( + " is overlapping with , add follow/precede restrictions!", tXl + ); + } + } + } + // find parents of literals, and transfer location + polX + = { + | /t: appl(p, [*_, l: appl(prod(lit(_), _, _), _), *_]) := x, true + }; + polY + = { + | /t: appl(p, [*_, l: appl(prod(lit(_), _, _), _), *_]) := y, true + }; + overloadedLits + = [info( + "Literal \"\" is used in both + ' and + ' ", + l@\loc + ) | <- polX, <- polY, p != q, !(p in pY || q in pX)]; + + if (overloadedLits != []) { + result + += info( + "Re-use of these literals is causing different interpretations of the same source.", + x@\loc ? |dunno:///| + ); + result += overloadedLits; + + fatherChildX + = { + | appl(p, [*a, appl(q, _), *_]) := x, q.def is sort || q.def is lex, true + }; + fatherChildY + = { + | appl(p, [*a, appl(q, _), *_]) := y, q.def is sort || q.def is lex, true + }; + for ( <- (fatherChildX - fatherChildY) + (fatherChildY - fatherChildX)) { + labelApX = "labelX"; + + if (prod(label(l, _), _, _) := q) { + labelApX = l; + } + else { + result + += [ + warning( + "You should give this production a good label []", + x@\loc ? |dunno:///| + ) + ]; + } + + result + += [ + error( + "You could safely restrict the nesting of + ' + 'under + ' + 'using the ! operator on argument : !", + x@\loc ? |dunno:///| + ) + ]; + } + } + return result; +} + +public list[int] yield(Tree x) { + return [i | /Tree y: char(int i) := x, y == x]; +} + +public list[Message] reorderingCauses(Tree x, Tree y) { + fatherChildX = {| appl(p, [*_, appl(q, _), *_]) := x, true}; + fatherChildY = {| appl(p, [*_, appl(q, _), *_]) := y, true}; + result = []; + + if (fatherChildX == fatherChildY) { + result += associativityCauses(x, y); + } + else { + result += associativityCauses(x, y); + result += priorityCauses(x, y); + result += danglingCauses(x, y); + } + + return result; +} + +list[Message] priorityCauses(Tree x, Tree y) { + if (/appl(p, [appl(q, _), *_]) := x, /Tree t: appl(q, [*_, appl(p, _)]) := y, p != q) { + return + [ + error( + "You might add this priority rule (or vice versa): + ' ", + t@\loc + ), + error( + "You might add this associativity rule (or right/assoc/non-assoc): + ' ", + t@\loc ? |dunno:///| + ) + ]; + } + if (/appl(p, [appl(q, _), *_]) := y, /Tree t: appl(q, [*_, appl(p, _)]) := x, p != q) { + return + [ + error( + "You might add this priority rule (or vice versa): + ' ", + t@\loc + ), + error( + "You might add this associativity rule (or right/assoc/non-assoc): + ' ", + t@\loc ? |dunno:///| + ) + ]; + } + return []; +} + +list[Message] danglingCauses(Tree x, Tree y) { + if (appl(p, /appl(q, _)) := x, appl(q, /appl(p, _)) := y) { + return danglingOffsideSolutions(x, y) + danglingFollowSolutions(x, y); + } + return []; +} + +list[Message] danglingFollowSolutions(Tree x, Tree y) { + if (prod(_, lhs, _) := x.prod, prod(_, [*pref, _, l: lit(_), *_], _) := y.prod, lhs == pref) + { + return + [ + error( + "You might add a follow restriction for on: + ' ", + x@\loc ? |dunno:///| + ) + ]; + } + if (prod(_, lhs, _) := y.prod, prod(_, [*pref, _, l: lit(_), *_], _) := x.prod, lhs == pref) + { + return + [ + error( + "You might add a follow restriction for on: + ' ", + x@\loc ? |dunno:///| + ) + ]; + } + return []; +} + +list[Message] danglingOffsideSolutions(Tree x, Tree y) { + if (appl(p, /Tree u: appl(q, _)) := x, appl(q, /appl(p, _)) := y, (u@\loc).begin.column >= (x@\loc).begin.column, (u@\loc).begin.line < (x@\loc).end.line) + { + return + [ + error( + "You might declare nested offside (to the left) of some child of using a failing syntax action that compares annotations @loc.start.column", + u@\loc + ) + ]; + } + if (appl(p, /Tree u: appl(q, _)) := y, appl(q, /appl(p, _)) := x, (u@\loc).begin.column >= (y@\loc).begin.column, (u@\loc).begin.line < (y@\loc).end.line) + { + return + [ + error( + "You might declare nested offside (to the left) of some child of using a failing syntax action that compares annotations @loc.start.column", + u@\loc + ) + ]; + } + return []; +} + +list[Message] associativityCauses(Tree x, Tree y) { + if (/appl(p, [appl(p, _), *_]) := x, /Tree t: appl(p, [*_, appl(p, _)]) := y) { + return + [ + error( + "This rule [] may be missing an associativity declaration (left, right, non-assoc)", + t@\loc + ) + ]; + } + if (/appl(p, [appl(p, _), *_]) := y, /Tree t: appl(p, [*_, appl(p, _)]) := x) { + return + [ + error( + "This rule [] may be missing an associativity declaration (left, right, non-assoc)", + t@\loc + ) + ]; + } + return []; +} +### |project://rascal/src/org/rascalmpl/library/analysis/grammars/Brackets.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +module analysis::grammars::Brackets + +import Grammar; +import Node; +import lang::rascal::grammar::definition::Priorities; +import ParseTree; + +DoNotNest prioritiesOf(type[&T] t) + = doNotNest(grammar({}, t.definitions)); + +default &T parens( + DoNotNest prios, node parent, node kid, &T x, &T(&T x) parenizer +) + = x; + +&T parens(DoNotNest prios, node parent, node kid, &T x, &T(&T x) parenizer) + = parenizer(x) + when <- prios, pprod.def has name, kprod.def has name, pprod.def.name == getName(parent), kprod.def.name == getName(kid), parent[astPosition(pos, pprod)] == kid; + +private +int astPosition(int pos, Production p) + = ( -1 | it + 1 | i <- [0,1.. pos], isASTsymbol(p.symbols[i]) ); + +bool isASTsymbol(\layouts(_)) = false; +bool isASTsymbol(\keywords(str name)) = false; +bool isASTsymbol(\lit(str string)) = false; +bool isASTsymbol(\cilit(str string)) = false; + +//bool isASTsymbol(\conditional(_, _)) = false; +bool isASTsymbol(\empty()) = false; +default bool isASTsymbol(Symbol _) = true; +### |project://rascal/src/org/rascalmpl/library/analysis/grammars/DefUse.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +module analysis::grammars::DefUse + +import Grammar; +import ParseTree; + +private bool definable(Symbol s) { + return + s is \sort || s is \lex || s is \keywords || s is \layouts || s is \parameterized-sort; +} + +public +tuple[set[Symbol] used, set[Symbol] defined] usedAndDefined(Grammar g) { + used = {s| /prod(l, _, _) := g, /Symbol s <- l, definable(s)}; + defined = g.rules<0>; + return visit() { + case \parameterized-sort(x, _) => \sort(x) + } +} + +public set[Symbol] usedNotDefined(Grammar g) { + = usedAndDefined(g); + return used - defined; +} + +public set[Symbol] definedNotUsed(Grammar g) { + = usedAndDefined(g); + return used - {s| s <- defined, !(s is \start)}; +} +### |project://rascal/src/org/rascalmpl/library/analysis/grammars/Dependency.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +module analysis::grammars::Dependency + +import Grammar; +import ParseTree; +import analysis::graphs::Graph; + +@synopsis{Compute the symbol dependency graph. This graph does not report intermediate nodes + for regular expressions.} +@experimental +Graph[Symbol] symbolDependencies(Grammar g) + = { + | /prod(Symbol s, [*_, Symbol elem, *_], _) := g, Symbol from := delabel(s), /Symbol to := elem, to is sort || to is lex || to is \parameterized-sort, from is sort || from is lex || from is \parameterized-sort + }; + +Graph[Symbol] symbolDependencies(GrammarDefinition d) + = {*symbolDependencies(d.modules[m].grammar)| m <- d.modules}; + +private Symbol delabel(label(_, Symbol t)) = t; +private default Symbol delabel(Symbol x) = x; +### |project://rascal/src/org/rascalmpl/library/analysis/grammars/LOC.rsc| +@license{ + Copyright (c) 2009-2022 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Generic utilities to compute (S)LOC metrics based on grammars} +@description{ +We use this definition to separate lines from: : + +* LF: Line Feed, U+000A +* VT: Vertical Tab, U+000B +* FF: Form Feed, U+000C +* CR: Carriage Return, U+000D +* CR+LF: CR (U+000D) followed by LF (U+000A) +* NEL: Next Line, U+0085 +* LS: Line Separator, U+2028 +* PS: Paragraph Separator, U+2029 +} +module analysis::grammars::LOC + +import ParseTree; +import List; +import util::FileSystem; +import util::Reflective; + +alias Stats = tuple[int total, map[loc file, int sloc] dist]; + +Stats slocStats(file(loc l), Stats stats) + = + when l.extension == "rsc", int n := countSLOC(parseModule(l)); + +Stats slocStats(directory(loc l, kids), Stats stats) + = + when stats2 := ( <0, ()> | slocStats(k, it) | k <- kids ); + +default Stats slocStats(FileSystem _, Stats stats) = stats; + +/* + * Abstract domain to map parsetrees to + */data Output + = newline() + | stuff() + ; + +int countSLOC(Tree t) { + list[Output] output = []; + + // Ignore any layout before or after the main meat. + if (t.prod.def is \start) { + t = t.args[1]; + } + void write(Output x) { + if (!isEmpty(output) && last(output) == x) { + return ; + } + output += [x]; + } + + void writeLayout(Tree t) { + if (isComment(t)) { + return ; + } + switch(t) { + case char(c): + if (isNewLineChar(c)) { + write(newline()); + } + case appl(_, as): + writeKids(as, writeLayout); + + case amb(ts): + // all alts have the same yield + // so just pick an arbitrary one + if (x <- ts) + writeLayout(x); + default: + ; + } + } + + void writeTree(Tree t) { + if (isComment(t)) { + return ; + } + if (isLayout(t)) { + return writeLayout(t); + } + switch(t) { + case char(c): + write(isNewLineChar(c) ? newline() : stuff()); + + case appl(_, as): + writeKids(as, writeTree); + + case amb(ts): + if (x <- ts) + writeTree(x); + default: + ; + } + } + + void writeKids(list[Tree] as, void(Tree) f) { + i = 0; + while(i < size(as)) { + // Interpret CR LF as a single newline + if (char(a) := as[i], i + 1 < size(as), char(b) := as[i + 1], isCR(a), isLF(b)) { + write(newline()); + } + else { + f(as[i]); + } + i += 1; + } + } + + writeTree(t); + return size([n | n: newline() <- output]) + 1; +} + +bool isLayout(appl(prod(\layouts(_), _, _), _)) = true; +bool isLayout(amb({*_, appl(prod(\layouts(_), _, _), _)})) = true; +default bool isLayout(Tree t) = false; + +bool isComment(appl(p: prod(_, _, {*_, \tag("category"("Comment"))}), _)) + = true; +bool isComment(appl(p: prod(_, _, {*_, \tag("category"("comment"))}), _)) + = true; +default bool isComment(Tree _) = false; + +/* + + */bool isLF(int c) = c == 0x000A; +bool isVT(int c) = c == 0x000B; +bool isFF(int c) = c == 0x000C; +bool isCR(int c) = c == 0x000D; +bool isNEL(int c) = c == 0x0085; +bool isLS(int c) = c == 0x2028; +bool isPS(int c) = c == 0x2029; + +bool isNewLineChar(int c) + = any(i <- [isLF, isVT, isFF, isCR, isNEL, isLS, isPS], i(c)); +### |project://rascal/src/org/rascalmpl/library/analysis/graphs/Graph.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl - CWI} +@contributor{Bas Basten - Bas.Basten@cwi.nl - CWI} +@synopsis{A `Graph` alias for reflective relations with associated graph analysis functions.} +@description{ +The Graph data type is an alias for binary type-reflective relations. So all operators and functions defined +on **reflective** relations are also defined on Graphs. + +The `Graph` library provides the following additional functions: +(((TOC))) +} +module analysis::graphs::Graph + +import Set; +import List; +import Relation; +import util::Math; + +alias Graph[&T] = rel[&T from, &T to]; + + +@synopsis{Compute topological order of the nodes in a graph.} +@examples{ +```rascal-shell +import analysis::graphs::Graph; +order({<3,4>, <1,2>, <2,4>, <1,3>}); +``` +} +list[&T] order(Graph[&T] g){ + = stronglyConnectedComponentsAndTopSort(g); + return topsort; +} + + +@synopsis{Compute strongly connected components in a graph.} +@examples{ +```rascal-shell +import analysis::graphs::Graph; +stronglyConnectedComponents({<1, 2>, <2, 3>, <3, 2>, <2, 4>, <4, 2>, <3, 5>, <5, 3>, <4, 5>, <5, 3>}); +``` +} +set[set[&T]] stronglyConnectedComponents(Graph[&T] g){ + = stronglyConnectedComponentsAndTopSort(g); + return components; +} + +@synopsis{Compute the strongly connected components in a graph and return also the topologically sorted elements} +@description{ +Tarjan's algorithm for computing strongly connected components in a graph +Returns : +* a set of strongly connected components (sets of vertices) +* the topological sort of vertices even for cyclic graphs) +* See +} +tuple[set[set[&T]], list[&T]] stronglyConnectedComponentsAndTopSort(Graph[&T] g){ + int index = 0; // depth-first search node number counter + map[&T, int] low = (); // smallest index of any node known to be reachable from v + map[&T, int] indexOf = (); // maps nodes to their index + set[&T] onStack = {}; // set of nodes on current stack + list[&T] stack = []; // node stack contains nodes of SCC under construction + + set[set[&T]]components = {};// set of SCCs to be constructed + list[&T] topsort = []; // sorted list of elements + + void strongConnect(&T v){ + // Set the depth index for v to the smallest unused index + indexOf[v] = index; + low[v] = index; + index += 1; + stack = push(v, stack); + onStack += {v}; + + // Consider successors of v + for(&T w <- successors(g, v)){ + if(!indexOf[w]?){ + // Successor w has not yet been visited; recurse on it + strongConnect(w); + low[v] = min(low[v], low[w]); + } else if(w in onStack){ + // Successor w is in stack S and hence in the current SCC + // If w is not on stack, then (v, w) is a cross-edge in the DFS tree and must be ignored + low[v] = min(low[v], indexOf[w]); + } + } + + // If v is a root node, pop the stack and generate an SCC + if(low[v] == indexOf[v]){ + // Start a new strongly connected component + scc = {}; + &T w = v; + do { + = pop(stack); + onStack -= {w}; + scc += {w}; + topsort = [w] + topsort; + } while (w != v); + components += {scc}; + } + } + + for(v <- carrier(g)){ + if(!indexOf[v]?){ + strongConnect(v); + } + } + + return ; +} + + +@synopsis{Determine the bottom nodes (leaves) of a graph.} +@description{ +Returns the bottom nodes of Graph `G`, i.e., the leaf nodes that don't have any descendants. +} +@examples{ +```rascal-shell +import analysis::graphs::Graph; +bottom({<1,2>, <1,3>, <2,4>, <3,4>}); +``` +} +public set[&T] bottom(Graph[&T] G) +{ + return range(G) - domain(G); +} + + +@synopsis{Determine the direct predecessors of a graph node.} +@description{ +Returns the direct predecessors of node `From` in Graph `G`. +} +@examples{ +```rascal-shell +import analysis::graphs::Graph; +predecessors({<1,2>, <1,3>, <2,4>, <3,4>}, 4); +``` +} +public set[&T] predecessors(Graph[&T] G, &T From) +{ + //return G[_,From]; + return invert(G)[From]; +} + + +@synopsis{Determine the graph nodes reachable from a set of nodes.} +@description{ +Returns the set of nodes in Graph `G` that are reachable from any of the nodes +in the set `Start`. +} +public set[&T] reach(Graph[&T] G, set[&T] Start) +{ + set[&T] R = Start; + set[&T] new = R; + + while (new != {}) { + new = G[new] - R; + R += new; + } + + return R; +} + + +@synopsis{Determine the graph nodes reachable from a set of nodes using a restricted set of intermediate nodes.} +@description{ +Returns the set of nodes in Graph `G` that are reachable from any of the nodes +in set `Start` using path that only use nodes in the set `Restr`. +} +@examples{ +```rascal-shell +import analysis::graphs::Graph; +reachR({<1,2>, <1,3>, <2,4>, <3,4>}, {1}, {1, 2, 3}); +``` +} +public set[&T] reachR(Graph[&T] G, set[&T] Start, set[&T] Restr) +{ + return (carrierR(G, Restr)+)[Start]; +} + + +@synopsis{Determine the graph nodes reachable from a set of nodes excluding certain intermediate nodes.} +@description{ +Returns set of nodes in Graph `G` that are reachable from any of the nodes +in `Start` via path that exclude nodes in `Excl`. +} +@examples{ +```rascal-shell +import analysis::graphs::Graph; +reachX({<1,2>, <1,3>, <2,4>, <3,4>}, {1}, {2}); +``` +} +public set[&T] reachX(Graph[&T] G, set[&T] Start, set[&T] Excl) +{ + return (carrierX(G, Excl)+)[Start]; +} + + +@synopsis{Determine the shortest path between two graph nodes.} +@description{ +Returns the shortest path between nodes `From` and `To` in Graph `G`. +} +@javaClass{org.rascalmpl.library.Prelude} +public java list[&T] shortestPathPair(Graph[&T] G, &T From, &T To); + + +@synopsis{Determine the direct successors of a graph node.} +@description{ +Returns the direct successors of node `From` in Graph `G`. +} +@examples{ +```rascal-shell +import analysis::graphs::Graph; +successors({<1,2>, <1,3>, <2,4>, <3,4>}, 1); +``` +} +public set[&T] successors(Graph[&T] G, &T From) +{ + return G[From]; +} + + +@synopsis{Determine the set of top nodes (roots) of a graph.} +@description{ +Returns the top nodes of Graph `G`, i.e., the root nodes that do not have any predecessors. +} +@examples{ +```rascal-shell +import analysis::graphs::Graph; +top({<1,2>, <1,3>, <2,4>, <3,4>}); +``` +} +public set[&T] top(Graph[&T] G) +{ + return domain(G) - range(G); +} + + +@synopsis{Determine the connected components of a graph.} +@description{ +Returns the [connected components](http://en.wikipedia.org/wiki/Connected_component_(graph_theory) of Graph `G`, as sets of nodes. All nodes within one component are all reachable from one another, there are no paths between two nodes from different components. The graph is assumed to be undirected. +} +@examples{ +```rascal-shell +import analysis::graphs::Graph; +connectedComponents({<1,2>, <1,3>, <4,5>, <5,6>}); +``` +} +public set[set[&T]] connectedComponents(Graph[&T] G) +{ + set[set[&T]] components = {}; + + Graph[&T] undirected = G + invert(G); + + set[&T] todo = domain(undirected); + + while (size(todo) > 0) { + component = reach(undirected, {getOneFrom(todo)}); + components += {component}; + todo -= component; + }; + + return components; +} + +@synopsis{Transitive reduction of a directed graph} +@description{ +The transitive reduction removes all "superfluous" edges in the sense +that all nodes remain reachable but all "shortcuts" have been removed. + +The algorithm is inspired by the following paper, and uses the builtin (fast) transitive closure +algorithm from Rascal, and the composition operator `o` as an oracle to find out +which edges span more than one level in the graph. Note that the transitive +reduction's worst case complexity is in the same order as transitive closure itself anyway. + +> Aho, A. V.; Garey, M. R.; Ullman, J. D. (1972), +> "The transitive reduction of a directed graph", SIAM Journal on Computing, 1 (2): 131–137, doi:10.1137/0201008 +} +@benefits{ +* directed acyclic graphs are simplified (easier to draw clearly) without breaking node reachability +} +@pitfalls{ +* reduces cyclic sub-graphs to "empty" +} +Graph[&T] transitiveReduction(Graph[&T] g) = g - (g o g+); + +@synopsis{Select the short-cut edges, the ones that transitively close at least two other edges.} +Graph[&T] transitiveEdges(Graph[&T] g) = g o g+; +### |project://rascal/src/org/rascalmpl/library/analysis/graphs/LabeledGraph.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +module analysis::graphs::LabeledGraph + +import analysis::graphs::Graph; +import Relation; + +alias LGraph[&T, &L] = rel[&T from, &L label, &T to]; + +@synopsis{Return the bottom nodes of a LGraph.} +public set[&T] bottom(LGraph[&T, &L] G) { + return G.to - G.from; +} + +@synopsis{The predecessors of a single node in a LGraph.} +public set[&T] predecessors(LGraph[&T, &L] G, &T From) { + return invert(G)[From]; +} + +@synopsis{Reachability from a given start set of nodes.} +public set[&T] reach(LGraph[&T, &L] G, set[&T] Start) { + return reach(G, Start); +} + +@synopsis{Reachability from given start set of nodes with restrictions.} +public set[&T] reachR(LGraph[&T, &L] G, set[&T] Start, set[&T] Restr) { + return reachR(G, Start, Restr); +} + +@synopsis{Reachability from given start set of nodes with exclusions.} +public set[&T] reachX(LGraph[&T, &L] G, set[&T] Start, set[&T] Excl) { + return reachX(G, Start, Excl); +} + +@synopsis{The successors of a single node in a LGraph.} +public set[&T] successors(LGraph[&T, &L] G, &T From) { + return G[From]; +} + +@synopsis{Return the top nodes of a LGraph.} +public set[&T] top(LGraph[&T, &L] G) { + return G.from - G.to; +} +### |project://rascal/src/org/rascalmpl/library/analysis/linearprogramming/LLLinearProgramming.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Low level linear programming interface} +module analysis::linearprogramming::LLLinearProgramming + +import List; +import util::Maybe; + +alias LLCoefficients = list[num]; + +data LLObjectiveFun = llObjFun(LLCoefficients coefficients, num const); + +public +LLObjectiveFun llObjFun(LLCoefficients coefficients) + = llObjFun(coefficients, 0); + +data ConstraintType + = leq() + | eq() + | geq() + ; + +data LLConstraint + = llConstraint(LLCoefficients coefficients, ConstraintType ctype, num const); + +alias LLConstraints = set[LLConstraint]; +alias LLVariableVals = list[num]; + +data LLSolution = llSolution(LLVariableVals varVals, num funVal); + +public +num llRunObjFul(LLObjectiveFun f, LLVariableVals vals) + = ( f.const | it + f.coefficients[var] * vals[var] | var <- index(f.coefficients) ); + +list[num] padToSize(list[num] l, int s) = l + [0.0 | _ <- [1,2.. s - size(l) + 1]]; + +public +tuple[LLConstraints constraints, LLObjectiveFun f] normalize( + LLConstraints constraints, LLObjectiveFun f +) { + int nrVars + = max([ + max([size(con.coefficients) | con <- constraints]), size(f.coefficients) + ]); + constraints + = {llConstraint(padToSize(con.coefficients, nrVars), con.ctype, con.const) + | con <- constraints + }; + f = llObjFun(padToSize(f.coefficients, nrVars), f.const); + return ; +} + +@javaClass{org.rascalmpl.library.analysis.linearprogramming.LinearProgramming} +public +java Maybe[LLSolution] llOptimize( + bool minimize, bool nonZero, LLConstraints constraints, LLObjectiveFun f +); +### |project://rascal/src/org/rascalmpl/library/analysis/linearprogramming/LinearProgramming.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +// High level linear programming interface +module analysis::linearprogramming::LinearProgramming + +import analysis::linearprogramming::LLLinearProgramming; +import List; +import util::Maybe; +import Set; +import Map; + +alias Coefficients = map[str var, num coef]; + +data LinearExpression = linearExp(Coefficients coefficients, num const); +alias ObjectiveFun = LinearExpression; + +public +ObjectiveFun linearExp(Coefficients coefficients) + = linearExp(coefficients, 0); + +data Constraint + = constraint(Coefficients coefficients, ConstraintType ctype, num const); + +public +LinearExpression neg(LinearExpression exp) + = linearExp((n: -v| <- toList(exp.coefficients)), -exp.const); + +public +LinearExpression add(LinearExpression lhs, LinearExpression rhs) + = linearExp( + (n: (lhs.coefficients[n] ? 0) + (rhs.coefficients[n] ? 0) + | n <- domain(lhs.coefficients) + domain(rhs.coefficients) + ), + lhs.const + rhs.const + ); + +public +LinearExpression sub(LinearExpression lhs, LinearExpression rhs) + = add(lhs, neg(rhs)); + +public +Constraint constraint(LinearExpression lhs, ConstraintType ctype) + = constraint(lhs.coefficients, ctype, -lhs.const); + +public +Constraint constraint( + LinearExpression lhs, ConstraintType ctype, LinearExpression rhs +) + = constraint(sub(lhs, rhs), ctype); + +alias Constraints = set[Constraint]; + +alias VariableVals = map[str var, num val]; +data Solution = solution(VariableVals varVals, num funVal); + +num runObjFul(ObjectiveFun f, VariableVals vals) + = ( f.const + | it + f.coefficients[var] * vals[var] + | var <- domain(f.coefficients) + ); + +public +Maybe[Solution] minimizeNonNegative( + Constraints constraints, ObjectiveFun f +) + = optimize(true, true, constraints, f); + +public +Maybe[Solution] minimize(Constraints constraints, ObjectiveFun f) + = optimize(true, false, constraints, f); + +public +Maybe[Solution] maximizeNonNegative( + Constraints constraints, ObjectiveFun f +) + = optimize(false, true, constraints, f); + +public +Maybe[Solution] maximize(set[Constraint] constraints, ObjectiveFun f) + = optimize(false, false, constraints, f); + +public +Maybe[Solution] optimize( + bool minimize, bool nonZero, Constraints constraints, ObjectiveFun f +) { + indexVar = getIndexVar(constraints, f); + llConstraints = toLLConstraints(constraints, indexVar); + llf = toLLObjectiveFun(f, indexVar); + llSol = llOptimize(minimize, nonZero, llConstraints, llf); + switch(llSol) { + case nothing(): + return nothing(); + case just(llsol): + return just(fromLLSolution(llsol, indexVar)); + } + + return nothing(); +} + +num zero = 0 ; + +list[num] toLLCoefficients(Coefficients coefficients, list[str] indexVar) + = [coefficients[i] ? zero | i <- indexVar]; + +Coefficients normalize(Coefficients coefs) + = ( () | it + ((c != 0) ? (var : c) : ()) | <- toList(coefs) ); + +public +LinearExpression normalizeLinExp(LinearExpression l) + = l[coefficients = normalize(l.coefficients)]; + +Coefficients fromLLVariableVals(LLVariableVals vars, list[str] indexVar) + = (indexVar[i]: vars[i]| i <- index(indexVar)); + +LLObjectiveFun toLLObjectiveFun(ObjectiveFun f, list[str] indexVar) + = llObjFun(toLLCoefficients(f.coefficients, indexVar), f.const); + +LLConstraint toLLConstraint(Constraint c, list[str] indexVar) + = llConstraint( + toLLCoefficients(c.coefficients, indexVar), c.ctype, c.const + ); + +LLConstraints toLLConstraints(Constraints cs, list[str] indexVar) + = {toLLConstraint(c, indexVar)| c <- cs}; + +list[str] getIndexVar(Constraints cons, ObjectiveFun f) + = toList({*domain(con.coefficients)| con <- cons} + domain(f.coefficients)); + +Solution fromLLSolution(LLSolution l, list[str] indexVar) + = solution( + normalize((indexVar[i]: l.varVals[i]| i <- index(l.varVals))), l.funVal + ); +### |project://rascal/src/org/rascalmpl/library/analysis/m3/AST.rsc| +@synopsis{a symbolic representation for abstract syntax trees of programming languages.} +@description{ +We provide a general set of data types for the syntactic constructs of programming languages: `Expression`, `Statement`, `Declaration` and `Type`. +Also, very common syntactic constructs are added to this, such as `if`, `while`, etc. + +The idea is that parsers for different languages will map to common abstract syntax elements, when this can be done meaningfully. +If not, then these front-ends will extend the existing types with new constructor definitions, or even new kinds of types will +be added. The shared representation limits the element of surprise when working with different languages, and perhaps may +make some downstream analyses reusable. + +The concept of a source location is important for abstract syntax trees. The annotation `src` will always point to value of type `loc`, pointing to the physical location of the construct in the source code. + +The concept of _declaration_ is also relevant. A `decl` annotation points from a use of a concept to its definition, but always via an indirection (i.e. fully qualified name). The `decl` annotation is also of type `loc`, where each location is a fully qualified name of the definition that is used. + +Finally, the concept of a _type_ is relevant for ASTs. In particular an `Expression` may have a `typ` annotation, or a variable declaration, etc. +} +@benefits{ +* Symbolic abstract syntax trees can be analyzed and transformed easily using Rascal primitives such as patterns, comprehensions and visit. +* By re-using recognizable names for different programming languages, it's easier to switch between languages to analyze. +* Some algorithms made be reusable on different programming languages, but please be aware of the _pitfalls_. +} +@pitfalls{ +* Even though different languages may map to the same syntactic construct, this does not mean that the semantics is the same. Downstream +metrics or other analysis tools should still take semantic differences between programming languages into account. +} +module analysis::m3::AST + +import Message; +import Node; +import IO; +import Set; +import util::Monitor; +import analysis::m3::TypeSymbol; + +@synopsis{For metric purposes we can use a true AST declaration tree, a simple list of lines for generic metrics, or the reason why we do not have an AST.} +data \AST (loc file = |unknown:///|) + = declaration(Declaration declaration) + | lines(list[str] contents) + | noAST(Message msg) + ; + +@synopsis{Uniform name for everything that is declared in programming languages: variables, functions, classes, etc.} +@description{ +Instances of the Declaration type represent the _syntax_ of declarations in programming languages. + +| field name | description | +| ---------- | ----------- | +| `src` | the exact source location of the declaration in a source file | +| `decl` | the resolved fully qualified name of the artefact that is being declared here | +| `typ` | a symbolic representation of the static type of the declared artefact here (not the syntax of the type) | +} +data Declaration(loc src = |unknown:///|, + loc decl = |unresolved:///|, + TypeSymbol typ = unresolved()); + +@synopsis{Uniform name for everything that is typically a _statement_ programming languages: assignment, loops, conditionals, jumps} +@description{ +Instances of the Statement type represent the _syntax_ of statements in programming languages. + +| field name | description | +| ---------- | ----------- | +| `src` | the exact source location of the statement in a source file | +| `decl` | if the statement directly represent a usage of a declared artefact, then this points to the fully qualified name of the used artifact. +} +data Statement(loc src = |unknown:///|, loc decl = |unresolved:///|); + +@synopsis{Uniform name for everything that is an _expression_ in programming languages: arithmetic, comparisons, function invocations, ...} +@description{ +Instances of the Expression type represent the _syntax_ of expressions in programming languages. + +| field name | description | +| ---------- | ----------- | +| `src` | the exact source location of the expression in a source file | +| `decl` | if this expression represents a usage, decl is the resolved fully qualified name of the artefact that is being used here | +| `typ` | a symbolic representation of the static type of the _result_ of the expression | +} +data Expression(loc src = |unknown:///|, + loc decl = |unresolved:///|, + TypeSymbol typ = \unresolved()); + +@synopsis{Uniform name for everything that is an _type_ in programming languages syntax: `int`, `void`, `List`} +@description{ +Instances of the Type type represent the _syntax_ of types in programming languages. + +| field name | description | +| ---------- | ----------- | +| `src` | the exact source location of the expression in a source file | +| `decl` | the fully qualified name of the type, if resolved and if well-defined | +| `typ` | a symbolic representation of the static type that is the meaning of this type expression | +} +data Type(loc src = |unknown:///|, + loc decl = |unresolved:///|, + TypeSymbol typ = \unresolved()); + +@synopsis{Uniform name for everything that is a _modifier_ in programming languages syntax: public, static, final, etc.} +@description{ +Instances of the Modifier type represent the _syntax_ of modifiers in programming languages. + +| field name | description | +| ---------- | ----------- | +| `src` | the exact source location of the expression in a source file | +} +data Modifier(loc src = |unknown:///|); + +data Bound; + +@synopsis{Test for the consistency characteristics of an M3 annotated abstract syntax tree} +bool astNodeSpecification( + node n, + str language = "java", + bool checkNameResolution = false, + bool checkSourceLocation = true +) { + // get a loc from any node if there is any. + loc pos(node y) + = (loc f := (y.src ? |unknown:///|(0, 0))) ? f : |unknown:///|(0, 0); + loc decl(node y) = (loc d := y.decl ? |unresolved:///|) ? d : |unresolved:///|; + int begin(node y) = begin(pos(y)); + int end(node y) = end(pos(y)); + int begin(loc l) = l.offset; + int end(loc l) = l.offset + l.length; + bool leftToRight(loc l, loc r) = end(l) <= begin(r); + bool leftToRight(node a, node b) = leftToRight(pos(a), pos(b)); + bool included(node parent, node child) + = begin(parent) <= begin(child) && end(child) <= end(parent); + + if (checkSourceLocation) { + // all AST nodes have src annotations + for (/node x := n, TypeSymbol _ !:= x, Message _ !:= x, Bound _ !:= x) { + if (!(x.src?)) { + println("No .src annotation on: + ' "); + return false; + } + // Note that by removing all the (unannotated) empty lists here, we cover many more complex situations + // below in detecting adjacent nodes in syntax trees. + children = [e | e <- getChildren(x), e != []]; + + // Here we collect all the possible ways nodes can be direct siblings in an abstract syntax tree: + siblings + = [ + *[ | [*_, node a, node b, *_] := children], + // adjacent nodes + *[ | [*_, node a, [node b, *_], *_] := children], + // node followed by non-empty list + *[ | [*_, [*_, node a], node b, *_] := children], + // non-empty list followed by node + *[ | [*_, [*_, node a], [node b, *_], *_] := children], + // adjacent non-empty lists + *[ | [*_, [*_, node a, node b, *_], *_] := children] + // nodes inside a list (elements can not be lists again) + ]; + + // Note that by induction: if all the pairwise adjacent siblings are in-order, then all siblings are in order + // siblings are sorted in the input, even if some of them are lists + for ( <- siblings) { + if (!leftToRight(a, b)) { + println( + "Siblings are out of order: + 'a : is + 'b : is " + ); + return false; + } + if (ab <- [a, b], !included(n, ab)) { + println( + "Child location not is not covered by the parent location: + ' parent: + ' child : , is " + ); + return false; + } + } + // if ([*_, [*_, [*_], *_], *_] := getChildren(x)) { + // println("Node contains a directly nested list: + // ' : "); + // return false; + // } + // if ([_, *_, str _, *_] := children || [*_, str _, *_, _] := children) { + // println("Literals and identifiers must be singletons: + // ' "); + // return false; + // } + } + } + if (checkNameResolution) { + for (/node m := n, m.decl?) { + if (decl(m).scheme == "unresolved") { + println("Use decl has remained unresolved at ."); + } + else + if (/^/ !:= decl(m).scheme) { + println(" has a strange loc scheme at "); + return false; + } + } + } + return true; +} + +@synopsis{Check the AST node specification on a (large) set of ASTs and monitor the progress.} +bool astNodeSpecification( + set[node] toCheck, + str language = "java", + bool checkNameResolution = false, + bool checkSourceLocation = true +) + = job("AST specification checker", bool ( + void(str, int) step + ) { + for (node ast <- toCheck) { + step(loc l := ast.src ? l.path : "AST without src location", 1); + if (!astNodeSpecification( + ast, + language = language, + checkNameResolution = checkNameResolution, + checkSourceLocation = checkSourceLocation + )) { + return false; + } + } + + return true; + }, + totalWork = size(toCheck) + ); +### |project://rascal/src/org/rascalmpl/library/analysis/m3/Core.rsc| +@synopsis{M3 common source code model represent facts extracted from source code for use in downstream metrics or other analyses.} +@description{ +The M3 ((Library:analysis::m3::Core)) defines basic concepts such as: + +* qualified names: we use locations to model qualified names for each programming language +* containment: which artifacts are contained in which other artifacts +* declarations: where artifacts are defined +* uses: where declared artifacts are used +* types: which artifacts has which types + +From this ((Library:analysis::m3::Core)) is supposed to be extended with features specific for a programming language. +} +@benefits{ +* Qualified names in the shape of a location are a uniform and generic way of identifying source code artifacts, that can be extended across languages, projects, and versions. +* M3 helps standardizing the shape of facts we extract from source code for all different languages, limiting the element of surprise. +* When we use M3 for many languages, common IDE features are made reusable (such as clicking from an extracted fact to the code that generated it). +* Some downstream analyses may be reusable between different languages if they all map to M3. +} +@pitfalls{ +* Even though different languages may map to the same M3 model, this does not mean that the semantics is the same. Downstream +metrics or other analysis tools should still take semantic differences between programming languages into account. +} +module analysis::m3::Core + +import Message; +import Set; +import IO; +import util::FileSystem; +import analysis::graphs::Graph; +import Node; +import Map; +import List; +import Relation; +extend analysis::m3::TypeSymbol; + +@synopsis{Modifier's are abstract syntax trees of type and declaration modifiers found in programming languages} +@description{ +In ((data::M3)) models the modifiers of each definition are collected for easy lookup. +} +data Modifier; + +@synopsis{An _M3_ model is a composable database of ground-truth facts about a specific set of source code artifacts} +@description{ +This `m3` data constructor holds all information to an M3 model. It is identified by the _id_ field, +which should be a unique name for the project or file or composition that the M3 model was constructed for. + +Practically all relations in an M3 model relate source locations of the `loc` type: +1. _Name_ locations are logical locations that represent fully qualified names of declared artefacts. + * For example: `|java+method:///java/util/List/toString()|` + * Name locations are always indicated with the column name `name` in any relation below. +2. _Source_ location are physical locations that point to an exact (part of) a source code file: + * For example: `|project://jre13/src/main/java/java/util/List.java|(100,350,<20,0>,<25,10>)` + * Source locations are always indicated with the column name `src` in any relation below. + +These are the _core_ facts stored in M3 models because 90% of all programming languages have +these core features: + +| Ground truth fact kind about source code | Description | +| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `set[Language]` | describes the languages this model contains information about, including their version numbers for the sake of transparency | +| `rel[loc name, loc src] declarations` | maps qualified names of declarations to their original source location in the current model, if any. | +| `rel[loc src, loc name] uses` | as the _inverse_ of `declarations` this maps every source location where a declared artefact is used to its fully qualified name.| +| `set[loc] implicitDeclarations` | provides a set of qualified names of things that are present no matter what in a programming language, for completeness sake.| +| `rel[loc from, loc to] containment` | links the qualified name of the outer (from) declaration to the names of everything that is declared inside of it (to).| +| `rel[loc name, TypeSymbol typ] types` | akin to the classical symbol table, this relation maps fully qualified names to a TypeSymbol representation of their static type.| +| `rel[str simpleName, loc qualifiedName] names` | is for producing human/user readable messages about declared artefacts; every fully qualified name {c,sh,w}ould have one.| +| `list[Message] messages` | collects the errors and warnings produced the parser/compiler that populated this model. | +| `rel[loc definition, loc comments]` documentation` | links documentation strings (comments) inside the source code to specific declarations. A typical example would be _JavaDoc_ comments to a class definition.| +| `rel[loc definition, Modifier modifier] modifiers` | links modifiers to fully qualified declarations (typically access modifiers like `public` or `private` or storage modifiers such as `static`)| + +More relations would be added by M3 model builders for specific programming paradigms. +} +@benefits{ +* Logical name locations are both a readable and optimally accurate references to specific source code artefacts. No accidental confusion by mixing namespaces. +* Binary relations on locations are easily composed to infer new and interesting facts. + * In particular the composition operator and comprehensions can be used to easily deduce or infer more facts; + * Composing `declarations o uses` immediately generates a detailed dependency graph + * Composing `uses o declarations` immediately produces a _jump-to-definition_ graph, while its inverse `(uses o declarations)<1,0>` produces a _references_ graph. +* M3 models never use _maps_ because those are not safely compositional (one maps could overwrite the facts of another). +* Specific programming paradigms and languages may add new facts to the M3 relation. + * For Java and C++ there would be class extension and interface implementation relations, for example. + * PHP would add a relation to link classes to traits, etc. etc. +* Every relation, set, list of facts in an M3 model is _composable_ by union or concatenation. +This makes an entire model composable by composing every item, respectively. The ((composeM3)) +function implements such a union. + * Composition can be used to easily construct project-level models from file-level models. + * Composition can be used to simulate (dynamic) linkage between projects. + * Composition can be used to start simulating remote-procedure calls and shared memory, and other inter-programming language composition like _JNI_. +* M3 models can be cached (efficiently) on disk using functions from ((ValueIO)). A single stored M3 model simulates an _object file_, +while a composed M3 model is more like an `.a` archive or a `.jar` archive. + * Integrating M3 model caching during a build process (e.g ANT, Makefiles or Maven) is a smart way to make whole program analysis fast and incremental. + * Integrating M3 model caching in Integrated Development Environments (e.g. the Language Server Protocol) enables fast and incremental IDE features based on whole program indexing that M3 provides. +} +@pitfalls{ +* Initial M3 models should not contain _inferred_ information, only ground truth data as extracted from parse trees or abstract syntax trees, and facts from the static name and type resolution stages of a compiler or interpreter. + * Inference is certainly possible (say to construct an over-approximated call graph), but that is _not_ what we call an _M3_ model. + * The reason is that _metrics_ of over- and under-approximated abstract interpretations of programs quickly loose their tractability and understandability, and also in + (the education of) empirical scientific methods it is of grave importance to separate facts from heuristic inference. +* Simply calling ((composeM3)) does not immediately represent the full static semantics of program composition. Namely, what the union of facts, as implemented by ((composeM3)) _means_ depends on programming language +semantics. Sometimes to connect the merged models also new connections must be made programmatically to complete the connections. Such analyses are static simulations of the `linking` and `loading` stages of programming languages. +When we simulate static composition, these analyses are ground truth, but when we simulate dynamic loading we have to treat the results as heuristic inferences. +* Not every programming language front-end that creates M3 models has to have implemented all the above relations (yet). Constructing +such a front-end may take time and incrementally growing models can already be very useful. +* Even though M3 models can have errors and be partially populated, please be aware that partially correct programs lead to partially correct models and all downstream analysis is correspondingly inaccurate. +* In statically types programming languages the `declarations` relation is typically one-to-one and the `uses` relation is `many-to-one`, +which means that name resolution is _unique_ at _compile-time_. However this is not required for other more dynamic languages, and this is fine. +You will see that one qualified name could potentially resolve to different artefacts at run-time. This will be reflected by the `uses` relation +also having _many-to-many_ tuples in it. **Be careful how you count**, for example, _dependencies_ or _coupling_ in such cases since we +are literally already over-approximating the reality of the running program. +} +data M3 (set[Language] languages = {}, + rel[loc name, loc src] declarations = {}, + set[loc] implicitDeclarations = {}, + rel[loc name, TypeSymbol typ] types = {}, + rel[loc src, loc name] uses = {}, + rel[loc from, loc to] containment = {}, + list[Message] messages = [], + rel[str simpleName, loc qualifiedName] names = {}, + rel[loc definition, loc comments] documentation = {}, + rel[loc definition, Modifier modifier] modifiers = {}) = m3(loc id); + +@synopsis{Extensible data-type to define language names and their versions} +@description{ +Most ground truth facts about source code require analysis tooling that is specific to the language: +* parsers +* name analysis +* type analysis + +However, there are language analysis methods that are _language agnostic_ such as counting lines of code. +For this we have the `generic()` language name. +} +public data Language (str version = "") = generic(); + +@synopsis{Generic function to compose the facts of a set of M3s into a single model.} +@description{ +We iterate over all the facts stored in every model, and use set union or list concatenation +to collect the elements of all relations and lists. +} +@pitfalls{ +* If the quality of the qualified names in the original models is lacking, than this is the moment that different +declarations might be conflated with the same fully qualified name. All downstream analysis is broken then. +* This function does compose the extended facts for specific programming languages as well but only if they have set, rel, list or lrel as types. +* If extended M3 models use something other than sets, lists or relations, this composition function ignores them completely. +* Composed models can be huge in memory. Make sure to allocate enough heap for the JVM. Real world programs of real world product +can take gigabytes of memory, even when compressed and optimized as M3 models. +} +@benefits{ +* Composition satisfies the requirements for many downstream analyses: + * Composition can be used to easily construct project-level models from file-level models, e.g. for open-source project analysis. + * Composition can be used to simulate (dynamic) linkage between projects, e.g. for whole-program analysis. + * Composition can be used to start simulating remote-procedure calls and shared memory, and other inter-programming language composition like _JNI_. +* Transitive closure on composed models leads to effective (and fast) reachability analysis. +* This function is rather memory-efficient by iterating over the already in-memory keyword parameter sets and lists, and +splicing the unions into an incremental transiently growing set or list via the comprehension. This avoids a lot of copying +and intermediate memory allocation which can be detrimental when doing large whole program analyses. +} +@memo +M3 composeM3(loc id, set[M3] models) { + M3 comp = m3(id); + map[M3, map[str, value]] fields = (m: getKeywordParameters(m)| m <- models); + set[str] keys = {*domain(getKeywordParameters(m))| m <- models}; + map[str, value] result = (); + + // first we do only the sets, to use efficient set union/splicing + for (k <- keys) { + newSet = {*elems| M3 m <- models, set[value] elems := fields[m][k]}; + if (newSet != {}) { + // don't set anything if the result is empty (it could be a list!) + result[k] = newSet; + } + } + + // then we do only the lists, to use efficient list splicing + for (k <- keys) { + newList = [*elems | m <- models, list[value] elems := fields[m][k]]; + if (newList != []) { + // don't set anything if the result is empty (it could be a set!) + result[k] = newList; + } + } + + return setKeywordParameters(comp, result); +} + +@synopsis{Generic function to apply a difference over the annotations of a list of M3s.} +@memo +M3 diffM3(loc id, list[M3] models) { + assert size(models) >= 2; + + M3 first = models[0]; + M3 others = composeM3(id, toSet(models[1..])); + M3 diff = m3(id); + + diff.declarations = first.declarations - others.declarations; + diff.implicitDeclarations + = first.implicitDeclarations - others.implicitDeclarations; + diff.types = first.types - others.types; + diff.uses = first.uses - others.uses; + diff.containment = first.containment - others.containment; + diff.names = first.names - others.names; + diff.documentation = first.documentation - others.documentation; + diff.modifiers = first.modifiers - others.modifiers; + + return diff; +} + +@memo +M3 modifyM3(loc id, list[M3] models, value(&T, &T) fun) { + set[str] allAnnoNames = {*domain(getKeywordParameters(m))| m <- models}; + map[str, value] allAnnos = (); + + for (m <- models) { + annos = getKeywordParameters(m); + + for (name <- allAnnoNames, name in annos) { + if (allAnnos[name]?) { + try { + allAnnos[name] = fun(allAnnos[name], annos[name]); + } + catch _: + ; + // ignore + } + else { + allAnnos[name] = annos[name]; + } + } + } + return setKeywordParameters(m3(id), allAnnos); +} + +bool isEmpty(M3 model) = model.id.scheme == "unknown"; + +@deprecated{Does not make much sense since containment usually contains only logical locations.} +set[loc] files(M3 model) { + todo = top(model.containment); + done = {}; + + while(todo != {}) { + = takeOneFrom(todo); + if (isDirectory(elem)) { + todo += model.containment[elem]; + } + else { + done += elem; + } + } + + return done; +} + +@synopsis{Transform the containment relation to a recursive tree model} +@description{ +This makes the containment relation into an abstract ((FileSystem)) for further analysis, +or visualization. +} +@benefits{ +* Transforming the containment relation to a tree model allows further analysis using operators +such as `visit` and descendant matching (`/`) which is sometimes more convenient. +* The tree shape is better for visualization purposes. +} +set[FileSystem] containmentToFileSystem(M3 model) { + @memo + set[FileSystem] relToFileSystem(rel[loc parent, loc child] r) { + FileSystem rec(loc l, set[loc] args) + = (args == {}) ? file(l) : directory(l, {rec(c, r[c])| c <- args}); + return {rec(t, r[t])| t <- top(r)}; + } + + return relToFileSystem(model.containment); +} + +list[Message] checkM3(M3 model) { + result = [m | m <- model.messages, m is error]; + result + += [error("undeclared element in containment", decl) + | decl <- model.containment - model.declarations]; + result + += [error("non-root element is not contained anywhere", decl) + | decl <- model.containment - model.declarations - + top(model.containment)]; + return result; +} + +@synopsis{Specification to test the quality of M3 models that specific language front-ends produce.} +@description{ +Based on the language agnostic relations in the M3 model, this function tries to validate the +_internal_ consistency of an M3 model. + +If an M3 instance is a `closedWorld` model, this means that there are no `uses` in the model that +are not declared in the current model in `declarations`. A closed world model allows for more +stringent consistency checks than a model that depends on external declarations. By selecting +`closedWorld=true` those additional checks are enabled, otherwise these are ignored or weakened +accordingly. It is advisable to provide at least one closed model per programming language front-end, +while testing against this spec. + +By `covering` we mean that everything that is declared in the model is also used at least once. +This is a simple check for knowing if the test covers the language in some form. +} +@benefits{ +* Front-end construction is tricky business. This test provides a sanity check before users start depending +on faulty models. +} +@pitfalls{ +* In `closedWorld` many things can be strictly checked, but in an open world with dependencies outside +of the current model the validation is much weaker. +} +bool m3SpecificationTest(M3 m, bool closedWorld = false, bool covering = false) { + decls = m.declarations; + uses = m.uses; + externals = uses - decls; + + // At least one language is represented + assert m.languages != {}; + + // What `closedWorld` means: + assert closedWorld ==> externals == {}; + + // We now merge the implicitDeclarations with the externals for the sake of brevity + externals += m.implicitDeclarations; + + // Nothing that is contained here does not not have a declaration, except the outermost translationUnit + assert decls - m.containment - top(m.containment) == {}; + + // Note that one declaration may occur twice ore more in the m.declarations relation. + // this is because of _overloading_ of names in programming languages, or because + // of dynamic name resolution (for languages where static name resolution is inherently inaccurate). + overloadedDeclarations = {d| d <- decls, {_, _, *_} := m.declarations[d]}; + + // Containment is 1-to-many, so a reverse lookup always produces a singleton. + // An exception must be made for the overloaded declarations that might be contained in different elements at the same time. + rel[loc inner, loc outer] reverseContainment = m.containment; + assert {inner + | inner <- reverseContainment, {_} !:= reverseContainment[inner] + } + - + overloadedDeclarations == {}; + + // Everything in the containment relation has been declared somewhere + assert carrier(m.containment) - decls - externals == {}; + + // Everything in the declarations relation is contained somewhere + assert decls - carrier(m.containment) == {}; + + // All uses point to actual declarations + assert uses - decls - externals == {}; + + // In this example, all declarations are used at least once + if (covering) { + assert decls - uses == {}; + } + // M.declarations is one-to-one + assert size(decls) == size(m.declarations); + + // documentation is on names, not sources + assert m.documentation - decls == {}; + + // modifiers is on names, not sources + assert m.modifiers - decls == {}; + + // types is on names, not sources + assert m.types - decls - externals == {}; + + // names is for the declared artefacts + assert m.names - decls - externals == {}; + + // simple names are indeed simple, meant for UI purposes + assert all(/^[$A-Za-z0-9_\-]+$/ <- m.names); + + return true; +} +### |project://rascal/src/org/rascalmpl/library/analysis/m3/FlowGraph.rsc| +module analysis::m3::FlowGraph + +extend analysis::m3::Core; + +data M3(rel[loc decl, BasicBlocks blocks] basicBlocks = {}, + rel[loc decl, FlowGraph graph] dataFlow = {}, + rel[loc decl, FlowGraph graph] controlFlow = {}); + +alias BasicBlocks = rel[loc whole, list[loc] parts]; +alias FlowGraph = rel[loc from, set[EdgeProperty] properties, loc to]; + +data EdgeProperty + = trueCondition() + | falseCondition() + ; +### |project://rascal/src/org/rascalmpl/library/analysis/m3/Registry.rsc| +@synopsis{in memory database for [analysis/m3/Core] models for resolving hyperlinks} +@description{ +The functions in this file are used to register m3 models in a global in-memory database. When a source location is clicked this database is used used to resolve logical source locations, such as `|java+class:///java/lang/Object|` to physical source locations such as `|file:///usr/lib/blabla.java|`. +} +module analysis::m3::Registry + +import analysis::m3::Core; +import IO; + +@synopsis{Register an M3 model for a certain project name.} +@description{ +The effect of registering a project is that the m3 URI resolver knows how to find the physical source location +for qualified names. + +Note that ((registerProject)) will be called usually as a side-effect of a function that extracts a model for +a specific language. +} +@benefits{ +* this enables qualified names as locations to be hyperlinks in the IDE +} +@pitfalls{ +* the registry is a global store that will retain links to M3 models even when they are not in use anymore. The +programmer should take care to call ((unregisterProject)) to prevent memory leakage. +} +void registerProject(loc project, M3 model) { + rel[str scheme, loc name, loc src] perScheme + = {| <- model.declarations}; + + for (str scheme <- perScheme) { + registerLocations( + scheme, project.authority, (name: src| <- perScheme[scheme]) + ); + } +} + +@synopsis{unregister an M3 model for a certain project name.} +@description{ +The effect of unregistering a project is that all references will be +removed from the registry, clearing memory. +} +@benefits{ +* this cleans up the memory used by the registry +} +@pitfalls{ +* if a different model is used for unregistering than for registering, + there could be a memory leak of remaining schemes and their respective locations. +} +void unregisterProject(loc project, M3 model) { + for (loc name <- model.declarations) { + unregisterLocations(name.scheme, project.authority); + } +} + +@synopsis{unregister an M3 model for a set of given schemes} +@description{ +The effect of unregistering a project is that all references will be +removed from the registry, clearing memory. +} +@benefits{ +* This cleans up the memory used by the registry, and by giving all possible + schemes for a certain language the chance is high there are not dangling + entries afterwards. +} +@pitfalls{ +* If more schemes were registered than are unregistered here, there is a + memory leak. +} +void unregisterProjectSchemes(loc project, set[str] schemes) { + for (str scheme <- schemes) { + unregisterLocations(scheme, project.authority); + } +} +### |project://rascal/src/org/rascalmpl/library/analysis/m3/TypeSymbol.rsc| +@synopsis{Symbolic representation for types that occur in programming languages.} +@description{ +M3 provides a general mechanism to associate types, symbolically, with source code artifacts or even run-time artifacts. + +The `TypeSymbol` type is a general concept which needs to be extended for specific programming languages. One language will +class and interface definitions which coincide with types, another may have higher order function types etc. + +As a basic principle, the symbols for declared types always link to their definition in the source code using a location value, while other implicit types do not have such a link (i.e. `int` and `void`). + +We cater for languages to have a subtype relation to be defined, and a least upper bound computation. +} +@benefits{ +* symbolic types can be analyzed and manipulated symbolically, i.e. to instantiate parametrized types. +* symbolic types can be used directly as constraint variables for type inference or type-based refactoring purposes. +} +@pitfalls{ +* If you import extensions to this M3 model for two different languages, ambiguity and other confusion may arise +because the subtype and lub functions of the two languages will merge. +* TypeSymbols are sometimes confused with their predecessors in the analysis pipeline: abstract syntax trees of `Type`. + * If `Type` is the syntax of types, then `TypeSymbol` represents their semantics. + * Programmers can only type in `Type` instances, while programming languages may also feature types which do not have syntax (such as union an intersection types during inference). + * Type and TypeSymbol constructors are the same when they are related, i.e. Type::\int() will produce TypeSymbol::\int(). + * Often different ways of writing the same type in syntax, produce a normal and simplified form in TypeSymbol representation. + * Abstract or generic (parameterized) TypeSymbols can be instantiated during abstract interpretation and other analysis algorithms. + * Instances of syntactic `Type` always have their `src` location with them, while most TypeSymbols are deduced and inferred and do not + have a clear source origin. +} +module analysis::m3::TypeSymbol + +data TypeSymbol = unresolved(); +### |project://rascal/src/org/rascalmpl/library/analysis/statistics/Correlation.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Correlation between data values.} +@description{ +Compute the [correlation](http://en.wikipedia.org/wiki/Correlation) between pairs of data values. +Correlation measures the statistical relationship between two sets of data. + +The following functions are provided: +(((TOC))) +} +module analysis::statistics::Correlation + +@synopsis{Pearson product-moment correlation coefficient.} +@description{ +Compute the [Pearson product-moment correlation coefficient](http://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient). +It is a measure of the strength of the linear dependence between two variables. +} +@pitfalls{ +Use ((SpearmansCorrelation)) when there is a *monotonous dependence* between the two variables. +} +@javaClass{org.rascalmpl.library.analysis.statistics.Correlations} +public java num PearsonsCorrelation(lrel[num x, num y] values); + +@synopsis{Standard errors associated with Pearson correlation.} +@javaClass{org.rascalmpl.library.analysis.statistics.Correlations} +public +java list[real] PearsonsCorrelationStandardErrors( + lrel[num x, num y] values +); + +@synopsis{P-values (significance) associated with Pearson correlation.} +@javaClass{org.rascalmpl.library.analysis.statistics.Correlations} +public +java list[real] PearsonsCorrelationPValues(lrel[num x, num y] values); + +@synopsis{Spearman's rank correlation coefficient.} +@description{ +Compute [Spearman's rank correlation coefficient](http://en.wikipedia.org/wiki/Spearman's_rank_correlation_coefficient). +The correlation between the data values is computed by first performing a rank transformation +on the data values using a natural ranking and then computing ((PearsonsCorrelation)). +} +@pitfalls{ +Use ((PearsonsCorrelation)) when there is a *linear dependence* between the variables. +} +@javaClass{org.rascalmpl.library.analysis.statistics.Correlations} +public java num SpearmansCorrelation(lrel[num x, num y] values); + +@synopsis{Covariance of data values.} +@description{ +Computes the [covariance](http://en.wikipedia.org/wiki/Covariance) between the `x` and `y` values. +} +@examples{ +```rascal-shell +import analysis::statistics::Correlation; +covariance([<1,12>,<3,12>,<3,11>,<5,7>]); +``` +} +@javaClass{org.rascalmpl.library.analysis.statistics.Correlations} +public java num covariance(lrel[num x, num y] values); +### |project://rascal/src/org/rascalmpl/library/analysis/statistics/Descriptive.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Descriptive Statistics.} +@description{ +Provides the following univariate (single variable) statistics functions: + +(((TOC))) +} +@examples{ +```rascal-shell +import analysis::statistics::Descriptive; +D = [67, 88, 55, 92.5, 102, 51]; +mn = min(D); +mx = max(D); +range = mx - mn; +midrange = mn + range/2; +sum(D); +mean(D); +geometricMean(D); +standardDeviation(D); +variance(D); +percentile(D,25); +percentile(D,50); +percentile(D,75); +``` +} +module analysis::statistics::Descriptive + +import Exception; +import util::Math; +import List; + +@synopsis{Geometric mean of data values.} +@description{ +Computes the [geometric mean](http://en.wikipedia.org/wiki/Geometric_mean) of the given data values. +} +real geometricMean([num hd, *num tl]) { + if (tl == []) { + return toReal(hd); + } + prod = ( hd | it * v | v <- tl ); + if (prod < 0) { + throw ArithmeticException( + "Geometric mean can only be calculated for positive numbers" + ); + } + if (prod == 0) { + return toReal(prod); + } + return nroot(prod, 1 + size(tl)); +} + +@synopsis{Kurtosis of data values.} +@description{ +Computes the [kurtosis](http://en.wikipedia.org/wiki/Kurtosis) of the given data values. +Kurtosis is a measure of the "peakedness" of a distribution. +} +real kurtosis(list[num] values: [_, *_]) { + varPow = pow(variance(values), 2); + + if (varPow == 0.) { + throw ArithmeticException( + "kurtosis is undefined for values with 0 variance" + ); + } + return centralMoment(values, order = 4) / varPow; +} + +@synopsis{Kurtosis excess of data values.} +@description{ +Computes the [kurtosis excess](http://en.wikipedia.org/wiki/Kurtosis) of the given data values. +Kurtosis excess is a measure of the "peakedness" of a distribution corrected such that a normal distribution will be 0. +} +real kurtosisExcess(list[num] values) = kurtosis(values) - 3; + +@synopsis{Largest data value.} +(&T <: num) max([(&T <: num) h, *(&T <: num) t]) = ( h | it < n ? n : it | n <- t ); + +@synopsis{Arithmetic mean of data values.} +@description{ +Computes the [arithmetic mean](http://en.wikipedia.org/wiki/Arithmetic_mean) of the data values. +} +real mean(list[num] nums: [_, *_]) = toReal(sum(nums)) / size(nums); + +@synopsis{Median of data values.} +@description{ +Returns the [median](http://en.wikipedia.org/wiki/Median) of the available values. +This is the same as the 50th ((percentile)). +} +@examples{ +```rascal-shell +import analysis::statistics::Descriptive; +median([1,2,5,7,8]); +median([1,2,2,6,7,8]); +``` +} +default real median(list[num] nums: [_, *_]) = mean(middle(nums)); + +private list[&T] middle(list[&T] nums) { + nums = sort(nums); + n = size(nums); + if (n % 2 == 1) { + return [nums[n / 2]]; + } + n = n / 2; + return nums[n - 1..n + 1]; +} + +@synopsis{Smallest data value.} +(&T <: num) min([(&T <: num) h, *(&T <: num) t]) = ( h | it > n ? n : it | n <- t ); + +@synopsis{Percentile of data values.} +@description{ +Returns the `p`th [percentile](http://en.wikipedia.org/wiki/Percentile) of the data values. + 0 < `p` <= 100 should hold. +} +&T <: num percentile(list[&T <: num] nums, num p) { + if (0 > p || p > 100) { + throw IllegalArgument(p, "Percentile argument should be between 0 and 100"); + } + nums = sort(nums); + idx = max(1., toReal(size(nums)) * (toReal(p) / 100)); + return nums[ceil(idx) - 1]; +} + +@synopsis{Variance of data values.} +@description{ +Computes the [variance](http://en.wikipedia.org/wiki/Variance) of the data values. +It measures how far a set of numbers is spread out. +} +num variance([num hd, *num tl]) { + if (tl == []) { + return 0.; + } + //Compensated variant of the two pass algorithm + n = 1 + size(tl); + mn = mean(tl + hd); + sum2 = ( pow(hd - mn, 2) | it + pow(i - mn, 2) | i <- tl ); + sum3 = ( hd - mn | it + (i - mn) | i <- tl ); + return (sum2 - (pow(sum3, 2) / n)) / (n - 1); +} + +@synopsis{Skewness of data values.} +@description{ +Returns the [skewness](http://en.wikipedia.org/wiki/Skewness) of the available values. Skewness is a measure of the asymmetry of a given distribution. +} +real skewness(list[num] values: [_, *_]) + = centralMoment(values, order = 3) / pow(centralMoment(values, order = 2), 3 / 2); + +@synopsis{Standard deviation of data values.} +@description{ +Computes the [standard deviation](http://en.wikipedia.org/wiki/Standard_deviation) +of the data values. It shows how much variation exists from the average (mean, or expected value). +} +real standardDeviation(list[num] values) { + if (values == []) { + throw IllegalArgument( + values, "Standard Deviation cannot be calculated for empty lists" + ); + } + return sqrt(variance(values)); +} + +@synopsis{Sum of data values.} +public (&T <: num) sum([(&T <: num) hd, *(&T <: num) tl]) = ( hd | it + i | i <- tl ); + +@synopsis{Sum of the squares of data values.} +(&T <: num) sumsq(list[&T <: num] values) = sum([n * n | n <- values]); + +@synopsis{Calculate the k-th central moment} +real centralMoment(list[num] nums: [_, *_], int order = 1) { + if (order < 0) { + throw IllegalArgument( + nums, "Central moment cannot be calculated for the -th order." + ); + } + if (order == 0) { + return 1.; + } + if (order == 1) { + return 0.; + } + mn = mean(nums); + return moment([n - mn | n <- nums], order = order); +} + +@synopsis{Calculate the k-th moment} +real moment(list[num] nums: [_, *_], int order = 1) { + if (order < 0) { + throw IllegalArgument( + order, "Central moment cannot be calculated for the -th order." + ); + } + if (order == 0) { + return 1.; + } + if (order == 1) { + return toReal(sum(nums)) / size(nums); + } + return ( 0. | it + pow(n, order) | n <- nums ) / size(nums); +} +### |project://rascal/src/org/rascalmpl/library/analysis/statistics/Frequency.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Frequency distributions.} +@description{ +Counting the frequency of events is usually the first step in statistical analysis of raw data. +It involves choosing what are the events to count, how to group them in certain +categories and then quickly counting the frequency of each occurring event. + +This module helps by providing commonly used functions for the purpose of counting events. +The output of these functions can be used to draw (cumulative) histograms, or they can +directly be used for further statistical processing and visualization. +} +module analysis::statistics::Frequency + +import util::Math; + +@synopsis{Compute a distribution: count how many times events are mapped to which bucket.} +@examples{ +```rascal-shell +import analysis::statistics::Frequency; +distribution({<"chicken","animal">,<"bear","animal">,<"oak","plant">,<"tulip","plant">}); +distribution({<"alice",2>,<"bob",3>,<"claire",5>},5); +``` +} +public map[&T, int] distribution(rel[&U event, &T bucket] input) { + map[&T, int] result = (); + for (<&U _, &T bucket> <- input) { + result[bucket] ? 0 += 1; + } + + return result; +} + +public +map[&T <: num, int] distribution( + rel[&U event, &T <: num bucket] input, &T <: num bucketSize +) { + map[&T <: num, int] result = (); + for (<&U _, &T <: num bucket> <- input) { + result[round(bucket, bucketSize)] ? 0 += 1; + } + return result; +} + +public map[&T, int] distribution(map[&U event, &T bucket] input) { + map[&T, int] result = (); + for (&U event <- input) { + result[input[event]] ? 0 += 1; + } + + return result; +} + +public +map[&T <: num, int] distribution( + map[&U event, &T <: num bucket] input, &T <: num bucketSize +) { + map[&T <: num, int] result = (); + for (&U event <- input) { + result[round(input[event], bucketSize)] ? 0 += 1; + } + return result; +} + +@synopsis{Cumulative frequency of values less than or equal to a given value.} +@description{ +Returns the cumulative frequency of values less than or equal to a given numeric or string value. +Returns 0 if the value is not comparable to the values set. +} +@examples{ +```rascal-shell +import analysis::statistics::Frequency; +D = [1, 2, 1, 1, 3, 5]; +cumFreq(D, 1); +cumFreq(D, 2); +cumFreq(D, 10); +``` +} +@javaClass{org.rascalmpl.library.analysis.statistics.Frequencies} +public java int cumFreq(list[value] values, num n); + +@javaClass{org.rascalmpl.library.analysis.statistics.Frequencies} +public java int cumFreq(list[value] values, str s); + +@synopsis{Cumulative percentage of values less than or equal to a given value.} +@description{ +Returns the cumulative percentage of values less than or equal to v (as a proportion between 0 and 1). + +```rascal-shell +import analysis::statistics::Frequency; +D = [1, 2, 1, 1, 3, 5]; +cumPct(D, 1); +cumPct(D, 2); +cumPct(D, 10); +``` +} +@javaClass{org.rascalmpl.library.analysis.statistics.Frequencies} +public java num cumPct(list[value] values, num n); + +@javaClass{org.rascalmpl.library.analysis.statistics.Frequencies} +public java num cumPct(list[value] values, str s); + +@synopsis{Percentage of values that are equal to a given value.} +@description{ +Returns the percentage of values that are equal to v (as a proportion between 0 and 1). +} +@examples{ +```rascal-shell +import analysis::statistics::Frequency; +D = [1, 2, 1, 1, 3, 5]; +pct(D, 1); +pct(D, 2); +pct(D, 10); +``` +} +@javaClass{org.rascalmpl.library.analysis.statistics.Frequencies} +public java num pct(list[value] values, num n); + +@javaClass{org.rascalmpl.library.analysis.statistics.Frequencies} +public java num pct(list[value] values, str s); +### |project://rascal/src/org/rascalmpl/library/analysis/statistics/Inference.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Statistical inference methods.} +@description{ +The following functions are provided: +(((TOC))) +} +module analysis::statistics::Inference + +@synopsis{Chi-square coefficient of data values.} +@description{ +Compute the [ChiSquare statistic](http://en.wikipedia.org/wiki/Chi-square_statistic) comparing observed and expected frequency counts. +} +@examples{ +Consider an example from the web page mentioned above. +To test the hypothesis that a random sample of 100 people has been drawn from a population in which men and women are equal in frequency, the observed number of men and women would be compared to the theoretical frequencies of 50 men and 50 women. If there were 44 men in the sample and 56 women, then we have the following: + +```rascal-shell +import analysis::statistics::Inference; +chiSquare([<50, 44>, <50, 56>]) +``` +} +@javaClass{org.rascalmpl.library.analysis.statistics.Inferences} +public java num chiSquare(lrel[num expected, int observed] values); + +@synopsis{Chi-square test on data values.} +@description{ +Perform a [chi-square test](http://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test) comparing +expected and observed frequency counts. There are two forms of this test: + +* Returns the _observed significance level_, or p-value, associated with a Chi-square goodness of fit test +comparing observed frequency counts to expected counts. + +* Performs a Chi-square goodness of fit test evaluating the null hypothesis that the observed counts conform to the frequency distribution described by the expected counts, with significance level `alpha` (0 < `alpha` < 0.5). Returns true iff the null hypothesis +can be rejected with confidence 1 - `alpha`. +} +@javaClass{org.rascalmpl.library.analysis.statistics.Inferences} +public java num chiSquareTest(lrel[num expected, int observed] values); + +@javaClass{org.rascalmpl.library.analysis.statistics.Inferences} +public +java bool chiSquareTest(lrel[num expected, int observed] values, real alpha); + +@synopsis{T-test on sample data.} +@description{ +Perform [student's t-test](http://en.wikipedia.org/wiki/Student's_t-test) +The test is provided in three variants: + +* Returns the _observed significance level_, or _p-value_, associated with a two-sample, two-tailed t-test comparing the means of the input samples. The number returned is the smallest significance level at which one can reject the null hypothesis that the two means are equal in favor of the two-sided alternative that they are different. For a one-sided test, divide the returned value by 2. + +The t-statistic used is `t = (m1 - m2) / sqrt(var1/n1 + var2/n2)` +where + +** `n1` is the size of the first sample +** `n2` is the size of the second sample; +** `m1` is the mean of the first sample; +** `m2` is the mean of the second sample; +** `var1` is the variance of the first sample; +** `var2` is the variance of the second sample. + +* Performs a two-sided t-test evaluating the null hypothesis that `sample1` and `sample2` are drawn from populations with the same mean, with significance level `alpha`. This test does not assume that the subpopulation variances are equal. +Returns true iff the null hypothesis that the means are equal can be rejected with confidence 1 - `alpha`. To perform a 1-sided test, use `alpha` / 2. + +* Performs a two-sided t-test evaluating the null hypothesis that the mean of the population from which sample is drawn equals `mu`. +Returns true iff the null hypothesis can be rejected with confidence 1 - `alpha`. To perform a 1-sided test, use `alpha` * 2. +} +@examples{ +We use the data from the following http://web.mst.edu/~psyworld/texample.htm#1[example] to illustrate the t-test. +First, we compute the t-statistic using the formula given above. +```rascal-shell +import util::Math; +import analysis::statistics::Descriptive; +import List; +s1 = [5,7,5,3,5,3,3,9]; +s2 = [8,1,4,6,6,4,1,2]; +(mean(s1) - mean(s2))/sqrt(variance(s1)/size(s1) + variance(s2)/size(s2)); +``` +This is the same result as obtained in the cited example. +We can also compute it directly using the `tTest` functions: +```rascal-shell,continue +import analysis::statistics::Inference; +tTest(s1, s2); +``` +Observe that this is a smaller value than comes out directly of the formula. +Recall that: _The number returned is the smallest significance level at which one can reject the null hypothesis that the two means are equal in favor of the two-sided alternative that they are different._ +Finally, we perform the test around the significance level we just obtained: +```rascal-shell,continue +tTest(s1,s2,0.40); +tTest(s1,s2,0.50); +``` +} +@javaClass{org.rascalmpl.library.analysis.statistics.Inferences} +public java num tTest(list[num] sample1, list[num] sample2); + +@javaClass{org.rascalmpl.library.analysis.statistics.Inferences} +public java bool tTest(list[num] sample1, list[num] sample2, num alpha); + +@javaClass{org.rascalmpl.library.analysis.statistics.Inferences} +public java bool tTest(num mu, list[num] sample, num alpha); + +@synopsis{Analysis of Variance (ANOVA) f-value.} +@description{ +Perform [Analysis of Variance test](http://en.wikipedia.org/wiki/Analysis_of_variance) +also described [here](http://www.statsoft.com/textbook/anova-manova/) + +Compute the F statistic -- also known as [F-test](http://en.wikipedia.org/wiki/F-test) -- using the definitional formula + `F = msbg/mswg` +where + +* `msbg` = between group mean square. +* `mswg` = within group mean square. + + +are as defined [here](http://faculty.vassar.edu/lowry/ch13pt1.html) +} +@javaClass{org.rascalmpl.library.analysis.statistics.Inferences} +public java num anovaFValue(list[list[num]] categoryData); + +@synopsis{Analysis of Variance (ANOVA) p-value.} +@description{ +Perform [Analysis of Variance test](http://en.wikipedia.org/wiki/Analysis_of_variance) +also described [here](http://www.statsoft.com/textbook/anova-manova/) + +Computes the exact p-value using the formula `p = 1 - cumulativeProbability(F)` +where `F` is the ((anovaFValue)). +} +@javaClass{org.rascalmpl.library.analysis.statistics.Inferences} +public java num anovaPValue(list[list[num]] categoryData); + +@synopsis{Analysis of Variance (ANOVA) test.} +@description{ +Perform [Analysis of Variance](http://en.wikipedia.org/wiki/Analysis_of_variance) +also described [here](http://www.statsoft.com/textbook/anova-manova/) + +Returns true iff the estimated p-value is less than `alpha` (0 < `alpha` <= 0.5). + +The exact p-value is computed using the formula `p = 1 - cumulativeProbability(F)` +where `F` is the ((anovaFValue)). +} +@javaClass{org.rascalmpl.library.analysis.statistics.Inferences} +public java bool anovaTest(list[list[num]] categoryData, num alpha); + +@synopsis{Gini coefficient.} +@description{ +Computes the [Gini Coefficient](http://en.wikipedia.org/wiki/Gini_coefficient) +that measures the inequality among values in a frequency distribution. + +The Gini coefficient is computed using Deaton's formula and returns a +value between 0 (completely equal distribution) and +1 (completely unequal distribution). +} +@examples{ +```rascal-shell +import analysis::statistics::Inference; +``` +A completely equal distribution: +```rascal-shell,continue +gini([<10000, 1>, <10000, 1>, <10000, 1>]); +``` +A rather unequal distribution: +```rascal-shell,continue +gini([<998000, 1>, <20000, 3>, <117500, 1>, <70000, 2>, <23500, 5>, <45200,1>]); +``` +} +@javaClass{org.rascalmpl.library.analysis.statistics.Inferences} +public java real gini(lrel[num observation, int frequency] values); +### |project://rascal/src/org/rascalmpl/library/analysis/statistics/SimpleRegression.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Statistical methods for simple regression.} +@description{ +The following functions are provided: +(((TOC))) +} +module analysis::statistics::SimpleRegression + +import Exception; + +@synopsis{Intercept of regression line.} +@description{ +Returns the [intercept](http://en.wikipedia.org/wiki/Root_of_a_function) of the estimated regression line. +The least squares estimate of the intercept is computed using these [normal equations](http://www.xycoon.com/estimation4.htm) +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public java num intercept(lrel[num, num] values) throws IllegalArgument; + +@synopsis{Standard error of intercept estimate.} +@description{ +Returns the http://www.xycoon.com/standarderrorb0.htm[standard error of the intercept estimate], usually denoted s(b0). +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public +java num interceptStdErr(lrel[num, num] values) throws IllegalArgument; + +@synopsis{Sum of squared errors divided by the degrees of freedom.} +@description{ +Returns the sum of squared errors divided by the degrees of freedom, usually abbreviated MSE. +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public +java num meanSquareError(lrel[num, num] values) throws IllegalArgument; + +@synopsis{Pearson's product-moment correlation coefficient.} +@description{ +Computes Pearson's product-moment correlation coefficient. +More functions related to this coefficient can be found in ((module:analysis::statistics::Correlation)). +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public java num R(lrel[num, num] values) throws IllegalArgument; + +@synopsis{Sum of squared deviations of the predicted y values about their mean.} +@description{ +Returns the sum of squared deviations of the predicted y values about their mean (which equals the mean of y). +This is usually abbreviated SSR or [SSM](http://www.xycoon.com/SumOfSquares.htm). +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public +java num regressionSumSquares( + list[tuple[num, num]] values +) throws IllegalArgument; + +@synopsis{Coefficient of determination.} +@description{ +Returns the [coefficient of determination](http://en.wikipedia.org/wiki/Coefficient_of_determination) usually denoted r__^2^. +It provides a measure of how well future outcomes are likely to be predicted by the regression model. +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public java num RSquare(lrel[num, num] values) throws IllegalArgument; + +@synopsis{Significance of the slope correlation.} +@description{ +Returns the significance level of the slope (equiv) correlation. +Specifically, the returned value is the smallest alpha such that the slope confidence interval with significance level equal to alpha does not include 0. On regression output, this is often denoted Prob(|t| > 0) +} +@pitfalls{ +The validity of this statistic depends on the assumption that the observations included in the model are drawn from a +[Bivariate Normal Distribution](http://en.wikipedia.org/wiki/Bivariate_normal_distribution). +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public java num significance(lrel[num, num] values) throws IllegalArgument; + +@synopsis{Slope of regression line.} +@description{ +Returns the slope of the estimated regression line. +The least squares estimate of the slope is computed using the http://www.xycoon.com/estimation4.htm[normal equations]. +The slope is sometimes denoted b1. +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public java num slope(lrel[num, num] values) throws IllegalArgument; + +@synopsis{The 95% slope confidence interval.} +@description{ +Returns the half-width of a 95% confidence interval for the slope estimate. +The 95% confidence interval is + +(slope - slopeConfidenceInterval, slope + slopeConfidenceInterval) +} +@pitfalls{ +* The validity of this statistic depends on the assumption that the observations included in the model are drawn from a +[Bivariate Normal Distribution](http://en.wikipedia.org/wiki/Bivariate_normal_distribution) +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public +java num slopeConfidenceInterval( + lrel[num, num] values +) throws IllegalArgument; + +@synopsis{Standard error of slope estimate.} +@description{ +Returns the http://www.xycoon.com/standarderrorb0.htm[standard error of the slope estimate], usually denoted s(b1). +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public java num slopeStdErr(lrel[num, num] values) throws IllegalArgument; + +@synopsis{Sum of cross products of observations.} +@description{ +Returns the sum of cross products, x__~i~*y__~i~. +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public +java num sumOfCrossProducts(lrel[num, num] values) throws IllegalArgument; + +@synopsis{Sum of squared errors.} +@description{ +Returns the sum of squared errors (SSE) associated with the regression model. +The sum is computed using the computational formula + +SSE = SYY - (SXY * SXY / SXX) + +where SYY is the sum of the squared deviations of the y values about their mean, SXX is similarly defined and SXY is the sum of the products of x and y mean deviations. + +The return value is constrained to be non-negative, i.e., if due to rounding errors the computational formula returns a negative result, 0 is returned. +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public +java num sumSquaredErrors(lrel[num, num] values) throws IllegalArgument; + +@synopsis{Sum of squared deviations.} +@description{ +Returns the sum of squared deviations of the y values about their mean. +This is defined as http://www.xycoon.com/SumOfSquares.htm[SSTO]. +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public +java num totalSumSquares(lrel[num, num] values) throws IllegalArgument; + +@synopsis{Sum of squared deviations of x values about their mean.} +@description{ +Returns the sum of squared deviations of the x values about their mean. +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public java num XSumSquares(lrel[num, num] values) throws IllegalArgument; + +@synopsis{Predict a value.} +@description{ +Returns the "predicted" `y` value associated with the supplied `x` value, based on regression model derived from the provided data values: + +`predict(x) = intercept + slope * x` +} +@javaClass{org.rascalmpl.library.analysis.statistics.SimpleRegressions} +public java num predict(lrel[num, num] values, num x) throws IllegalArgument; +### |project://rascal/src/org/rascalmpl/library/lang/aterm/IO.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Mark Hills - Mark.Hills@cwi.nl (CWI)} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +module lang::aterm::IO + +@synopsis{read an ATerm from a text file} +@javaClass{org.rascalmpl.library.lang.aterm.IO} +public java &T readTextATermFile(type[&T] begin, loc location); + +public value readTextATermFile(loc location) { + return readTextATermFile(#value, location); +} + +@synopsis{Read an ATerm from a named file.} +@javaClass{org.rascalmpl.library.lang.aterm.IO} +public java value readATermFromFile(str fileName); + +@synopsis{write an ATerm to a text file} +@javaClass{org.rascalmpl.library.lang.aterm.IO} +public java void writeTextATermFile(loc location, value v); +### |project://rascal/src/org/rascalmpl/library/lang/aterm/syntax/ATerm.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen Vinju - Jurgen.Vinju@cwi.nl} +module lang::aterm::\syntax::ATerm + +syntax AFun + = Quoted: StrCon + | Unquoted: IdCon + ; + +syntax ATerm + = Int: IntCon + | Real: RealCon + | Fun: AFun + | Appl: AFun "(" {ATerm ","}+ ")" + | Placeholder: "\<" ATerm "\>" + | List: "[" {ATerm ","}* "]" + | Annotated: ATerm Annotation + ; + +syntax Annotation = Default: "{" {ATerm ","}+ "}"; + +syntax IntCon + = Natural: NatCon + | Positive: "+" NatCon + | Negative: "-" NatCon + ; + +syntax RealCon = RealCon: IntCon "." NatCon OptExp; + +syntax OptExp + = Present: "e" IntCon + | Absent: + ; + +lexical NatCon = Digits: [0-9]+ !>> [0-9]; + +lexical StrChar + = NewLine: [\\] [n] + | Tab: [\\] [t] + | Quote: [\\] [\"] + | Backslash: [\\] [\\] + | Decimal: [\\] [0-9] [0-9] [0-9] + | Normal: ![\n\t\"\\] + ; + +lexical StrCon = Default: [\"] StrChar* [\"]; + +lexical IdCon = Default: [A-Za-z] [A-Za-z\-0-9]* !>> [A-Za-z\-0-9]; +### |project://rascal/src/org/rascalmpl/library/lang/aut/IO.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Bert Lisser - Jurgen.Vinju@cwi.nl - CWI} +module lang::aut::IO + +@synopsis{Read relations from an AUT file.} +@description{ +An AUT file contains tuples of ternary relation as lines with the following format: +* `(,,)` +* each field is separated by a comma + +readAUT takes an AUT file and generates a value of type `rel[int, str,int]`. +} +@javaClass{org.rascalmpl.library.lang.aut.IO} +public java rel[int, str, int] readAUT(str nameAUTFile); + +@synopsis{write an AUT file} +@javaClass{org.rascalmpl.library.lang.aut.IO} +public java void writeAUT(str nameAUTFile, rel[int, str, int] r); +### |project://rascal/src/org/rascalmpl/library/lang/box/syntax/Box.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl (CWI)} +@synopsis{An abstract declarative language for two dimensional text layout} +module lang::box::\syntax::Box + +import List; + +@synopsis{Every kind of boxes encodes one or more parameterized two-dimensional text constraints.} +@description{ +* `H` puts their elements next to each other one the same line separated by `hs` spaces. +* `V` puts their elements below each other on their own line, separated by `vs` empty lines. +* `HOV` acts like `H` as long as the elements all fit on one line, otherwise it becomes a `V` +* `HV` acts like H until the line is full, and then continues on the next line like `V`. +* `I` is a `V` box that indents every line in the output with `is` spaces +* `WD` produces a number of spaces exactly as wide as the wides line of the constituent boxes +* `A` is a table formatter. The list of Rows is formatted with `H` but each cell is aligned vertically with the rows above and below. +* `SPACE` produces `space` spaces +* `L` produces A literal word. This word may only contain printable characters and no spaces; this is a required property that the formatting algorithm depends on for correctness. +* `U` splices its contents in the surrounding box, for automatic flattening of overly nested structures in syntax trees. +* `G` is an additional group-by feature for `list[Box]` that reduces tot the above core features. You can use it to wrap another +box around every `gs` elements. +* `AG` is an additional group-by feature for array `Row`s that reduces to the above core features. You can use it to wrap a `R` row +around every `gs` elements and then construct an `A` around those rows. +* `NULL()` is the group that will dissappear from its context, useful for skipping content. It is based on the `U` box. +} +@benefits{ +* Box expressions are a declarative mechanism to express formatting rules that are flexible enough to deal +with limited horizontal space, and satisfy the typical alignment and indentation principles found in +the coding standards for programming languages. +* The parameters of Box expressions allow for full configuration. It is up to the code that produces Box +expressions to present these parameters to the user or not. For example, indentation level `is` should be +set on every `I` Box according to the current preferences of the user. +} +@pitfalls{ +* `U(boxes)` is rendered as `H(boxes)` if it's the outermost Box. +} +data Box (int hs = 1, int vs = 0, int is = 4) + = H_(list[Box] boxes) + | V_(list[Box] boxes) + | HOV_(list[Box] boxes) + | HV_(list[Box] boxes) + | I_(list[Box] boxes) + | WD_(list[Box] boxes) + | A_(list[Row] rows, Box rs = NULL(), list[Alignment] columns = []) + | AG_(list[Box] boxes, int gs = 2, list[Alignment] columns = [], Box rs = NULL()) + | SPACE(int space) + | L(str word) + | U_(list[Box] boxes) + | G_(list[Box] boxes, bool backwards = false, int gs = 2, Box op = H([])) + | NULL() + ; + +Box H(Box boxes..., int hs = 1) = H_(boxes, hs = hs); +Box V(Box boxes..., int vs = 0) = V_(boxes, vs = vs); +Box HOV(Box boxes..., int hs = 1, int vs = 0) = HOV_(boxes, hs = hs, vs = vs); +Box HV(Box boxes..., int hs = 1, int vs = 0) = HV_(boxes, hs = hs, vs = vs); +Box I(Box boxes...) = I_(boxes); +Box WD(Box boxes...) = WD_(boxes); +Box A(Row rows..., Box rs = NULL(), list[Alignment] columns = []) + = A_(rows, rs = rs, columns = columns); +Box AG(Box boxes..., int gs = 2, list[Alignment] columns = [], Box rs = NULL()) + = AG_(boxes, gs = gs, columns = columns, rs = rs); +Box U(Box boxes...) = U_(boxes); +Box G(Box boxes..., bool backwards = false, int gs = 2, Box op = H([])) + = G_(boxes, backwards = backwards, gs = gs, op = op); + +@synopsis{A row is a list of boxes that go into an `A` array/table.} +@description{ +Rows do not have parameters. These are set on the `A` level instead, +or per cell Box. +} +data Row = R(list[Box] cells); + +// Row R(Box cells...) = _R(cells); +data Alignment + = l() + | r() + | c() + ; + +@synopsis{NULL can be used to return a Box that will completely disappear in the surrounding context.} +@description{ +Consider `NULL()`` as an alternative to producing `H([])` when you see unexpected +additional spaces generated. + +Typical applications for NULL would be to produce it for layout nodes that contain +only whitespace. If you encounter source code comments you can produce the appropriate `L` content, +but if you want the position ignored, use `NULL`. + +`NULL`` depends on the splicing semantics of `U` to disappear completely before the layout +algorithm starts counting boxes and widths. +} +@examples{ +* `H([L("A"), H([]),L("B")])` will produce `"A B"` with two spaces; +* `H([L("A"), NULL(),L("B")])` will produce `"A B"` with only one space. +} +@pitfalls{ +* Do not use `NULL` for empty Row cells, unless you do want your cells aligned to the left and filled up to the right with empty H boxes. +* NULL will be formatted as `H([])` if it's the outermost Box. +} +Box NULL() = U([]); + +@synopsis{Convenience box for adding separators to an existing box list} +@description{ +Each element is wrapped by the `op` operator together with the next separator. +The resulting list is wrapped by a G box, of which the elements will be spliced +into their context. +} +Box SL(list[Box] boxes, Box sep, Box op = H([ ], hs = 0 )) + = G([b, sep | b <- boxes][..-1], op = op, gs = 2); + +@synopsis{Flatten and fold U and G boxes to simplify the Box structure} +@description{ +U and G and AG boxes greatly simplify the Box tree before it is formatted. This +happens "just-in-time" for efficiency reasons. However, from a Box tree +with many U and G boxes it can become hard to see what the actual formatting +constraints are going to be. + +This function applies the semantics of G and U and returns a Box that renders +exactly the same output, but with a lot less nested structure. +} +@benefits{ +* useful to debug complex `toBox` mappings +* formatting semantics preserving transformation +} +@pitfalls{ +* only useful for debugging purposes, because it becomes a pipeline bottleneck otherwise. +} +Box debUG(Box b) { + list[Box] groupBy([], int _gs, Box _op) = []; + list[Box] groupBy(list[Box] boxes: [Box _, *_], int gs, Box op) + = [op[boxes = boxes[..gs]], *groupBy(boxes[gs..], gs, op)]; + + list[Box] groupByBackward([], int _gs, Box _op) = []; + list[Box] groupByBackward(list[Box] boxes: [Box _, *_], int gs, Box op) + = [ + op[boxes = boxes[..size(boxes) mod gs]], + *groupBy(boxes[size(boxes) mod gs..], gs, op) + ]; + + list[Row] groupRows([], int _gs) = []; + list[Row] groupRows(list[Box] boxes: [Box _, *_], int gs) + = [R(boxes[..gs]), *groupRows(boxes[gs..], gs)]; + + return + innermost visit(b) { + case [*Box pre, U_([*Box mid]), *Box post] => [*pre, *mid, *post] + case G_(list[Box] boxes,gs = gs, op = op, backwards = bw) + => U_(bw ? groupByBackward(boxes, gs, op) : groupBy(boxes, gs, op)) + case AG_(list[Box] boxes,gs = gs, columns = cs, rs = rs) + => A(groupRows(boxes, gs), columns = cs, rs = rs) + } +} + +@synopsis{Short-hand for `H(hs=0)``} +Box H0(Box boxes...) = H_(boxes, hs = 0); + +@synopsis{Short-hand for `HOV(hs=0)``} +Box HOV0(Box boxes...) = HOV_(boxes, hs = 0); + +@synopsis{Short-hand for `H(hs=1)``} +Box H1(Box boxes...) = H_(boxes, hs = 1); + +@synopsis{Short-hand for `HOV(hs=1)``} +Box HOV1(Box boxes...) = HOV_(boxes, hs = 1); + +@synopsis{Short-hand for `V(vs=0)``} +Box V0(Box boxes...) = V_(boxes, vs = 0); + +@synopsis{Short-hand for `V(vs=1)``} +Box V1(Box boxes...) = V_(boxes, vs = 1); +### |project://rascal/src/org/rascalmpl/library/lang/box/util/Box2Text.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl (CWI)} +@synopsis{Two-dimensional text layout algorithm} +@description{ +The input to Box2Text is a hierarchy of "Boxes" represented by the ((lang::box::\syntax::Box)) algebraic data-type. +These boxes put hard and soft relative positioning constraints on the embedded text fragments, and +there is the global soft constraints of the width of the screen (or the paper). + + +This implementation is a port from ASF+SDF to Rascal. The ASF+SDF implementation was published as +"From Box to Tex:An algebraic approach to the construction of documentation tools" by Mark van den Brand +and Eelco Visser (June 30, 1994). The original Box concept was introduced by Joel Coutaz as this technical report: +"The Box, A Layout Abstraction for User Interface Toolkits" (1984) Pittsburgh, PA: Carnegie Mellon University. + +The main function `format` maps a Box tree to a `str`: +* To obtain Box terms people typically transform ASTs or ((ParseTree))s to Box using pattern matching in Rascal. +* ((Options)) encode global default options for constraint parameters that only override local parameters if they were elided. +} +@examples{ +This demonstrates the semantics of the main hard constraints: +* `H` for horizontal; +* `V` for vertical; +* `I` for indentation. + +```rascal-shell +import lang::box::util::Box2Text; +import lang::box::\syntax::Box; +format(H(L("A"), L("B"), L("C"), hs=2)) +format(H(L("A"), L("B"), L("C"), hs=1)) +format(H(L("A"), L("B"), L("C"), hs=0)) +format(V(L("A"), L("B"), L("C"), vs=2)) +format(V(L("A"), L("B"), L("C"), vs=1)) +format(V(L("A"), L("B"), L("C"), vs=0)) +format(H(L("A"), V(L("B"), L("C")))) +format(H(L("A"), I(L("B")), L("C"))) +format(H(L("A"), V(L("B"), H(L("C"), L("D"))))) +``` + +The "soft" constraints change their behavior based on available horizontal room: +```rascal-shell,continue +format(HV([L("W") | i <- [0..10]])); +format(HV([L("W") | i <- [0..20]])); +format(HV([L("W") | i <- [0..40]])); +format(HV([L("W") | i <- [0..80]])); +format(HV([L("W") | i <- [0..100]])); +format(HOV([L("W") | i <- [0..10]])); +format(HOV([L("W") | i <- [0..20]])); +format(HOV([L("W") | i <- [0..30]])); +``` + +By cleverly combining constraints, a specifically desired behavior is easy to achieve: +```rascal-shell,continue +format(H(L("if"), H(L("("), L("true"), L(")"), hs=0), HOV(L("doSomething")))) +format(H(L("if"), H(L("("), L("true"), L(")"), hs=0), HOV([L("W") | i <- [0..30]]))) +format(H(L("if"), H(L("("), L("true"), L(")"), hs=0), HV([L("W") | i <- [0..30]]))) +``` +} +@pitfalls{ +* Box2text does not have highlighting features anymore; you can use ((util::Highlight)) for this instead. +} +module lang::box::util::Box2Text + +import util::Math; +import List; +import String; +import lang::box::\syntax::Box; +import IO; + +@synopsis{formatting options for ((Box2Text))} +@description{ + * `maxWidth` is the constraint that makes HV and HOV boxes switch to vertical mode (origin: ((Box2Text)) + * `wrapAfter` is the lowerbound that makes HV and HOV stay horizontal (origin ((Box2Text))) + * `tabSize` is the default indentation used when an `I` box does not have an explicit `is` parameter (origin: ((util::LanguageServer))) + * `insertSpaces`, when set to true it prefers spaces over tabs, when set to false we use tabs for indentation (see `tabSize`) + * `trimTrailingWhitespace` when `true` the formatter can not leave spaces or tabs after the last non-whitespace character, + when false it does not matter. (origin: ((util::LanguageServer))) + * `insertFinalNewline`, insert a newline character at the end of the file if one does not exist. (origin: ((util::LanguageServer))) + * `trimFinalNewlines`, trim all newlines after the final newline at the end of the file. (origin: ((util::LanguageServer)) + +Note that there may be more FormattingOptions due to other elements of a formatting pipeline, such as ((layoutDiff)). +} +data FormattingOptions (int maxWidth = 80, + int wrapAfter = 70, + int tabSize = 4, + bool insertSpaces = true, + bool trimTrailingWhitespace = true, + bool insertFinalNewline = true, + bool trimFinalNewlines = true) = formattingOptions(); + +@synopsis{Specialized alignments for the final column to implement `trimTrailingWhitespace`} +private data Alignment + = fl() + | fc() + ; + +@synopsis{Converts boxes into a string by finding an "optimal" two-dimensional layout} +@description{ +* This algorithm never changes the left-to-right order of the Boxes constituents, such that +syntactical correctness is maintained +* This algorithm tries to never over-run the `maxWidth` parameter, but if it must to maintain +text order, and the specified nesting of boxes, it will anyway. For example, if a table column doesn't +fit it will still be printed. We say `maxWidth` is a _soft_ constraint. +* Separator options like `i`, `h` and `v` options are _hard_ constraints, they may lead to overriding `maxWidth`. +* H, V and I boxes represent hard constraints too. +* HV and HOV are the soft constraints that allow for better solutions, so use them where you can to allow for +flexible layout that can handle deeply nested expressions and statements. +} +public +str format(Box b, FormattingOptions opts = formattingOptions()) + = finalNewlineOptions( + "<for (line <- box2text(b, opts = opts)) {> + '<}>", + opts.insertFinalNewline, + opts.trimFinalNewlines + ); + +private +str finalNewlineOptions( + str lines, bool insertFinalNewline, bool trimFinalNewlines +) { + if (!insertFinalNewline) { + lines = lines[..-1]; + + if (trimFinalNewlines, /^\s+$/ := lines) { + lines = prefix; + } + } + else + if (trimFinalNewlines, /^\s*$/ := lines) { + lines = "\n"; + } + return lines; +} + +@synopsis{Box2text uses list[str] as intermediate representation of the output during formatting} +@benefits{ +* Helps with fast concatenation +* Allows for measuring (max) width and height of intermediate results very quickly +} +@pitfalls{ +* Because of this representation, box2text does not recognize that unprintable characters have width 0. So, +ANSI escape codes, and characters like \r and \n in `L` boxes _will break_ the accuracy of the algorithm. +} +alias Text = list[str]; + +@synopsis{Converts boxes into list of lines (Unicode)} +public +Text box2text(Box b, FormattingOptions opts = formattingOptions()) + = box2data( + b, + options( + maxWidth = opts.maxWidth, + wrapAfter = opts.wrapAfter, + is = opts.tabSize, + trimTrailingWhitespace = opts.trimTrailingWhitespace, + insertSpaces = opts.insertSpaces + ) + ); + +////////// private functions below implement the intermediate data-structures +////////// and the constraint solver +@synopsis{Configuration options for a single formatting run.} +@description{ +This is used during the algorithm, not for external usage. + +* `hs` is the current separation between every horizontal element in H, HV and HOV boxes +* `vs` is the current separation between vertical elements in V, HV and HOV boxes +* `is` is the default (additional) indentation for indented boxes +* `maxWidth` is the number of columns (characters) of a single line on screen or on paper +* `wrapAfter` is the threshold criterion for line fullness, to go to the next line in a HV box and to switching +between horizontal and vertical for HOV boxes. +} +private data Options + = options( + int hs = 1, + int vs = 0, + int is = 4, + int maxWidth = 80, + int wrapAfter = 70, + bool trimTrailingWhitespace = true, + bool insertSpaces = true); + +@synopsis{Quickly splice in any nested U boxes, and empty H, V, HV, I or HOV boxes} +list[Box] u(list[Box] boxes) { + return + [*((U_(list[Box] nested) := b) ? u(nested) : [b]) | b <- boxes, !isDegenerate(b)]; +} + +@synopsis{Empty H, V, HOV, HV, I boxes should not lead to accidental extra separators in their context} +private bool isDegenerate(Box b) = b has boxes && b.boxes == []; + +@synopsis{simple vertical concatenation (every list element is a line)} +private Text vv(Text a, Text b) = [*a, *b]; + +@synopsis{Create a string of spaces just as wide as the parameter a} +private str blank(str a, Options opts) = hskip(size(a), opts)[0]; + +@synopsis{Computes a white line with the length of the last line of a} +Text wd([], Options _) = []; +Text wd([*_, str x], Options opts) = [blank(x, opts)]; + +@synopsis{Computes the maximum width of text t} +private int twidth([]) = 0; +private default int twidth(Text t) = max([size(line) | line <- t]); + +@synopsis{Computes the length of the last line of t} +private int hwidth([]) = 0; +private int hwidth([*_, str last]) = size(last); + +@synopsis{Prepends str a before text b, all lines of b will be shifted} +private Text bar(str a, [], Options _) = [a]; +private +Text bar(str a, [str bh, *str bt], Options opts) + = vv([""], prepend(blank(a, opts), bt)); + +@synopsis{Produce text consisting of a white line of length n} +Text hskip(int n, Options opts) + = opts.insertSpaces + ? [right("", n)] + : ["<for (_ <- [0..n / opts.is]) {>\t<}><for (_ <- [0..n % opts.is]) {> <}>"]; + +@synopsis{Produces text consisting of n white lines at length 0} +private Text vskip(int n) = ["" | _ <- [0..n]]; + +@synopsis{Prepend Every line in b with `a`} +private Text prepend(str a, Text b) = ["" | line <- b]; + +@synopsis{Implements horizontal concatenation, also for multiple lines} +private Text hh([], Text b, Options _) = b; +private Text hh(Text a, [], Options _) = a; +private Text hh([a], Text b, Options opts) = bar(a, b, opts); + +private +default Text hh(Text a, Text b, Options opts) + = vv(a[0..-1], bar(a[-1], b, opts)); + +@synopsis{Horizontal concatenation, but if the left text is empty return nothing.} +private Text lhh([], Text _, Options _) = []; +private default Text lhh(a, b, Options opts) = hh(a, b, opts); + +@synopsis{Horizontal concatenation, but if the right text is empty return nothing.} +private Text rhh(Text _, [], Options _) = []; +private Text rhh(Text a, Text b, Options opts) = hh(a, b, opts); + +@synopsis{Vertical concatenation, but if the right text is empty return nothing.} +private Text rvv(Text _, []) = []; +private default Text rvv(Text a, Text b) = vv(a, b); + +private Text LL(str s) { + assert + s != "" + : "literal strings must never be empty for Box2Text to work correctly."; + return [s]; +} + +private Text HH([], Box _, Options _opts, int _m) = []; + +private Text HH(list[Box] b: [_, *_], Box _, Options opts, int m) { + Text r = []; + b = reverse(b); + for (a <- b) { + Text t = \continue(a, H([]), opts, m); + int s = hwidth(t); + r = hh(t, rhh(hskip(opts.hs, opts), r, opts), opts); + m = m - s - opts.hs; + } + + return r; +} + +private +Text GG( + list[Box] boxes, Box c, Options opts, int m, int gs, Box op, bool backwards +) + = \continue(c[boxes = groupBy(boxes, gs, op, backwards)], c, opts, m); + +public +list[Box] groupBy(list[Box] boxes, int gs, Box op, false) + = groupBy(boxes, gs, op); + +@synopsis{simulates grouping as-if done from the back, by starting to peel off the rest instead of grouping the rest at the end} +public +list[Box] groupBy(list[Box] boxes, int gs, Box op, true) + = [ + op[boxes = boxes[..size(boxes) mod gs]], + *groupBy(boxes[size(boxes) mod gs..], gs, op) + ]; + +public list[Box] groupBy([], int _gs, Box _op) = []; + +public +list[Box] groupBy(list[Box] boxes: [Box _, *_], int gs, Box op) + = [op[boxes = boxes[..gs]], *groupBy(boxes[gs..], gs, op)]; + +private Text VV([], Box _c, Options _opts, int _m) = []; + +private Text VV(list[Box] b: [_, *_], Box c, Options opts, int m) { + Text r = []; + b = reverse(b); + for (a <- b) { + if (V_(_) !:= c || L("") !:= a) { + Text t = \continue(a, V([]), opts, m); + r = vv(t, rvv(vskip(opts.vs), r)); + } + } + return r; +} + +private Text II([], Box _c, Options _opts, int _m) = []; + +private +Text II(list[Box] b: [_, *_], c: H_(list[Box] _), Options opts, int m) + = HH(b, c, opts, m); + +private +Text II(list[Box] b: [Box _, *Box _], c: V_(list[Box] _), Options opts, int m) + = rhh( + hskip(opts.is, opts), \continue(V(b, vs = opts.vs), c, opts, m - opts.is), opts + ); + +private Text WDWD([], Box _c, Options _opts, int _m) = []; + +private Text WDWD([Box head, *Box tail], Box c, Options opts, int m) { + int h = head.hs ? opts.hs; + Text t = \continue(head, c, opts, m); + int s = hwidth(t); + return + hh(wd(t, opts), rhh(hskip(h, opts), WDWD(tail, c, opts, m - s - h), opts), opts); +} + +private Text ifHOV([], Box b, Box c, Options opts, int m) = []; + +private +Text ifHOV(Text t: [str head], Box b, Box c, Options opts, int m) + = size(head) <= m ? t : \continue(b, c, opts, m); + +private +Text ifHOV(Text t: [str head, str _, *str_], Box b, Box c, Options opts, int m) + = \continue(b, c, opts, m); + +private +Text HOVHOV(list[Box] b, Box c, Options opts, int m) + = ifHOV(HH(b, c, opts, m), V(b), c, opts, m); + +/* Gets complicated HVHV */private Text HVHV(Text T, int s, Text a, Box A, list[Box] B, Options opts, int m) { + int h = opts.hs; + int v = opts.vs; + int i = opts.is; + int n = h + hwidth(a); + + if (size(a) > 1) { + // Multiple lines + Text T1 = \continue(A, V([]), opts, m - i); + return vv(T, rvv(vskip(v), HVHV(T1, m - hwidth(T1), B, opts, m, H_([])))); + } + if (n <= s) { + // Box A fits in current line + return HVHV(hh(lhh(T, hskip(h, opts), opts), a, opts), s - n, B, opts, m, H_([])); + } + else { + n -= h; + // n == size(a) + if (i + n < m) { + // Fits in the next line, not in current line + Text T1 = \continue(A, V([]), opts, m - i); + return vv(T, rvv(vskip(v), HVHV(T1, m - n - i, B, opts, m, H_([])))); + } + else { + // Doesn't fit in either lines + Text T1 = \continue(A, V([]), opts, m - i); + return vv(T, rvv(vskip(v), HVHV(T1, m - hwidth(T1), B, opts, m, H_([])))); + } + } +} + +private Text HVHV(Text T, int _s, [], Options _opts, int _m, Box _c) = T; + +private Text HVHV(Text T, int s, [Box head, *Box tail], Options opts, int m, Box c) { + Text T1 = \continue(head, c, opts, s); + return HVHV(T, s, T1, head, tail, opts, m); +} + +private Text HVHV([], Box _, Options opts, int m) = []; + +private +Text HVHV(list[Box] b: [Box head], Box _, Options opts, int m) + = \continue(head, V_([]), opts, m); + +private +Text HVHV(list[Box] b: [Box head, Box next, *Box tail], Box _, Options opts, int m) { + Text T = \continue(head, V_([]), opts, m); + return HVHV(T, m - hwidth(T), [next, *tail], opts, m, H_([])); +} + +private Text continueWith(Box b: L(str s), Box c, Options opts, int m) = LL(s); +private +Text continueWith(Box b: H_(list[Box] bl), Box c, Options opts, int m) + = HH(u(bl), c, opts, m); +private +Text continueWith(Box b: V_(list[Box] bl), Box c, Options opts, int m) + = VV(u(bl), c, opts, m); +private +Text continueWith(Box b: I_(list[Box] bl), Box c, Options opts, int m) + = II(u(bl), c, opts, m); +private +Text continueWith(Box b: WD_(list[Box] bl), Box c, Options opts, int m) + = WDWD(u(bl), c, opts, m); +private +Text continueWith(Box b: HOV_(list[Box] bl), Box c, Options opts, int m) + = HOVHOV(u(bl), c, opts, m); +private +Text continueWith(Box b: HV_(list[Box] bl), Box c, Options opts, int m) + = HVHV(u(bl), c, opts, m); +private +Text continueWith(Box b: SPACE(int n), Box c, Options opts, int m) + = hskip(n, opts); + +// This is a degenerate case, an outermost U-Box without a wrapper around it. +private +Text continueWith(Box b: U_(list[Box] bl), Box c, Options opts, int m) + = HH(u(bl), c, opts, m); + +private +Text continueWith(Box b: G_(list[Box] bl), Box c, Options opts, int m) + = GG(u(bl), c, opts, m, b.gs, b.op, b.backwards); + +private +Text continueWith(Box b: A_(list[Row] rows), Box c, Options opts, int m) + = AA(rows, c, b.columns, b.rs, opts, m); + +private +Text continueWith(Box b: AG_(list[Box] boxes), Box c, Options opts, int m) + = AAG(u(boxes), b.gs, b.columns, b.rs, c, opts, m); + +@synopsis{Option inheritance layer; then continue with the next box.} +@description{ +The next box is either configured by itself. Options are transferred from the +box to the opts parameter for easy passing on to recursive calls. +} +private +Text \continue(Box b, Box c, Options opts, int m) + = continueWith(b, c, opts[hs = b.hs][vs = b.vs][is = (b.is?) ? b.is : opts.is], m); + +/* ------------------------------- Alignment ------------------------------------------------------------*/@synopsis{This is to store the result of the first pass of the algorithm over all the cells in an array/table} +data Box(int width = 0, int height = 1); + +@synopsis{Completely layout a box and then measure its width and height, and annotate the result into the Box} +private Box boxSize(Box b, Box c, Options opts, int m) { + Text s = \continue(b, c, opts, m); + b.width = twidth(s); + b.height = size(s); + return b; +} + +private list[list[Box]] RR(list[Row] bl, Box c, Options opts, int m) { + list[list[Box]] g = [b | R(list[Box] b) <- bl]; + return [[boxSize(z, c, opts, m) | Box z <- b] | list[Box] b <- g]; +} + +@synopsis{Compute the maximum number of columns of the rows in a table} +private +int Acolumns(list[Row] rows) + = ( 0 | max(it, size(row.cells)) | row <- rows ); + +@synopsis{Compute the maximum cell width for each column in an array} +private +list[int] Awidth(list[list[Box]] rows) + = [( 0 | max(it, row[col].width) | row <- rows, col < size(row) ) + | int col <- [0..size(head(rows))]]; + +@synopsis{Adds empty cells to every row until every row has the same amount of columns.} +list[Row] AcompleteRows( + list[Row] rows, + int columns = Acolumns(rows), + Box rs = NULL() +) + = [R( + u([ + *row.cells[..-1], + H_([ row.cells[-1], rs ], hs = 0 ), + *[SPACE(1) | _ <- [0..columns - size(row.cells)]] + ]) + ) | row <- rows[..-1]] + + [ + R( + u([*rows[-1].cells, *[SPACE(1) | _ <- [0..columns - size(rows[-1].cells)]]]) + ) + ]; + +@synopsis{Helper function for aligning Text inside an array cell} +private +Box align(l(), Box cell, int maxWidth) + = maxWidth - cell.width > 0 ? H_([ cell, SPACE(maxWidth - cell.width) ], hs = 0 ) : cell; + +@synopsis{Helper function for aligning Text inside an array cell} +private +Box align(r(), Box cell, int maxWidth) + = maxWidth - cell.width > 0 ? H_([ SPACE(maxWidth - cell.width), cell ], hs = 0 ) : cell; + +private +Box align(c(), Box cell, int maxWidth) + = maxWidth - cell.width > 1 + ? H_([ + SPACE((maxWidth - cell.width) / 2), cell, SPACE((maxWidth - cell.width) / 2) + ], + hs = 0 + ) + : maxWidth - cell.width == 1 ? align(l(), cell, maxWidth) : cell; + +// the last left box should not fill up to the right for the next non-existing column, to help implement `trimTrailingWhitespace` +private Box align(fl(), Box cell, int maxWidth) = cell; + +// the last center box should not fill up to the right, only to the left to help implement `trimTrailingWhitespace` +private +Box align(fc(), Box cell, int maxWidth) + = maxWidth - cell.width > 1 + ? H_([ SPACE((maxWidth - cell.width) / 2), cell ], hs = 0 ) + : maxWidth - cell.width == 1 ? align(l(), cell, maxWidth) : cell; + +private +Text AA( + list[Row] table, Box c, list[Alignment] alignments, Box rs, Options opts, int m +) { + if (table == []) { + return []; + } + // first flatten any nested U cell lists into the Rows + table = [R(u(r.cells)) | Row r <- table]; + + // we remove any H-V backtracking because table cells are too small anyway, generally. + // so we prefer the less wide V over HOV and HV. This boosts efficiency radically, because + // later, ever cell will be formatted individually to an optimal width, and measured, before we even start + // to format the table. Then the same cells will be formatted again from scratch. By removing the + // backtracking, larger tables (like reified grammars) become doable. + table = visit(table) { + case Box b: HOV_(list[Box] boxes) => V_(boxes, vs = b.vs) + case Box b: HV_(list[Box] boxes) => V_(boxes, vs = b.vs) + } + + // then we can know the number of columns + int maxColumns = Acolumns(table); + + // then we fill each row up to the maximum of columns + list[list[Box]] rows + = RR(AcompleteRows(table, columns = maxColumns, rs = rs), c, opts, m); + + // and we infer alignments where not provided + alignments = AcompleteAlignments(alignments, maxColumns); + + if (opts.trimTrailingWhitespace) { + alignments = AfinalColumnSpecials(alignments); + } + // finally we compute alignment information + list[int] maxWidths = Awidth(rows); + + try { + // A row is simply an H box where each cell is filled with enough spaces to align for the next column + return + \continue( + V_( + [H_([align(al, cell, mw) | <- zip3(row, alignments, maxWidths)]) + | row <- rows] + ), + c, + opts, + m + ); + } + catch IllegalArgument(_, "List size mismatch"): { + throw IllegalArgument( + "Array alignments size is while there are columns." + ); + } +} + +private +Text AAG( + [], int _gs, list[Alignment] _columns, Box _rs, Box _c, Options _opts, int _m +) + = []; + +private +Text AAG( + list[Box] boxes: [Box _, *_], int gs, list[Alignment] columns, Box rs, + Box c, Options opts, int m +) + = \continue(A(groupRows(boxes, gs), columns = columns, rs = rs), c, opts, m); + +private list[Row] groupRows([], int _gs) = []; + +private +list[Row] groupRows(list[Box] boxes: [Box _, *_], int gs) + = [R(boxes[..gs]), *groupRows(boxes[gs..], gs)]; + +@synopsis{Cuts off and extends the alignment spec to the width of the table} +@description{ +* if too few columns are specified: `l()`'s are added accordingly +* if too many columns are specified: they are cut off from the right +} +private +list[Alignment] AcompleteAlignments( + list[Alignment] alignments, int maxColumns +) + = [ + *alignments[..maxColumns], + *[l() | _ <- [0..maxColumns - size(alignments)]] + ]; + +@synopsis{Translate l() and c() to fl() and fc() for the final columns to help implement `trimTrailingWhitespace`} +private +list[Alignment] AfinalColumnSpecials([*Alignment pre, l()]) + = [*pre, fl()]; +private +list[Alignment] AfinalColumnSpecials([*Alignment pre, c()]) + = [*pre, fc()]; +private +default list[Alignment] AfinalColumnSpecials(list[Alignment] as) + = as; + +@synopsis{Check soft limit for HV and HOV boxes} +// TODO this seems to ignore SPACE boxes? +private +bool noWidthOverflow(list[Box] hv, Options opts) + = ( 0 | it + size(s) | /L(s) := hv ) < opts.wrapAfter; + +@synopsis{Changes all HV boxes that do fit horizontally into hard H boxes.} +private +Box applyHVconstraints(Box b, Options opts) + = innermost visit(b) { + case Box B: HV_(list[Box] boxes,hs = h, is = i, vs = v) + => H_(boxes, hs = h, is = (B.is?) ? i : opts.is, vs = v) + when noWidthOverflow(boxes, opts) + }; + +@synopsis{Changes all HOV boxes that do fit horizontally into hard H boxes, +and the others into hard V boxes.} +private +Box applyHOVconstraints(Box b, Options opts) + = innermost visit(b) { + case Box B: HOV_(list[Box] boxes,hs = h, is = i, vs = v) + => noWidthOverflow(boxes, opts) + ? H_(boxes, hs = h, is = (B.is?) ? i : opts.is, vs = v) + : V_(boxes, hs = h, is = (B.is?) ? i : opts.is, vs = v) + }; + +@synopsis{Workhorse, that first applies hard HV and HOV limits and then starts the general algorithm} +private Text box2data(Box b, Options opts) { + b = applyHVconstraints(b, opts); + b = applyHOVconstraints(b, opts); + return + \continue( + b, + V_([]), + options( + is = opts.is, + insertSpaces = opts.insertSpaces, + trimTrailingWhitespace = opts.trimTrailingWhitespace + ), + opts.maxWidth + ); +} + +///////////////// regression tests //////////////////////////////// +test bool horizontalPlacement2() + = format(H(L("A"), L("B"), L("C"), hs = 2)) == "A B C + '"; + +test bool horizontalPlacement3() + = format(H(L("A"), L("B"), L("C"), hs = 3)) == "A B C + '"; + +test bool horizontalIndentIsNoop1() + = format(H(L("A"), I(L("B")))) == "A B + '"; + +test bool horizontalIndentIsNoop2() + = format(HV(L("A"), I(L("B")))) == "A B + '"; + +test bool horizontalIndentIsNoop3() + = format(HOV(L("A"), I(L("B")))) == "A B + '"; + +test bool emptyBoxesNoExtraSpacing1() + = format(H(L("A"), H(), L("B"))) == "A B + '"; + +test bool emptyBoxesNoExtraSpacing2() + = format(H(L("A"), V(), L("B"))) == "A B + '"; + +test bool emptyBoxesNoExtraSpacing3() + = format(H(L("A"), I(), L("B"))) == "A B + '"; + +test bool emptyBoxesNoExtraSpacing4() + = format(V(L("A"), H(), L("B"))) == "A + 'B + '"; + +test bool emptyBoxesNoExtraSpacing5() + = format(V(L("A"), V(), L("B"))) == "A + 'B + '"; + +test bool verticalPlacement0() + = format(V(L("A"), L("B"), L("C"), vs = 0)) == "A + 'B + 'C + '"; + +test bool verticalPlacement1() + = format(V(L("A"), L("B"), L("C"), vs = 1)) == "A + ' + 'B + ' + 'C + '"; + +test bool verticalIndentation2() + = format(V(L("A"), I(L("B")), L("C"))) == "A + ' B + 'C + '"; + +test bool blockIndent() + = format(V(L("A"), I(V(L("B"), L("C"))), L("D"))) == "A + ' B + ' C + 'D + '"; + +test bool wrappingIgnoreIndent() + = format( + HV(L("A"), I(L("B")), L("C"), hs = 0), + opts = formattingOptions(maxWidth = 2, wrapAfter = 2) + ) == "AB + 'C + '"; + +test bool wrappingWithIndent() + = format( + HV(L("A"), I(L("B")), I(L("C")), hs = 0), + opts = formattingOptions(maxWidth = 2, wrapAfter = 2) + ) == "AB + ' C + '"; + +test bool multiBoxIndentIsVertical() + = format(I(L("A"), L("B"))) == " A + ' B + '"; + +test bool flipping1NoIndent() + = format( + HOV(L("A"), L("B"), L("C"), hs = 0, vs = 0), + opts = formattingOptions(maxWidth = 2, wrapAfter = 2) + ) == "A + 'B + 'C + '"; + +test bool horizontalOfOneVertical() + = format(H(L("A"), V(L("B"), L("C")))) == "A B + ' C + '"; + +test bool stairCase() + = format(H(L("A"), V(L("B"), H(L("C"), V(L("D"), H(L("E"), L("F"))))))) == "A B + ' C D + ' E F + '"; + +test bool simpleTable() + = format( + A( + R([L("1"), L("2"), L("3")]), + R([L("4"), L("5"), L("6")]), + R([L("7"), L("8"), L("9")]) + ) + ) == "1 2 3 + '4 5 6 + '7 8 9 + '"; + +test bool simpleAlignedTable() + = format( + A( + R([L("1"), L("2"), L("3")]), R([L("44"), L("55"), L("66")]), R([L("777"), L("888"), L("999")]), + columns = [l(), c(), r()] + ) + ) == "1 2 3 + '44 55 66 + '777 888 999 + '"; + +test bool simpleAlignedTableDifferentAlignment() + = format( + A( + R([L("1"), L("2"), L("3")]), R([L("44"), L("55"), L("66")]), R([L("777"), L("888"), L("999")]), + columns = [r(), c(), l()] + ) + ) == " 1 2 3 + ' 44 55 66 + '777 888 999 + '"; + +test bool WDtest() { + L1 = H(L("aap"), hs = 0); + L2 = H(WD(L1), L("noot"), hs = 0); + L3 = H(WD(L2), L("mies"), hs = 0); + + return format(V(L1, L2, L3)) == "aap + ' noot + ' mies + '"; +} + +test bool groupByTest() { + lst = [L("") | i <- [0..10]]; + g1 = G(lst, op = H(), gs = 3); + lst2 = [H(L(""), L(""), L("")) | i <- [0,3.. 7]] + [H(L("9"))]; + + return format(V(g1)) == format(V(lst2)); +} + +test bool groupByBackwardsTest() { + lst = [L("") | i <- [0..10]]; + g1 = G(lst, op = H(), gs = 3, backwards = true); + lst2 = [H(L("0"))] + [H(L(""), L(""), L("")) | i <- [1,4.. 10]]; + + return format(V([g1])) == format(V(lst2)); +} + +test bool noDegenerateHVSeparators1() + = format(HV(L("a"), V(), L("b"))) == "a b + '"; + +test bool noDegenerateHVSeparators2() + = format( + HV(L("a"), V(), L("b")), opts = formattingOptions(maxWidth = 1, wrapAfter = 1) + ) == "a + 'b + '"; + +test bool noDegenerateHOVSeparators1() + = format(HOV(L("a"), V(), L("b"))) == "a b + '"; + +test bool noDegenerateHOVSeparators2() + = format( + HOV(L("a"), V(), L("b")), opts = formattingOptions(maxWidth = 1, wrapAfter = 1) + ) == "a + 'b + '"; +### |project://rascal/src/org/rascalmpl/library/lang/box/util/Tree2Box.rsc| +@synopsis{The default formatting rules for _any_ parse tree.} +@description{ +This module is meant to be extended to include rules specific for a language. + +The main goal of this module is to minimize the number of necessary specializations +for any specific programming language. + +This module is a port of the original default formatting rules, implemented in C + ATerm library + APIgen, +of the "Pandora" in The ASF+SDF Meta-Environment, as described +in +> M.G.J. van den Brand, A.T. Kooiker, Jurgen J. Vinju, and N.P. Veerman. A Language Independent Framework for +> Context-sensitive Formatting. In CSMR '06: Proceedings of the Conference on Software Maintenance and +> Reengineering, pages 103-112, Washington, DC, USA, 2006. IEEE Computer Society Press. + +However, due to the more powerful pattern matching available in Rascal, than in C with the ATerm library, +we can specialize for more cases more easily than in the original paper. For example, single and multi-line +comment styles are automatically recognized. + +The current algorithm, not extended, additionally guarantees that no comments are lost as long as their grammar +rules have been tagged with `@category="comment"` or the legacy `@category="Comment"` + +Another new feature is the normalization of case-insensitive literals. By providing ((toUpper)) or ((toLower)) +the mapping algorithm will change every instance of a case-insensitive literal accordingly before translating +it to an L box expression. In case of ((asIs)), the literal will be printed as it occurred in the source code. +} +@examples{ +```rascal-shell +import lang::box::\syntax::Box; +extend lang::box::util::Tree2Box; +// Notice how we used `extend` and not `import`, which will be important in the following. +import lang::pico::\syntax::Main; +// First, let's get an example program text +example = "begin + '%% this is an example Pico program + ' declare + ' a : %inline comment% natural, + ' b : natural; + ' a := a + b; + ' b := a - b; + ' a := a - b + 'end"; +// Now we parse it: +program = [start[Program]] example; +// Then we can convert it to a Box tree: +b = toBox(program); +// Finally, we can format the box tree to get a prettier format: +import lang::box::util::Box2Text; +format(b) +// If you are not happy, then you should produce a specialization: +Box toBox((Program) `begin <{Statement ";"}* body> end`, FormattingOptions opts=formattingOptions()) + = V([ + L("begin"), + I([ + toBox(decls) + ], is=2), + I([ + toBox(body) + ], is=4), + L("end") + ]); +// and we see the result here: +format(toBox(program)); +``` +} +module lang::box::util::Tree2Box + +import ParseTree; +import lang::box::\syntax::Box; +import String; + +@synopsis{Configuration options for toBox} +data FormattingOptions (CaseInsensitivity ci = asIs()) = formattingOptions(); + +@synopsis{Normalization choices for case-insensitive literals.} +data CaseInsensitivity + = toLower() + | toUpper() + | toCapitalized() + | asIs() + ; + +@synopsis{This is the generic default formatter} +@description{ +This generic formatter is to be overridden by someone constructing a formatter tools +for a specific language. The goal is that this `toBox` default rule maps +syntax trees to plausible Box expressions, and that only a minimal amount of specialization +by the user is necessary. +} +default Box toBox(t: appl(Production p, list[Tree] args), FO opts = fo()) { + // the big workhorse switch identifies all kinds of special cases for shapes of + // grammar rules, and accidental instances (emptiness, only whitespace, etc.) + switch() { + // nothing should not produce additional spaces + case <_, []>: + return NULL(); + + // literals are printed as-is + case : { + str yield = ""; + return yield != "" ? L(yield) : NULL(); + } + + // case-insensitive literals are optionally normalized + case : { + str yield = ""; + return yield != "" ? L(ci(opts.ci, "")) : NULL(); + } + + // non-existing content should not generate accidental spaces + case : + return NULL(); + + case : + return U([toBox(present)]); + + // non-separated lists should stick without spacing (probably lexical) + case : + return H([toBox(e, opts = opts) | e <- elements], hs = 0); + + // non-separated lists should stick without spacing (probably lexical) + case : + return H([toBox(e, opts = opts) | e <- elements], hs = 0); + + // comma's are usually for parameters separation. leaving it to + // parent to wrap the box in the right context. + case : + return + U( + [H([ toBox(elements[i], opts = opts), // element + *[L(",") | i + 2 < size(elements)] // separator + ], hs = 0 ) + | int i <- [0,4.. size(elements)]] + ); + + // comma's are usually for parameters separation + case : + return + HV( + [H([ toBox(elements[i], opts = opts), // element + *[L(",") | i + 2 < size(elements)] // separator + ], hs = 0 ) + | int i <- [0,4.. size(elements)]] + ); + + // semi-colons are usually for statement separation + case : + return + V( + [H([ toBox(elements[i], opts = opts), // element + *[L(";") | i + 2 < size(elements)] // separator + ], hs = 0 ) + | int i <- [0,4.. size(elements)]] + ); + + // optional semi-colons also happen often + case : + return + V( + [H([ + toBox(elements[i], opts = opts), + // element + *[toBox(elements[i + 2]) | i + 2 < size(elements)] + // separator + ], + hs = 0 + ) | int i <- [0,4.. size(elements)]] + ); + + case : + return + V( + [H([ toBox(elements[i], opts = opts), // element + *[L(";") | i + 2 < size(elements)] // separator + ], hs = 0 ) + | int i <- [0,4.. size(elements)]] + ); + + // optional semi-colons also happen often + case : + return + V( + [H([ + toBox(elements[i], opts = opts), + // element + *[toBox(elements[i + 2]) | i + 2 < size(elements)] + // separator + ], + hs = 0 + ) | int i <- [0,4.. size(elements)]] + ); + + // now we have any other literal as separator + case : + return + U( + [H([ + toBox(elements[i], opts = opts), + // element + *[toBox(elements[i + 2]) | i + 2 < size(elements)] + // separator + ], + hs = 0 + ) | int i <- [0,4.. size(elements)]] + ); + + case : + return + U( + [H([ + toBox(elements[i], opts = opts), + // element + *[toBox(elements[i + 2]) | i + 2 < size(elements)] + // separator + ], + hs = 0 + ) | int i <- [0,4.. size(elements)]] + ); + + // this is a normal list + case : + return U([toBox(elements[i], opts = opts) | int i <- [0,2.. size(elements)]]); + + // this is likely a lexical + case : + return H([toBox(e, opts = opts) | e <- elements], hs = 0); + + // this is likely a lexical + case : + return H([toBox(e, opts = opts) | e <- elements], hs = 0); + + // this is a normal list + case : + return U([toBox(elements[i], opts = opts) | int i <- [0,2.. size(elements)]]); + + // this is likely a lexical + case : + return H([toBox(e, opts = opts) | e <- elements], hs = 0); + + // this is likely a lexical + case : + return H([toBox(e, opts = opts) | e <- elements], hs = 0); + + // We remove all layout node positions to make the number of children predictable + // Comments can be recovered by `layoutDiff`. By not recursing into layout + // positions `toBox` becomes more than twice as fast. + case : + return NULL(); + + // lexicals are never split in pieces + case : { + str yield = ""; + return yield != "" ? L(yield) : NULL(); + } + + // Now we will deal with a lot of cases for expressions and block-structured statements. + // Those kinds of structures appear again and again as many languages share inspiration + // from their pre-decessors. + // binary operators become flat lists + case : + return U([toBox(elements[0]), L(op), toBox(elements[-1])]); + + // postfix operators stick + case : + return H([toBox(e, opts = opts) | e <- elements], hs = 0); + + // prefix operators stick + case : + return H([toBox(e, opts = opts) | e <- elements], hs = 0); + + // brackets stick + case : + return H(L("("), I(HOV(toBox(elements[2], opts = opts))), L(")"), hs = 0); + + case : + return toBox(single); + + // if the sort name is statement-like and the structure block-like, we go for + // vertical with indentation + // program: "begin" Declarations decls {Statement ";"}* body "end" ; + case : + return + V([ + H([ + *[toBox(p, opts = opts) | Tree p <- elements[0..size(pre)]], + toBox(elements[size(pre)], opts = opts) + ]), + I([V([toBox(e, opts = opts) | Tree e <- elements[size(pre) + 1..-1]])]), + toBox(elements[-1], opts = opts) + ]); + + // this is to simplify the tree structure for efficiency and readability + case : + return toBox(singleton); + } + + return HV([toBox(a, opts = opts) | a <- args]); +} + +@synopsis{For ambiguity clusters an arbitrary choice is made.} +default Box toBox(amb({Tree t, *Tree _}), FO opts = fo()) = toBox(t); + +@synopsis{When we end up here we simply render the unicode codepoint back.} +default Box toBox(c: char(_), FormattingOptions opts = fo()) = L(""); + +@synopsis{Cycles are invisible and zero length} +default Box toBox(cycle(_, _), FO opts = fo()) = NULL(); + +@synopsis{Create a V box of V boxes where the inner boxes are connected and the outer boxes are separated by an empty line.} +@description{ +This function learns from the input trees how vertical clusters were layout in the original tree. +The resulting box maintains the original clustering. +For example, such lists of declarations which are separated by a newline, remain separated after formatting with `toClusterBox` +``` +int a1 = 1; +int a2 = 2; + +int b1 = 3; +int b2 = 4; +``` +} +@benefits{ +* many programmers use vertical clustering, or "grouping statements", to indicate meaning or intent, by not throwing this +away we are not throwing away the documentative value of their grouping efforts. +} +@pitfalls{ +* ((toClusterBox)) is one of the (very) few Box functions that use layout information from the input tree to +influence the layout of the output formatted code. It replaces a call to ((toBox)) for that reason. +* ((toClusterBox)) does not work on separated lists, yet. +} +Box toClusterBox(list[Tree] lst, FO opts = fo()) { + list[Box] cluster([]) = []; + + list[Box] cluster([Tree e]) = [V([ toBox(e) ], vs = 0 )]; + + list[Box] cluster([*Tree pre, Tree last, Tree first, *Tree post]) + = [ + V([ *[toBox(p, opts = opts) | p <- pre], toBox(last, opts = opts) ], vs = 0 ), + *cluster([first, *post]) + ] + when first@\loc.begin.line - last@\loc.end.line > 1; + + default list[Box] cluster(list[Tree] l) + = [V([toBox(e, opts = opts) | e <- l], vs = 0)]; + + return V(cluster(lst), vs = 1); +} + +Box toClusterBox(&T* lst, FO opts = fo()) + = toClusterBox([e | e <- lst], opts = opts); +Box toClusterBox(&T+ lst, FO opts = fo()) + = toClusterBox([e | e <- lst], opts = opts); + +@synopsis{Reusable way of dealing with large binary expression trees} +@description{ +1. the default `toBox` will flatten nested binary expressions to U lists. +2. the G box groups each operator with the following expression on the right hand-side, + * given an initial element (usually L("=") or L(":=")) for the assignment operators +3. the entire list is indented in case the surrounding context needs more space +4. the net result is usually in vertical mode: +``` + = operand1 + + operand2 + + operand3 +``` +or in horizontal mode: +``` += operand1 + operand2 + operand3 +``` + +By default ((toExpBox)) wraps it result in a HOV context, but you can pass +in a different `wrapper` if you like. +} +Box toExpBox(Box prefix, Tree expression, Box wrapper = HOV()) + = wrapper[boxes = [G(prefix, toBox(expression), gs = 2, op = H())]]; + +@synopsis{Reusable way of dealing with large binary expression trees} +@description{ +1. the default `toBox` will flatten nested binary expressions to U lists. +2. the G box groups each operator horizontally with the following expression on the right hand-side. +4. the net result is usually in vertical mode: +``` + operand1 + operand2 + + operand3 +``` +or in horizontal mode: +``` +operand1 + operand2 + operand3 +``` + +By default ((toExpBox)) wraps it result in a HV context, but you can pass +in a different `wrapper` if you like. + +} +Box toExpBox(Tree expression, Box wrapper = HV()) + = wrapper[boxes = [G(toBox(expression), gs = 2, backwards = true, op = H())]]; + +@synopsis{Private type alias for legibility's sake} +private alias FO = FormattingOptions; + +@synopsis{This is a short-hand for legibility's sake} +private FO fo() = formattingOptions(); + +@synopsis{Implements normalization of case-insensitive literals} +private str ci(toLower(), str word) = toLowerCase(word); +private str ci(toUpper(), str word) = toUpperCase(word); +private str ci(toCapitalized(), str word) = capitalize(word); +private str ci(asIs(), str word) = word; + +@synopsis{Removing production labels helps with case distinctions on ((Symbol)) kinds.} +private +Production delabel(prod(Symbol s, list[Symbol] syms, set[Attr] attrs)) + = prod(delabel(s), [delabel(x) | x <- syms], attrs); +private Production delabel(regular(Symbol s)) = regular(delabel(s)); + +@synopsis{Removing symbol labels helps with case distinctions on ((Symbol)) kinds.} +private Symbol delabel(label(_, Symbol s)) = delabel(s); +private Symbol delabel(conditional(Symbol s, _)) = delabel(s); +private default Symbol delabel(Symbol s) = s; +### |project://rascal/src/org/rascalmpl/library/lang/c90/syntax/C.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +module lang::c90::\syntax::C + +syntax Statement + = "{" Declaration* Statement* "}" + | Identifier ":" Statement + | "case" Expression ":" Statement + | "default" ":" Statement + | ";" + | Expression ";" + | "if" "(" Expression ")" Statement + | "if" "(" Expression ")" Statement "else" Statement + | "switch" "(" Expression ")" Statement + | "while" "(" Expression ")" Statement + | "do" Statement "while" "(" Expression ")" ";" + | "for" "(" Expression? ";" Expression? ";" Expression? ")" Statement + | "goto" Identifier ";" + | "continue" ";" + | "break" ";" + | "return" ";" + | "return" Expression ";" + ; + +syntax Expression + = variable: Identifier + | @category="number" HexadecimalConstant + | @category="number" IntegerConstant + | @category="string" CharacterConstant + | @category="number" FloatingPointConstant + | @category="string" StringConstant + | Expression "[" Expression "]" + | Expression "(" {NonCommaExpression ","}* ")" + | "sizeof" "(" TypeName ")" + | bracket \bracket: "(" Expression ")" + | Expression "." Identifier + | Expression "-\>" Identifier + | Expression "++" + | Expression "--" + > [+] !<< "++" Expression + | [\-] !<< "--" Expression + | "&" Expression + | "*" Expression + | "+" Expression + | "-" Expression + | "~" Expression + | "!" Expression + | sizeOfExpression: "sizeof" Expression exp + // May be ambiguous with "sizeof(TypeName)". + | "(" TypeName ")" Expression + > left ( multiplicationExpression: Expression lexp "*" Expression rexp + // May be ambiguous with "TypeName *Declarator". + | Expression "/" Expression + | Expression "%" Expression + ) + > left ( Expression "+" Expression | Expression "-" Expression ) + > left ( Expression "\<\<" Expression | Expression "\>\>" Expression ) + > left ( Expression "\<" Expression + | Expression "\>" Expression + | Expression "\<=" Expression + | Expression "\>=" Expression + ) + > left ( Expression "==" Expression | Expression "!=" Expression ) + > left Expression "&" Expression + > left Expression "^" Expression + > left Expression "|" Expression + > left Expression "&&" Expression + > left Expression "||" Expression + > right Expression "?" Expression ":" Expression + > right ( Expression "=" Expression + | Expression "*=" Expression + | Expression "/=" Expression + | Expression "%=" Expression + | Expression "+=" Expression + | Expression "-=" Expression + | Expression "\<\<=" Expression + | Expression "\>\>=" Expression + | Expression "&=" Expression + | Expression "^=" Expression + | Expression "|=" Expression + ) + > left commaExpression: Expression "," Expression + ; + +syntax NonCommaExpression = nonCommaExpression: Expression expr; + +lexical Identifier = ([a-zA-Z_] [a-zA-Z0-9_]* !>> [a-zA-Z0-9_]) \ Keyword; + +syntax AnonymousIdentifier = ; + +keyword Keyword + = "auto" + | "break" + | "case" + | "char" + | "const" + | "continue" + | "default" + | "do" + | "double" + | "else" + | "enum" + | "extern" + | "float" + | "for" + | "goto" + | "if" + | "int" + | "long" + | "register" + | "return" + | "short" + | "signed" + | "sizeof" + | "static" + | "struct" + | "switch" + | "typedef" + | "union" + | "unsigned" + | "void" + | "volatile" + | "while" + ; + +syntax Declaration + = declarationWithInitDecls: Specifier+ specs {InitDeclarator ","}+ initDeclarators ";" + | declarationWithoutInitDecls: Specifier+ specs ";" + // Avoid. + ; + +syntax GlobalDeclaration + = globalDeclarationWithInitDecls: Specifier* specs0 {InitDeclarator ","}+ initDeclarators ";" + | globalDeclarationWithoutInitDecls: Specifier+ specs1 ";" + // Avoid. + ; + +syntax InitDeclarator + = Declarator decl + | Declarator decl "=" Initializer + ; + +syntax Specifier + = storageClass: StorageClass + | typeSpecifier: TypeSpecifier + | typeQualifier: TypeQualifier + ; + +syntax StorageClass + = typeDef: "typedef" + | "extern" + | "static" + | "auto" + | "register" + ; + +syntax TypeSpecifier + = identifier: Identifier + | \void: "void" + | char: "char" + | short: "short" + | \int: "int" + | long: "long" + | \float: "float" + | \double: "double" + | "signed" + | "unsigned" + | struct: "struct" Identifier + | structDecl: "struct" Identifier "{" StructDeclaration* "}" + | structAnonDecl: "struct" "{" StructDeclaration* "}" + | union: "union" Identifier + | unionDecl: "union" Identifier "{" StructDeclaration* "}" + | unionAnonDecl: "union" "{" StructDeclaration* "}" + | enum: "enum" Identifier + | enumDecl: "enum" Identifier "{" {Enumerator ","}+ "}" + | enumAnonDecl: "enum" "{" {Enumerator ","}+ "}" + ; + +syntax TypeQualifier + = "const" + | "volatile" + ; + +syntax StructDeclaration + = structDeclWithDecl: Specifier+ specs {StructDeclarator ","}+ ";" + // TODO: Disallow store class specifiers. + | structDeclWithoutDecl: Specifier+ specs ";" + // TODO: Disallow store class specifiers. Avoid. + ; + +syntax StructDeclarator + = Declarator + | Declarator? ":" Expression + // Prefer the one where 'Declarator' is filled. + ; + +syntax Parameters + = {Parameter ","}+ MoreParameters? + | "void" + ; + +syntax MoreParameters = "," "..."; + +syntax Parameter = Specifier* Declarator; + +syntax PrototypeParameter = Specifier* AbstractDeclarator; + +syntax PrototypeParameters + = {PrototypeParameter ","}+ MoreParameters? + | "void" + ; + +syntax Initializer + = NonCommaExpression + | "{" {Initializer ","}+ ","? "}" + ; + +syntax TypeName = Specifier+ AbstractDeclarator; + +syntax Enumerator + = Identifier + | Identifier "=" NonCommaExpression + ; + +syntax AbstractDeclarator + = identifier: AnonymousIdentifier + | bracket \bracket: "(" AbstractDeclarator decl ")" + | arrayDeclarator: AbstractDeclarator decl "[" Expression? exp "]" + | functionDeclarator: AbstractDeclarator decl "(" Parameters? params ")" + > pointerDeclarator: "*" TypeQualifier* qualifiers AbstractDeclarator decl + ; + +syntax PrototypeDeclarator + = identifier: Identifier + | bracket \bracket: "(" AbstractDeclarator abs_decl ")" + | arrayDeclarator: PrototypeDeclarator proto_decl "[" Expression? exp "]" + | functionDeclarator: PrototypeDeclarator proto_decl "(" PrototypeParameters? params ")" + > pointerDeclarator: "*" TypeQualifier* qualifiers PrototypeDeclarator decl + ; + +syntax Declarator + = identifier: Identifier + | bracket \bracket: "(" Declarator decl ")" + | arrayDeclarator: Declarator decl "[" Expression? exp "]" + | functionDeclarator: Declarator decl "(" Parameters? params ")" + > pointerDeclarator: "*" TypeQualifier* qualifiers Declarator decl + ; + +lexical IntegerConstant = [0-9]+ [uUlL]* !>> [0-9]; + +lexical HexadecimalConstant = [0] [xX] [a-fA-F0-9]+ [uUlL]* !>> [a-fA-F0-9]; + +lexical FloatingPointConstant + = [0-9]+ Exponent [fFlL]? + | [0-9]* [.] [0-9]+ !>> [0-9] Exponent? [fFlL]? + | [0-9]+ [.] Exponent? [fFlL]? + ; + +lexical Exponent = [Ee] [+\-]? [0-9]+ !>> [0-9]; + +lexical CharacterConstant = [L]? [\'] CharacterConstantContent+ [\']; + +lexical CharacterConstantContent + = [\\] ![] + | ![\\\'] + ; + +lexical StringConstant = [L]? [\"] StringConstantContent* [\"]; + +lexical StringConstantContent + = [\\] ![] + | ![\\\"] + ; + +// TODO: Type specifiers are required for K&R style parameter declarations, initialization of them is not allowed however. +// TODO: Disallow storage class specifiers as specifiers. +// TODO: Disallow ArrayDeclarators in the declarator. +syntax FunctionDefinition = defaultFunctionDefinition: Specifier* specs Declarator Declaration* "{" Declaration* Statement* "}"; + +syntax FunctionPrototype = defaultFunctionPrototype: Specifier* specs PrototypeDeclarator decl ";"; + +syntax ExternalDeclaration + = FunctionDefinition + | FunctionPrototype + | GlobalDeclaration + ; + +start syntax TranslationUnit = ExternalDeclaration+; + +lexical Comment + = [/] [*] MultiLineCommentBodyToken* [*] [/] + | "//" ![\n]* [\n] + ; + +lexical MultiLineCommentBodyToken + = ![*] + | Asterisk + ; + +lexical Asterisk = [*] !>> [/]; + +layout LAYOUTLIST = LAYOUT* !>> [\ \t\n\r]; + +lexical LAYOUT + = whitespace: [\ \t\n\r] + | @category="comment" comment: Comment + ; +### |project://rascal/src/org/rascalmpl/library/lang/csv/IO.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@synopsis{Functions for reading and writing Comma-Separated Values (CSV) files.} +@description{ +The http://tools.ietf.org/html/rfc4180[CSV format] is used for exchanging +information between spreadsheets and databases. A CSV file has the following structure: + +* An optional header line consisting of field names separated by comma's. +* One or more lines consisting of values separated by comma's. + +The following functions are provided: + +(((TOC))) +} +@examples{ +* CSV file with headers +```csv +field_name1,field_name2,field_name3 +aaa,bbb,ccc +zzz,yyy,xxx +``` +} +module lang::csv::IO + +import lang::csv::\syntax::Parse; +import lang::csv::ast::CSV; +import lang::csv::ast::Implode; +import Map; +import List; + +@synopsis{Read a relation from a CSV (Comma Separated Values) file.} +@description{ +Read a CSV file and return a value of a required type. + +The `result` argument is the required type of the value that is produced by reading the CSV +that is found at `location`. +Optionally, the following arguments can be supplied: + +* `header = true` specifies that a header is present (default). +* `header = false` specifies that no header is present. +* `separator = ","` specifies that `,` is the separator character between fields (default). + +The CSV data should conform to the specified type (if any). + +If the required type is not specified, it is _inferred_ in three steps: + +_Step 1_: The type of each field occurrence is inferred from its contents using the +following rules: + +* An empty value is of type `void`. +* A field that contains a string that corresponds to a number is numeric. +* A field that contains `true` or `false` is of type is `bool`. +* In all other cases the field is of type `str`. + +_Step 2_: The type of each field is inferred from the type of all of its occurrences: + +* If all occurrences have a numeric type, then the smallest possible type is used. +* If the occurrences have a mixed type, i.e., numeric, non-numeric, boolean or string, then the type is `str`. +* If the requested type for a field is `str` and another type would be inferred by the preceding two rules, +its inferred type will be `str`. + +Reading the values in fields is straightforward, except for the case that the text in the field is enclosed between double quotes (`"`): + +* the text may include line breaks which are represented as `\n` in the resulting string value of the field. +* the text may contain escaped double quotes (`""`) which are represented as `\"` in the resulting string value. +} +@examples{ +Given is the following file `ex1.csv`: + +```rascal +((|project://rascal/src/org/rascalmpl/library/lang/csv/examples/ex1.csv|)) +``` + +We can read it in various ways: + +```rascal-shell +import lang::csv::IO; +R1 = readCSV(#rel[int position, str artist, str title, int year], |project://rascal/src/org/rascalmpl/library/lang/csv/examples/ex1.csv|, separator = ";"); +``` +Now we can, for instance, select one of the fields of `R1`: + +```rascal-shell,continue +R1.artist; +``` +It is also possible to infer the type: + +```rascal-shell,continue +R1 = readCSV(|project://rascal/src/org/rascalmpl/library/lang/csv/examples/ex1.csv|, separator = ";"); +``` +} +@javaClass{org.rascalmpl.library.lang.csv.IO} +public +java value readCSV( + loc location, + bool header = true, + str separator = ",", + str encoding = "UTF8" +); + +@deprecated{ +Use readCSV with keyword parameters +} +public value readCSV(loc location, map[str, str] options) { + return + readCSV( + location, + header = ((options["header"] ? "true") == "true"), + separator = options["separator"] ? "," + ); +} + +@javaClass{org.rascalmpl.library.lang.csv.IO} +public +java &T readCSV( + type[&T] result, + loc location, + bool header = true, + str separator = ",", + str encoding = "UTF8" +); + +@javaClass{org.rascalmpl.library.lang.csv.IO} +public +java type[value] getCSVType( + loc location, + bool header = true, + str separator = ",", + str encoding = "UTF8" +); + +@synopsis{Write a relation to a CSV (Comma Separated Values) file.} +@description{ +Write `relation` to a CSV file at `location`. +The options influence the way the actrual CSV file is written: + +* `header`: add or omit a header (based on the labels of the relation). +* `separator`: defines the separator character between fields (default is `,`). +} +@examples{ +```rascal-shell +import lang::csv::IO; +rel[int position, str artist, str title, int year] R1 = { + <1,"Eagles","Hotel California",1977>, + <2,"Queen","Bohemian rhapsody",1975>, + <3,"Boudewijn de Groot","Avond",1997> +}; +// we can write the CSV with a header row: +writeCSV(#rel[int position, str artist, str title, int year], R1, |tmp:///ex1a.csv|); +// or write it without the header row: +writeCSV(#rel[int, str, str, int], R1, |tmp:///ex1b.csv|, header = false, separator = ";"); +``` + +The result of both calls to writeCSV are included below: + +`ex1a.csv` (with a header line and default separator `,`): +```rascal +((|tmp:///ex1a.csv|)) +``` + +`ex1b.csv` (without a header line with separator `;`): +```rascal +((|tmp:///ex1b.csv|)) +``` +} +@javaClass{org.rascalmpl.library.lang.csv.IO} +public +java void writeCSV( + type[&T] schema, + &T relation, + loc location, + bool header = true, + str separator = ",", + str encoding = "UTF8" +); + +public +lang::csv::ast::CSV::Table loadCSV(loc l) + = implodeCSV(parseCSV(l)); + +public +lang::csv::ast::CSV::Table loadNormalizedCSV(loc l) + = unquote(loadCSV(l)); + +@synopsis{Generator for CSV resources} +@resource{ +csv +} +public str generate(str moduleName, loc uri) { + map[str, str] options = uri.params; + + // We can pass the name of the function to generate. If we did, grab it then remove + // it from the params. + str funname = "resourceValue"; + if ("funname" in options) { + funname = options["funname"]; + options = domainX(options, {"funname"}); + } + type[value] csvType + = getCSVType( + uri, + header = ((options["header"] ? "true") == "true"), + separator = options["separator"] ? "," + ); + + optionParams = []; + if ("header" in options) { + optionParams = optionParams + "header="; + } + if ("separator" in options) { + optionParams = optionParams + "separator=\"\""; + } + if ("encoding" in options) { + optionParams = optionParams + "encoding=\"\""; + } + mbody + = "module + 'import lang::csv::IO; + ' + 'alias Type = ; + ' + 'public Type () { + ' return readCSV(#, <if (size(optionParams) > 0) {>, <}>); + '} + '"; + + return mbody; +} +### |project://rascal/src/org/rascalmpl/library/lang/csv/ast/CSV.rsc| +module lang::csv::ast::CSV + +import String; + +data Table (loc src = |unknown:///|) = table(list[Record] records); + +data Record (loc src = |unknown:///|) = record(list[Field] fields); + +data Field (loc src = |unknown:///|) + = unquoted(str text) + | quoted(str text) + ; + +public Table unquote(Table tbl) { + str unescape(str s) + = replaceAll( + replaceAll(replaceAll(s, "\\n", "\n"), "\\t", "\t"), "\"\"", "\"" + ); + + return + visit(tbl) { + case quoted(txt) => unquoted(unescape(substring(txt, 1, size(txt) - 1))) + } +} +### |project://rascal/src/org/rascalmpl/library/lang/csv/ast/Implode.rsc| +module lang::csv::ast::Implode + +import lang::csv::\syntax::CSV; +import lang::csv::ast::CSV; +import ParseTree; + +public +lang::csv::ast::CSV::Table implodeCSV( + lang::csv::\syntax::CSV::Table tbl +) + = implode(#lang::csv::ast::CSV::Table, tbl); +### |project://rascal/src/org/rascalmpl/library/lang/csv/syntax/CSV.rsc| +module lang::csv::\syntax::CSV + +start syntax Table = table: {Record EOL}*; + +syntax Record // todo: $ does match single \r + = record: {Field ","}+; + +syntax Field + = unquoted: UQStr + | quoted: QStr + ; + +lexical UQStr + = ![\n\r\",] ![\n\r,]* !>> ![\n\r,] + | + /* empty */ ; + +lexical QStr = [\"] QChar* [\"] !>> ![,\n\r]; + +lexical QChar + = ![\"\n\r] + | [\"] [\"] + ; + +lexical EOL + = [\n] + | [\r] [\n] + | [\r] !>> [\n] + ; +### |project://rascal/src/org/rascalmpl/library/lang/csv/syntax/Parse.rsc| +module lang::csv::\syntax::Parse + +import lang::csv::\syntax::CSV; +import ParseTree; + +public lang::csv::\syntax::CSV::Table parseCSV(loc l) = parse(#Table, l); +### |project://rascal/src/org/rascalmpl/library/lang/diff/unified/UnifiedDiff.rsc| +@synopsis{Syntax definition for Unified Diff format (see )} +@contributor{Tijs van der Storm - storm@cwi.nl (CWI)} +module lang::diff::unified::UnifiedDiff + +start syntax DiffFile = Diff; + +syntax Diff = Header old Header new Chunk* chunks; + +syntax Chunk = ChunkStart Line+; + +syntax ChunkStart = ^"@@ -" Range " +" Range " @@" Content; + +syntax Header = ^Indicator " " FileName DateTime? "\n"; + +syntax DateTime = "\t" Date date " " Time time " " TimeZone timeZone; + +lexical FileName = ![\t\n]*; + +syntax Indicator + = old: "---" + | new: "+++" + ; + +syntax Date = Year year "-" Month month "-" Day day; + +syntax Time = Hours hours ":" Minutes minutes ":" Seconds seconds [0-9] [0-9] [0-9] [0-9] [0-9] [0-9] [0-9] [0-9] [0-9]; + +lexical Year = [0-9] [0-9] [0-9] [0-9]; +lexical Month = [0-1] [0-9]; +lexical Day = [0-1] [0-9]; +lexical Hours = [0-2] [0-9]; +lexical Minutes = [0-6] [0-9]; +lexical Seconds = [0-6] [0-9]; + +syntax TimeZone = Sign sign Hours hours Minutes minutes; + +lexical Sign = [+\-]; + +syntax Range + = Decimal begin + // chunksize = 1 + | Decimal begin "," Decimal size + ; + +lexical Decimal = [0-9]+ !>> [0-9]; + +syntax Line + = common: ^" " Content content + | onlyOld: ^"-" Content content + | onlyNew: ^"+" Content content + | noNewLine: ^"\\ No newline at end of file\n" + ; + +lexical Content = ![\n]* [\n]; +### |project://rascal/src/org/rascalmpl/library/lang/dimacs/syntax/Dimacs.rsc| +@synopsis{Standard format for boolean formulas in conjunctive normal form.} +@description{ +This format is used by the yearly SAT competition, see https://www.satcompetition.org/ . +} +@examples{ +This is a sample `.cnf` file: +```dimacs +c A sample .cnf file. +p cnf 3 2 +1 -3 0 +2 3 -1 0 +``` +} +module lang::dimacs::\syntax::Dimacs + +layout L = [\t\ \r]*; + +lexical Comment = comment: "c" ![\n]* "\n"; +lexical Prologue = prologue: "p" "cnf" Number variables Number clauses ![\n]* "\n"; + +lexical Number + = positive: [0-9]+ !>> [0-9] + | non-assoc negative: "-" Number number + ; + +start syntax Dimacs = Prologue prologue {Line "\n"}+ lines "\n"; + +syntax Line + = disjunct: Disjunct disjunct + | comment: Comment comment + ; + +syntax Disjunct = Number+ numbers; +### |project://rascal/src/org/rascalmpl/library/lang/dot/Dot.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl} +module lang::dot::Dot + +import String; +import Set; +import Map; + +alias Id = str; + +@synopsis{Abstract Data Type of Dot language} +data DotGraph + = graph(Id id, Stms stmts) + | digraph(Id id, Stms stmts) + ; + +alias Stms = list[Stm]; + +alias NodeId = tuple[Id, PortId]; + +alias PortId = tuple[Id, CompassPt]; + +data CompassPt + = N() + | NE() + | E() + | SE() + | S() + | SW() + | W() + | NW() + | C() + | _() + ; + +data Stm + = N(Id id, Attrs attrs) + | N(Id id) + | N(NodeId nid, Attrs attrs) + | N(NodeId nid) + | E(Id from, Id to, Attrs attrs) + | E(Id from, Id to) + | E(NodeId nfrom, Id to, Attrs attrs) + | E(NodeId nfrom, Id to) + | E(Stm sfrom, Id to, Attrs attrs) + | E(Stm sfrom, Id to) + | E(Id from, NodeId nto, Attrs attrs) + | E(Id from, NodeId nto) + | E(NodeId nfrom, NodeId nto, Attrs attrs) + | E(NodeId nfrom, NodeId nto) + | E(Stm sfrom, NodeId nto, Attrs attrs) + | E(Stm sfrom, NodeId nto) + | E(Id from, Stm sto, Attrs attrs) + | E(Id from, Stm sto) + | E(NodeId nfrom, Stm sto, Attrs attrs) + | E(NodeId nfrom, Stm sto) + | E(Stm sfrom, Stm sto, Attrs attrs) + | E(Stm sfrom, Stm sto) + | S(Id id, Stms stms) + | S(Stms stms) + | A(Id prop, Id val) + | GRAPH(Attrs attrs) + | NODE(Attrs attrs) + | EDGE(Attrs attrs) + ; + +alias Attr = tuple[str prop, Id val]; + +alias Attrs = list[Attr]; + +alias Outline = map[int key, list[str] args]; + +alias Dotline = tuple[DotGraph graph, Outline outline]; + +@synopsis{Dummy function call needed to tag initialized global variables of type DotGraph. +It is possible to select that variable on the outline menu of the Rascal Editor. +An application is for example to display dotgraphs.} +DotGraph export(DotGraph g) { + return g; +} + +Dotline export(Dotline g) { + return g; +} + +bool hasOutline(Dotline _) { + return true; +} + +bool hasOutline(DotGraph _) { + return false; +} + +Outline currentOutline ; + +void setCurrentOutline(Dotline current) { + currentOutline = current.outline; +} + +@synopsis{Translates DotGraph to String input for dot} +str toString(digraph(Id id, Stms stms)) + = "digraph {<for (x <- stms) {> + ';<}> + '} + '"; + +str toString(Dotline g) = toString(g.graph); + +list[value] getChildren(value key) { + if (int k := key) { + if (k == -1) + return toList(domain(currentOutline)); + return currentOutline[k]; + } + return []; +} + +private str reLabel(str prop, str val) { + if (prop == "label") { + return "\"\""; + } + return val; +} + +private +str oAttrs(Attrs attrs) + = "[<for (y <- attrs) {> =,<}>]"; + +private str oCompassPt(N()) = "n"; +private str oCompassPt(NE()) = "ne"; +private str oCompassPt(E()) = "e"; +private str oCompassPt(SE()) = "se"; +private str oCompassPt(S()) = "s"; +private str oCompassPt(SW()) = "sw"; +private str oCompassPt(W()) = "w"; +private str oCompassPt(NW()) = "nw"; +private default str oCompassPt(CompassPt _) = "_"; + +str oPortId(PortId id) { + if (isEmpty(id[0])) + return ":"; + return "::"; +} + +str oNodeId(NodeId id) = ""; + +str oStms(Stms stms, str sep) = "<for (y <- stms) {> <}>"; + +str oStm(N(Id id)) = ""; +str oStm(N(Id id, Attrs attrs)) = ""; + +str oStm(E(Id from, Id to)) = "-\>"; +str oStm(E(Id from, Id to, Attrs attrs)) = "-\>"; + +str oStm(E(NodeId from, Id to)) = "-\>"; + +//str oStm( E(Id from, Id to, Attrs attrs)) = "-\>"; +str oStm(E(Stm from, Id to)) = "-\>"; +str oStm(E(Stm from, Id to, Attrs attrs)) + = "-\>"; + +str oStm(E(Id from, NodeId to)) = "-\>"; +str oStm(E(Id from, NodeId to, Attrs attrs)) + = "-\>"; + +str oStm(E(NodeId from, NodeId to)) = "-\>"; + +//str oStm( E(Id from, NodeId to, Attrs attrs)) = "-\>"; +str oStm(E(Stm from, NodeId to)) = "-\>"; +str oStm(E(Stm from, NodeId to, Attrs attrs)) + = "-\>"; + +str oStm(E(Id from, Stm to)) = "-\>"; +str oStm(E(Id from, Stm to, Attrs attrs)) + = "-\>"; + +str oStm(E(NodeId from, Stm to)) = "-\>"; + +//str oStm( E(Id from, Stm to, Attrs attrs)) = "-\>"; +str oStm(E(Stm from, Stm to)) = "-\>"; +str oStm(E(Stm from, Stm to, Attrs attrs)) + = "-\>"; + +str oStm(S(Stms stms)) = "subgraph {} "; +str oStm(S(Id id, Stms stms)) = "subgraph {} "; +str oStm(A(Id prop, Id val)) = " = "; +str oStm(GRAPH(Attrs attrs)) = "graph "; +str oStm(EDGE(Attrs attrs)) = "edge "; +str oStm(NODE(Attrs attrs)) = "node "; +### |project://rascal/src/org/rascalmpl/library/lang/dot/syntax/Dot.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl} +module lang::dot::\syntax::Dot + +start syntax DOT = Graph Id "{" StatementList "}" "\n"?; + +keyword Reserved + = "graph" + | "digraph" + | "node" + | "edge" + | "subgraph" + ; + +syntax Graph + = "graph" + | "digraph" + | AttrTag + ; + +syntax AttrTag + = "node" + | "edge" + | "graph" + ; + +syntax Nod + = NodeId + | Subgraph + ; + +lexical Id + = ([A-Za-z0-9_] !<< [a-zA-Z0-9_] [a-zA-Z0-9_]* !>> [0-9A-Z_a-z]) \ Reserved + | [\"] (![\"] | "\\\"")* [\"] + | [\-]? "." [0-9]+ + | [\-]? [0-9]+ "." [0-9]* + ; + +syntax StatementList = StatementOptional*; +syntax Statement + = NodeStatement + | EdgeStatement + | AttrStatement + | Id "=" Id + ; + +syntax StatementOptional = Statement ";"?; + +syntax NodeStatement = Nod AttrList; + +syntax EdgeStatement = Nod EdgeRhs AttrList; + +syntax Edg = EdgeOp Nod; + +syntax EdgeOp + = "-\>" + | "--" + ; + +syntax EdgeRhs = Edg+; + +syntax NodeId + = Id + | Id Port + ; + +syntax Port = ":" Id (":" Id)?// | ":" Id + // | ":" CompassPt + ; + +// syntax CompassPt = "n" | "ne" | "e" | "se" | "s" | "sw" | "w"| "nw" | "c" |"_"; +syntax AttrList = AttrList0*; + +syntax AttrList0 = "[" DotAttr* "]"; + +syntax DotAttr + = Id "=" Id + | Id "=" Id "," + ; + +syntax AttrStatement = AttrTag AttrList; + +syntax Subgraph = ("subgraph" Id?)? "{" StatementList "}"; + +lexical Comment + = "/*" (![*] | [*] !>> "/")* "*/" + | "//" ![\n]*$ + ; + +layout LAYOUTLIST = LAYOUT* !>> [\ \t\n\r] !>> "//" !>> "/*"; + +lexical LAYOUT + = Whitespace: [\ \t\n\r] + | @category="comment" Comment + ; +### |project://rascal/src/org/rascalmpl/library/lang/html/AST.rsc| +@synopsis{Plain Algebraic Datatype for HTML} +module lang::html::AST + +@synopsis{Abstract Syntax for HTML} +@description{ +This is HTML encoded like so: +* <1> element tags are constructor names of type `HTMLElement` +* <2> all tags have a list of HTMLElement as children, except the `void` tags that do not have any parameters +* <3> text nodes and data nodes (which are invisible in HTML) have the \data or text constructor +* <4> attributes are keyword parameters of type `str` +* <5> unknown tags (such as SVG) are mapped to `unknownElement` nodes and their children are not included. +* <6> the `location` field is reserved for source locations of tags and other content. Normally this would be called `src` but `src` is reserved for other attributes in HTML +} +data HTMLElement (loc location = |unknown:///|) + // <1> + = a(list[HTMLElement] elems) + | abbr(list[HTMLElement] elems) + | address(list[HTMLElement] elems) + | area() + // <2> + | article(list[HTMLElement] elems) + | aside(list[HTMLElement] elems) + | audio(list[HTMLElement] elems) + | b(list[HTMLElement] elems) + | base() + // <2> + | bdi(list[HTMLElement] elems) + | bdo(list[HTMLElement] elems) + | blockquote(list[HTMLElement] elems) + | body(list[HTMLElement] elems) + | br() + // <2> + | button(list[HTMLElement] elems) + | canvas(list[HTMLElement] elems) + | caption(list[HTMLElement] elems) + | cite(list[HTMLElement] elems) + | code(list[HTMLElement] elems) + | col() + | colgroup(list[HTMLElement] elems) + | command() + | \data(str dataContent) + // <3> + | datalist(list[HTMLElement] elems) + | dd(list[HTMLElement] elems) + | del(list[HTMLElement] elems) + | details(list[HTMLElement] elems) + | dfn(list[HTMLElement] elems) + | dialog(list[HTMLElement] elems) + | div(list[HTMLElement] elems) + | dl(list[HTMLElement] elems) + | dt(list[HTMLElement] elems) + | em(list[HTMLElement] elems) + | embed() + | fieldset(list[HTMLElement] elems) + | figcaption(list[HTMLElement] elems) + | figure(list[HTMLElement] elems) + | footer(list[HTMLElement] elems) + | form(list[HTMLElement] elems) + | h1(list[HTMLElement] elems) + | h2(list[HTMLElement] elems) + | h3(list[HTMLElement] elems) + | h4(list[HTMLElement] elems) + | h5(list[HTMLElement] elems) + | h6(list[HTMLElement] elems) + | head(list[HTMLElement] elems) + | header(list[HTMLElement] elems) + | hgroup(list[HTMLElement] elems) + | hr() + | html(list[HTMLElement] elems) + | i(list[HTMLElement] elems) + | iframe(list[HTMLElement] elems) + | img() + | input() + | ins(list[HTMLElement] elems) + | kbd(list[HTMLElement] elems) + | keygen() + | label(list[HTMLElement] elems) + | legend(list[HTMLElement] elems) + | li(list[HTMLElement] elems) + | link() + | main(list[HTMLElement] elems) + | \map(list[HTMLElement] elems) + | mark(list[HTMLElement] elems) + | menu(list[HTMLElement] elems) + | menuitem(list[HTMLElement] elems) + | meta() + | meter(list[HTMLElement] elems) + | nav(list[HTMLElement] elems) + | noscript(list[HTMLElement] elems) + | object(list[HTMLElement] elems) + | ol(list[HTMLElement] elems) + | optgroup(list[HTMLElement] elems) + | option(list[HTMLElement] elems) + | output(list[HTMLElement] elems) + | p(list[HTMLElement] elems) + | param() + | pre(list[HTMLElement] elems) + | progress(list[HTMLElement] elems) + | q(list[HTMLElement] elems) + | rp(list[HTMLElement] elems) + | rt(list[HTMLElement] elems) + | ruby(list[HTMLElement] elems) + | s(list[HTMLElement] elems) + | samp(list[HTMLElement] elems) + | script(list[HTMLElement] elems) + | section(list[HTMLElement] elems) + | select(list[HTMLElement] elems) + | small(list[HTMLElement] elems) + | source() + | span(list[HTMLElement] elems) + | strong(list[HTMLElement] elems) + | style(list[HTMLElement] elems) + | sub(list[HTMLElement] elems) + | summary(list[HTMLElement] elems) + | sup(list[HTMLElement] elems) + | table(list[HTMLElement] elems) + | tbody(list[HTMLElement] elems) + | td(list[HTMLElement] elems) + | template(list[HTMLElement] elems) + | text(str contents) + // <3> + | textarea(list[HTMLElement] elems) + | tfoot(list[HTMLElement] elems) + | th(list[HTMLElement] elems) + | thead(list[HTMLElement] elems) + | time(list[HTMLElement] elems) + | title(list[HTMLElement] elems) + | tr(list[HTMLElement] elems) + | track() + | u(list[HTMLElement] elems) + | ul(list[HTMLElement] elems) + | unknownElement(list[HTMLElement] elems) + // <5> + | var(list[HTMLElement] elems) + | video(list[HTMLElement] elems) + | wbr() + ; + +data HTMLElement(// <4> + str abbr = "", + str about = "", + str accept = "", + str accesskey = "", + str action = "", + str align = "", + str allowfullscreen = "", + str alt = "", + str aria = "", + str async = "", + str autocomplete = "", + str autofocus = "", + str autoplay = "", + str border = "", + str challenge = "", + str char = "", + str charset = "", + str checked = "", + str cite = "", + str class = "", + str cols = "", + str colspan = "", + str command = "", + str content = "", + str contenteditable = "", + str contextmenu = "", + str controls = "", + str coords = "", + str \data = "", + str datatype = "", + str \datetime = "", + str \default = "", + str defer = "", + str dir = "", + str dirname = "", + str disabled = "", + str download = "", + str draggable = "", + str dropzone = "", + str enctype = "", + str \for = "", + str form = "", + str formaction = "", + str formenctype = "", + str formmethod = "", + str formnovalidate = "", + str formtarget = "", + str headers = "", + str height = "", + str hidden = "", + str high = "", + str href = "", + str hreflang = "", + str http = "", + str icon = "", + str id = "", + str inlist = "", + str ismap = "", + str itemid = "", + str itemprop = "", + str itemref = "", + str itemscope = "", + str itemtype = "", + str keytype = "", + str kind = "", + str label = "", + str lang = "", + str language = "", + str \list = "", + str local_ = "", + str loop = "", + str low = "", + str manifest = "", + str max = "", + str maxlength = "", + str media = "", + str mediagroup = "", + str method = "", + str min = "", + str multiple = "", + str muted = "", + str name = "", + str novalidate = "", + str onabort = "", + str onafterprint = "", + str onbeforeprint = "", + str onbeforeunload = "", + str onblur = "", + str oncanplay = "", + str oncanplaythrough = "", + str onchange = "", + str onclick = "", + str oncontextmenu = "", + str ondblclick = "", + str ondrag = "", + str ondragend = "", + str ondragenter = "", + str ondragleave = "", + str ondragover = "", + str ondragstart = "", + str ondrop = "", + str ondurationchange = "", + str onemptied = "", + str onended = "", + str onerror = "", + str onfocus = "", + str onformchange = "", + str onforminput = "", + str onhashchange = "", + str oninput = "", + str oninvalid = "", + str onkeydown = "", + str onkeypress = "", + str onkeyup = "", + str onload = "", + str onloadeddata = "", + str onloadedmetadata = "", + str onloadstart = "", + str onmessage = "", + str onmousedown = "", + str onmousemove = "", + str onmouseout = "", + str onmouseover = "", + str onmouseup = "", + str onmousewheel = "", + str onoffline = "", + str ononline = "", + str onpagehide = "", + str onpageshow = "", + str onpause = "", + str onplay = "", + str onplaying = "", + str onpopstate = "", + str onprogress = "", + str onratechange = "", + str onredo = "", + str onreset = "", + str onresize = "", + str onscroll = "", + str onseeked = "", + str onseeking = "", + str onselect = "", + str onshow = "", + str onstalled = "", + str onstorage = "", + str onsubmit = "", + str onsuspend = "", + str ontimeupdate = "", + str onundo = "", + str onunload = "", + str onvolumechange = "", + str onwaiting = "", + str open = "", + str optimum = "", + str pattern = "", + str ping = "", + str placeholder = "", + str poster = "", + str prefix = "", + str preload = "", + str property = "", + str radiogroup = "", + str readonly = "", + str \rel = "", + str required = "", + str resource = "", + str rev = "", + str reversed = "", + str role = "", + str rows = "", + str rowspan = "", + str sandbox = "", + str scope = "", + str scoped = "", + str seamless = "", + str selected = "", + str shape = "", + str size = "", + str sizes = "", + str span = "", + str spellcheck = "", + str src = "", + str srcdoc = "", + str srclang = "", + str \start = "", + str step = "", + str style = "", + str tabindex = "", + str target = "", + str template = "", + str title = "", + str translate = "", + str \type = "", + str typeof = "", + str usemap = "", + str valign = "", + str \value = "", + str vocab = "", + str width = "", + str wrap = "", + str xml_base = "", + str xml_id = "", + str xml_lang = "", + str xml_space = ""); +### |project://rascal/src/org/rascalmpl/library/lang/html/IO.rsc| +@license{ + Copyright (c) 2009-2022 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@synopsis{Provides read and write functionality for HTMLElements defined in ((lang::html::AST))} +module lang::html::IO + +extend lang::html::AST; +import Content; + +data HTMLEscapeMode + = baseMode() + | extendedMode() + | xhtmlMode() + ; + +data HTMLSyntax + = htmlSyntax() + | xmlSyntax() + ; + +@javaClass{org.rascalmpl.library.lang.html.IO} +@synopsis{Parse a HTML file and return an HTMLElement AST} +@description{ +This function uses [JSoup's](http://www.jsoup.org) HTML parser which is robust +against errors in the HTML, and complete in the sense that it supports all of HTML. +} +java HTMLElement readHTMLFile( + loc file, + loc base = file, + bool trackOrigins = false, + bool includeEndTags = true +); + +@synopsis{Parse a HTML string and return an HTMLElement AST} +@description{ +This function uses [JSoup's](http://www.jsoup.org) HTML parser which is robust +against errors in the HTML, and complete in the sense that it supports all of HTML. +} +@javaClass{org.rascalmpl.library.lang.html.IO} +java HTMLElement readHTMLString( + str content, + loc base = |http://localhost|, + bool trackOrigins = false, + bool includeEndTags = true, + loc src = |unknown:///| +); + +@javaClass{org.rascalmpl.library.lang.html.IO} +@synopsis{Pretty-print the HTMLElement AST to a string} +@description{ +This function uses [JSoup's](http://www.jsoup.org) DOM functionality to +yield a syntactically correct (X)HTML string. +} +java str writeHTMLString( + HTMLElement dom, + str charset = "UTF-8", + HTMLEscapeMode escapeMode = baseMode(), + bool outline = false, + bool prettyPrint = true, + int indentAmount = 4, + int maxPaddingWidth = 30, + HTMLSyntax \syntax = htmlSyntax(), + bool dropOrigins = true +); + +@synopsis{Pretty-print the HTMLElement AST to a string} +@description{ +This function uses [JSoup's](http://www.jsoup.org) DOM functionality to +yield a syntactically correct (X)HTML file. +} +@javaClass{org.rascalmpl.library.lang.html.IO} +java void writeHTMLFile( + loc file, + HTMLElement dom, + str charset = "UTF-8", + HTMLEscapeMode escapeMode = baseMode(), + bool outline = false, + bool prettyPrint = true, + int indentAmount = 4, + int maxPaddingWidth = 30, + HTMLSyntax \syntax = htmlSyntax(), + bool dropOrigins = true +); + +@synopsis{Convenience function to visualize an HTMLElement tree in the browser} +Content serve(HTMLElement elem) = html(writeHTMLString(elem)); +### |project://rascal/src/org/rascalmpl/library/lang/html5/DOM.rsc| +@synopsis{DOM-based AST model for HTML5 including pretty printer} +@contributor{Tijs van der Storm - storm@cwi.nl (CWI)} +module lang::html5::DOM + +import List; + +@synopsis{Generic representation for all HTML tag types.} +@description{ +Every standard HTML5 node type has a convenience function in +this module with a name that is equal to the tag name, modulo renamings +for identifier compatibility in Rascal. +} +@examples{ +```rascal-shell +import lang::html5::DOM; +title("This is a title node") +``` +} +data HTML5Node = html5node(str name, list[value] kids); + +@synopsis{Generic representation for all HTML attributes.} +@description{ +Every standard HTML5 attribute has a convenience function in +this module with a name that starts with an "a" for "attribute". +This is to prevent overlaps with node names that are the same. +} +@examples{ +```rascal-shell +import lang::html5::DOM; +aabbr("Short") +} +data HTML5Attr = html5attr(str name, value val); + +HTML5Node a(value kids...) = html5node("a", kids); +HTML5Node abbr(value kids...) = html5node("abbr", kids); +HTML5Node address(value kids...) = html5node("address", kids); +HTML5Node area(value kids...) = html5node("area", kids); +HTML5Node article(value kids...) = html5node("article", kids); +HTML5Node aside(value kids...) = html5node("aside", kids); +HTML5Node audio(value kids...) = html5node("audio", kids); +HTML5Node b(value kids...) = html5node("b", kids); +HTML5Node base(value kids...) = html5node("base", kids); +HTML5Node bdi(value kids...) = html5node("bdi", kids); +HTML5Node bdo(value kids...) = html5node("bdo", kids); +HTML5Node blockquote(value kids...) = html5node("blockquote", kids); +HTML5Node body(value kids...) = html5node("body", kids); +HTML5Node br(value kids...) = html5node("br", kids); +HTML5Node button(value kids...) = html5node("button", kids); +HTML5Node canvas(value kids...) = html5node("canvas", kids); +HTML5Node caption(value kids...) = html5node("caption", kids); +HTML5Node cite(value kids...) = html5node("cite", kids); +HTML5Node code(value kids...) = html5node("code", kids); +HTML5Node col(value kids...) = html5node("col", kids); +HTML5Node colgroup(value kids...) = html5node("colgroup", kids); +HTML5Node \data(value kids...) = html5node("data", kids); +HTML5Node datalist(value kids...) = html5node("datalist", kids); +HTML5Node dd(value kids...) = html5node("dd", kids); +HTML5Node del(value kids...) = html5node("del", kids); +HTML5Node details(value kids...) = html5node("details", kids); +HTML5Node dfn(value kids...) = html5node("dfn", kids); +HTML5Node dialog(value kids...) = html5node("dialog", kids); +HTML5Node div(value kids...) = html5node("div", kids); +HTML5Node dl(value kids...) = html5node("dl", kids); +HTML5Node dt(value kids...) = html5node("dt", kids); +HTML5Node em(value kids...) = html5node("em", kids); +HTML5Node embed(value kids...) = html5node("embed", kids); +HTML5Node fieldset(value kids...) = html5node("fieldset", kids); +HTML5Node figcaption(value kids...) = html5node("figcaption", kids); +HTML5Node figure(value kids...) = html5node("figure", kids); +HTML5Node footer(value kids...) = html5node("footer", kids); +HTML5Node form(value kids...) = html5node("form", kids); +HTML5Node h1(value kids...) = html5node("h1", kids); +HTML5Node h2(value kids...) = html5node("h2", kids); +HTML5Node h3(value kids...) = html5node("h3", kids); +HTML5Node h4(value kids...) = html5node("h4", kids); +HTML5Node h5(value kids...) = html5node("h5", kids); +HTML5Node h6(value kids...) = html5node("h6", kids); +HTML5Node head(value kids...) = html5node("head", kids); +HTML5Node header(value kids...) = html5node("header", kids); +HTML5Node hgroup(value kids...) = html5node("hgroup", kids); +HTML5Node hr(value kids...) = html5node("hr", kids); +HTML5Node html(value kids...) = html5node("html", kids); +HTML5Node i(value kids...) = html5node("i", kids); +HTML5Node iframe(value kids...) = html5node("iframe", kids); +HTML5Node img(value kids...) = html5node("img", kids); +HTML5Node input(value kids...) = html5node("input", kids); +HTML5Node ins(value kids...) = html5node("ins", kids); +HTML5Node kbd(value kids...) = html5node("kbd", kids); +HTML5Node keygen(value kids...) = html5node("keygen", kids); +HTML5Node label(value kids...) = html5node("label", kids); +HTML5Node legend(value kids...) = html5node("legend", kids); +HTML5Node li(value kids...) = html5node("li", kids); +HTML5Node link(value kids...) = html5node("link", kids); +HTML5Node main(value kids...) = html5node("main", kids); +HTML5Node \map(value kids...) = html5node("map", kids); +HTML5Node mark(value kids...) = html5node("mark", kids); +HTML5Node menu(value kids...) = html5node("menu", kids); +HTML5Node menuitem(value kids...) = html5node("menuitem", kids); +HTML5Node meta(value kids...) = html5node("meta", kids); +HTML5Node meter(value kids...) = html5node("meter", kids); +HTML5Node nav(value kids...) = html5node("nav", kids); +HTML5Node noscript(value kids...) = html5node("noscript", kids); +HTML5Node object(value kids...) = html5node("object", kids); +HTML5Node ol(value kids...) = html5node("ol", kids); +HTML5Node optgroup(value kids...) = html5node("optgroup", kids); +HTML5Node option(value kids...) = html5node("option", kids); +HTML5Node output(value kids...) = html5node("output", kids); +HTML5Node p(value kids...) = html5node("p", kids); +HTML5Node param(value kids...) = html5node("param", kids); +HTML5Node pre(value kids...) = html5node("pre", kids); +HTML5Node progress(value kids...) = html5node("progress", kids); +HTML5Node q(value kids...) = html5node("q", kids); +HTML5Node rp(value kids...) = html5node("rp", kids); +HTML5Node rt(value kids...) = html5node("rt", kids); +HTML5Node ruby(value kids...) = html5node("ruby", kids); +HTML5Node s(value kids...) = html5node("s", kids); +HTML5Node samp(value kids...) = html5node("samp", kids); +HTML5Node script(value kids...) = html5node("script", kids); +HTML5Node section(value kids...) = html5node("section", kids); +HTML5Node select(value kids...) = html5node("select", kids); +HTML5Node small(value kids...) = html5node("small", kids); +HTML5Node source(value kids...) = html5node("source", kids); +HTML5Node span(value kids...) = html5node("span", kids); +HTML5Node strong(value kids...) = html5node("strong", kids); +HTML5Node style(value kids...) = html5node("style", kids); +HTML5Node sub(value kids...) = html5node("sub", kids); +HTML5Node summary(value kids...) = html5node("summary", kids); +HTML5Node sup(value kids...) = html5node("sup", kids); +HTML5Node table(value kids...) = html5node("table", kids); +HTML5Node tbody(value kids...) = html5node("tbody", kids); +HTML5Node td(value kids...) = html5node("td", kids); +HTML5Node template(value kids...) = html5node("template", kids); +HTML5Node textarea(value kids...) = html5node("textarea", kids); +HTML5Node tfoot(value kids...) = html5node("tfoot", kids); +HTML5Node th(value kids...) = html5node("th", kids); +HTML5Node thead(value kids...) = html5node("thead", kids); +HTML5Node time(value kids...) = html5node("time", kids); +HTML5Node title(value kids...) = html5node("title", kids); +HTML5Node tr(value kids...) = html5node("tr", kids); +HTML5Node track(value kids...) = html5node("track", kids); +HTML5Node u(value kids...) = html5node("u", kids); +HTML5Node ul(value kids...) = html5node("ul", kids); +HTML5Node var(value kids...) = html5node("var", kids); +HTML5Node video(value kids...) = html5node("video", kids); +HTML5Node wbr(value kids...) = html5node("wbr", kids); + +HTML5Attr aaaliasbbr(value val) = html5attr("abbr", val); +HTML5Attr aabout(value val) = html5attr("about", val); +HTML5Attr aaccept(value val) = html5attr("accept", val); +HTML5Attr aaccesskey(value val) = html5attr("accesskey", val); +HTML5Attr aaction(value val) = html5attr("action", val); +HTML5Attr aalign(value val) = html5attr("align", val); +HTML5Attr aallowfullscreen(value val) = html5attr("allowfullscreen", val); +HTML5Attr aalt(value val) = html5attr("alt", val); +HTML5Attr aaria(value val) = html5attr("aria", val); +HTML5Attr aasync(value val) = html5attr("async", val); +HTML5Attr aautocomplete(value val) = html5attr("autocomplete", val); +HTML5Attr aautofocus(value val) = html5attr("autofocus", val); +HTML5Attr aautoplay(value val) = html5attr("autoplay", val); +HTML5Attr aborder(value val) = html5attr("border", val); +HTML5Attr achallenge(value val) = html5attr("challenge", val); +HTML5Attr achar(value val) = html5attr("char", val); +HTML5Attr acharset(value val) = html5attr("charset", val); +HTML5Attr achecked(value val) = html5attr("checked", val); +HTML5Attr aacite(value val) = html5attr("cite", val); +HTML5Attr aclass(value val) = html5attr("class", val); +HTML5Attr acols(value val) = html5attr("cols", val); +HTML5Attr acolspan(value val) = html5attr("colspan", val); +HTML5Attr acommand(value val) = html5attr("command", val); +HTML5Attr acontent(value val) = html5attr("content", val); +HTML5Attr acontenteditable(value val) = html5attr("contenteditable", val); +HTML5Attr acontextmenu(value val) = html5attr("contextmenu", val); +HTML5Attr acontrols(value val) = html5attr("controls", val); +HTML5Attr acoords(value val) = html5attr("coords", val); +HTML5Attr adata(value val) = html5attr("data", val); +HTML5Attr adatatype(value val) = html5attr("datatype", val); +HTML5Attr adatetime(value val) = html5attr("datetime", val); +HTML5Attr adefault(value val) = html5attr("default", val); +HTML5Attr adefer(value val) = html5attr("defer", val); +HTML5Attr adir(value val) = html5attr("dir", val); +HTML5Attr adirname(value val) = html5attr("dirname", val); +HTML5Attr adisabled(value val) = html5attr("disabled", val); +HTML5Attr adownload(value val) = html5attr("download", val); +HTML5Attr adraggable(value val) = html5attr("draggable", val); +HTML5Attr adropzone(value val) = html5attr("dropzone", val); +HTML5Attr aenctype(value val) = html5attr("enctype", val); +HTML5Attr afor(value val) = html5attr("for", val); +HTML5Attr aform(value val) = html5attr("form", val); +HTML5Attr aformaction(value val) = html5attr("formaction", val); +HTML5Attr aformenctype(value val) = html5attr("formenctype", val); +HTML5Attr aformmethod(value val) = html5attr("formmethod", val); +HTML5Attr aformnovalidate(value val) = html5attr("formnovalidate", val); +HTML5Attr aformtarget(value val) = html5attr("formtarget", val); +HTML5Attr aheaders(value val) = html5attr("headers", val); +HTML5Attr aheight(value val) = html5attr("height", val); +HTML5Attr ahidden(value val) = html5attr("hidden", val); +HTML5Attr ahigh(value val) = html5attr("high", val); +HTML5Attr ahref(value val) = html5attr("href", val); +HTML5Attr ahreflang(value val) = html5attr("hreflang", val); +HTML5Attr ahttp(value val) = html5attr("http", val); +HTML5Attr aicon(value val) = html5attr("icon", val); +HTML5Attr aid(value val) = html5attr("id", val); +HTML5Attr ainlist(value val) = html5attr("inlist", val); +HTML5Attr aismap(value val) = html5attr("ismap", val); +HTML5Attr aitemid(value val) = html5attr("itemid", val); +HTML5Attr aitemprop(value val) = html5attr("itemprop", val); +HTML5Attr aitemref(value val) = html5attr("itemref", val); +HTML5Attr aitemscope(value val) = html5attr("itemscope", val); +HTML5Attr aitemtype(value val) = html5attr("itemtype", val); +HTML5Attr akeytype(value val) = html5attr("keytype", val); +HTML5Attr akind(value val) = html5attr("kind", val); +HTML5Attr alabel(value val) = html5attr("label", val); +HTML5Attr alang(value val) = html5attr("lang", val); +HTML5Attr alanguage(value val) = html5attr("language", val); +HTML5Attr alist(value val) = html5attr("list", val); +HTML5Attr alocal_(value val) = html5attr("local_", val); +HTML5Attr aloop(value val) = html5attr("loop", val); +HTML5Attr alow(value val) = html5attr("low", val); +HTML5Attr amanifest(value val) = html5attr("manifest", val); +HTML5Attr amax(value val) = html5attr("max", val); +HTML5Attr amaxlength(value val) = html5attr("maxlength", val); +HTML5Attr amedia(value val) = html5attr("media", val); +HTML5Attr amediagroup(value val) = html5attr("mediagroup", val); +HTML5Attr amethod(value val) = html5attr("method", val); +HTML5Attr amin(value val) = html5attr("min", val); +HTML5Attr amultiple(value val) = html5attr("multiple", val); +HTML5Attr amuted(value val) = html5attr("muted", val); +HTML5Attr aname(value val) = html5attr("name", val); +HTML5Attr anovalidate(value val) = html5attr("novalidate", val); +HTML5Attr aonabort(value val) = html5attr("onabort", val); +HTML5Attr aonafterprint(value val) = html5attr("onafterprint", val); +HTML5Attr aonbeforeprint(value val) = html5attr("onbeforeprint", val); +HTML5Attr aonbeforeunload(value val) = html5attr("onbeforeunload", val); +HTML5Attr aonblur(value val) = html5attr("onblur", val); +HTML5Attr aoncanplay(value val) = html5attr("oncanplay", val); +HTML5Attr aoncanplaythrough(value val) + = html5attr("oncanplaythrough", val); +HTML5Attr aonchange(value val) = html5attr("onchange", val); +HTML5Attr aonclick(value val) = html5attr("onclick", val); +HTML5Attr aoncontextmenu(value val) = html5attr("oncontextmenu", val); +HTML5Attr aondblclick(value val) = html5attr("ondblclick", val); +HTML5Attr aondrag(value val) = html5attr("ondrag", val); +HTML5Attr aondragend(value val) = html5attr("ondragend", val); +HTML5Attr aondragenter(value val) = html5attr("ondragenter", val); +HTML5Attr aondragleave(value val) = html5attr("ondragleave", val); +HTML5Attr aondragover(value val) = html5attr("ondragover", val); +HTML5Attr aondragstart(value val) = html5attr("ondragstart", val); +HTML5Attr aondrop(value val) = html5attr("ondrop", val); +HTML5Attr aondurationchange(value val) + = html5attr("ondurationchange", val); +HTML5Attr aonemptied(value val) = html5attr("onemptied", val); +HTML5Attr aonended(value val) = html5attr("onended", val); +HTML5Attr aonerror(value val) = html5attr("onerror", val); +HTML5Attr aonfocus(value val) = html5attr("onfocus", val); +HTML5Attr aonformchange(value val) = html5attr("onformchange", val); +HTML5Attr aonforminput(value val) = html5attr("onforminput", val); +HTML5Attr aonhashchange(value val) = html5attr("onhashchange", val); +HTML5Attr aoninput(value val) = html5attr("oninput", val); +HTML5Attr aoninvalid(value val) = html5attr("oninvalid", val); +HTML5Attr aonkeydown(value val) = html5attr("onkeydown", val); +HTML5Attr aonkeypress(value val) = html5attr("onkeypress", val); +HTML5Attr aonkeyup(value val) = html5attr("onkeyup", val); +HTML5Attr aonload(value val) = html5attr("onload", val); +HTML5Attr aonloadeddata(value val) = html5attr("onloadeddata", val); +HTML5Attr aonloadedmetadata(value val) + = html5attr("onloadedmetadata", val); +HTML5Attr aonloadstart(value val) = html5attr("onloadstart", val); +HTML5Attr aonmessage(value val) = html5attr("onmessage", val); +HTML5Attr aonmousedown(value val) = html5attr("onmousedown", val); +HTML5Attr aonmousemove(value val) = html5attr("onmousemove", val); +HTML5Attr aonmouseout(value val) = html5attr("onmouseout", val); +HTML5Attr aonmouseover(value val) = html5attr("onmouseover", val); +HTML5Attr aonmouseup(value val) = html5attr("onmouseup", val); +HTML5Attr aonmousewheel(value val) = html5attr("onmousewheel", val); +HTML5Attr aonoffline(value val) = html5attr("onoffline", val); +HTML5Attr aononline(value val) = html5attr("ononline", val); +HTML5Attr aonpagehide(value val) = html5attr("onpagehide", val); +HTML5Attr aonpageshow(value val) = html5attr("onpageshow", val); +HTML5Attr aonpause(value val) = html5attr("onpause", val); +HTML5Attr aonplay(value val) = html5attr("onplay", val); +HTML5Attr aonplaying(value val) = html5attr("onplaying", val); +HTML5Attr aonpopstate(value val) = html5attr("onpopstate", val); +HTML5Attr aonprogress(value val) = html5attr("onprogress", val); +HTML5Attr aonratechange(value val) = html5attr("onratechange", val); +HTML5Attr aonredo(value val) = html5attr("onredo", val); +HTML5Attr aonreset(value val) = html5attr("onreset", val); +HTML5Attr aonresize(value val) = html5attr("onresize", val); +HTML5Attr aonscroll(value val) = html5attr("onscroll", val); +HTML5Attr aonseeked(value val) = html5attr("onseeked", val); +HTML5Attr aonseeking(value val) = html5attr("onseeking", val); +HTML5Attr aonselect(value val) = html5attr("onselect", val); +HTML5Attr aonshow(value val) = html5attr("onshow", val); +HTML5Attr aonstalled(value val) = html5attr("onstalled", val); +HTML5Attr aonstorage(value val) = html5attr("onstorage", val); +HTML5Attr aonsubmit(value val) = html5attr("onsubmit", val); +HTML5Attr aonsuspend(value val) = html5attr("onsuspend", val); +HTML5Attr aontimeupdate(value val) = html5attr("ontimeupdate", val); +HTML5Attr aonundo(value val) = html5attr("onundo", val); +HTML5Attr aonunload(value val) = html5attr("onunload", val); +HTML5Attr aonvolumechange(value val) = html5attr("onvolumechange", val); +HTML5Attr aonwaiting(value val) = html5attr("onwaiting", val); +HTML5Attr aopen(value val) = html5attr("open", val); +HTML5Attr aoptimum(value val) = html5attr("optimum", val); +HTML5Attr apattern(value val) = html5attr("pattern", val); +HTML5Attr aping(value val) = html5attr("ping", val); +HTML5Attr aplaceholder(value val) = html5attr("placeholder", val); +HTML5Attr aposter(value val) = html5attr("poster", val); +HTML5Attr aprefix(value val) = html5attr("prefix", val); +HTML5Attr apreload(value val) = html5attr("preload", val); +HTML5Attr aproperty(value val) = html5attr("property", val); +HTML5Attr aradiogroup(value val) = html5attr("radiogroup", val); +HTML5Attr areadonly(value val) = html5attr("readonly", val); +HTML5Attr arel(value val) = html5attr("rel", val); +HTML5Attr arequired(value val) = html5attr("required", val); +HTML5Attr aresource(value val) = html5attr("resource", val); +HTML5Attr arev(value val) = html5attr("rev", val); +HTML5Attr areversed(value val) = html5attr("reversed", val); +HTML5Attr arole(value val) = html5attr("role", val); +HTML5Attr arows(value val) = html5attr("rows", val); +HTML5Attr arowspan(value val) = html5attr("rowspan", val); +HTML5Attr asandbox(value val) = html5attr("sandbox", val); +HTML5Attr ascope(value val) = html5attr("scope", val); +HTML5Attr ascoped(value val) = html5attr("scoped", val); +HTML5Attr aseamless(value val) = html5attr("seamless", val); +HTML5Attr aselected(value val) = html5attr("selected", val); +HTML5Attr ashape(value val) = html5attr("shape", val); +HTML5Attr asize(value val) = html5attr("size", val); +HTML5Attr asizes(value val) = html5attr("sizes", val); +HTML5Attr aspan(value val) = html5attr("span", val); +HTML5Attr aspellcheck(value val) = html5attr("spellcheck", val); +HTML5Attr asrc(value val) = html5attr("src", val); +HTML5Attr asrcdoc(value val) = html5attr("srcdoc", val); +HTML5Attr asrclang(value val) = html5attr("srclang", val); +HTML5Attr astart(value val) = html5attr("start", val); +HTML5Attr astep(value val) = html5attr("step", val); +HTML5Attr astyle(value val) = html5attr("style", val); +HTML5Attr atabindex(value val) = html5attr("tabindex", val); +HTML5Attr atarget(value val) = html5attr("target", val); +HTML5Attr atemplate(value val) = html5attr("template", val); +HTML5Attr atitle(value val) = html5attr("title", val); +HTML5Attr atranslate(value val) = html5attr("translate", val); +HTML5Attr atype(value val) = html5attr("type", val); +HTML5Attr atypeof(value val) = html5attr("typeof", val); +HTML5Attr ausemap(value val) = html5attr("usemap", val); +HTML5Attr avalign(value val) = html5attr("valign", val); +HTML5Attr avalue(value val) = html5attr("value", val); +HTML5Attr avocab(value val) = html5attr("vocab", val); +HTML5Attr awidth(value val) = html5attr("width", val); +HTML5Attr awrap(value val) = html5attr("wrap", val); +HTML5Attr axml_base(value val) = html5attr("xml_base", val); +HTML5Attr axml_id(value val) = html5attr("xml_id", val); +HTML5Attr axml_lang(value val) = html5attr("xml_lang", val); +HTML5Attr axml_space(value val) = html5attr("xml_space", val); + +/* +# void tags: no closing tag. +# for others: need closing tag. ( is not allowed) +# area, base, br, col, command, embed, hr, img, input, +# keygen, link, meta, param, source, track, wbr + + +#Raw text: script, style (see below) + +#Escapable raw text: textarea, title +# can have charrefs, but no ambiguous ampersand +# http://www.w3.org/TR/html5/syntax.html#syntax-ambiguous-ampersand + +#The text in raw text and escapable raw text elements must not contain +#any occurrences of the string "" (U+003E), or "/" +#(U+002F). + +*/bool isVoid(str x) + = x + in {"area", + "base", + "br", + "col", + "command", + "embed", + "hr", + "img", + "input", + "keygen", + "link,", + "meta", + "param", + "source", + "track", + "wbr"}; + +bool isRawText(str x) = x in {"script", "style"}; + +bool isEscapableRawText(str x) = x in {"textarea", "title"}; + +bool isBlockLevel(str x) + = x + in {"address", + "article", + "aside", + "audio", + "blockquote", + "canvas", + "dd", + "div", + "dl", + "fieldset", + "figcaption", + "figure", + "footer", + "form", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "header", + "hgroup", + "hr", + "noscript", + "ol", + "output", + "p", + "pre", + "section", + "table", + "tfoot", + "ul", + "video"}; + +str startTag(str n, str attrs) = "\<\>"; + +str endTag(str n) = "\\>"; + +str startTag(str n, {}) = startTag(n, ""); + +default str startTag(str n, set[HTML5Attr] attrs) + = startTag(n, " " + attrsToString(attrs)); + +str attrsToString(set[HTML5Attr] attrs) + = intercalate(" ", [attrToString(a) | HTML5Attr a <- attrs]); + +// TODO: escaping +str attrToString(html5attr(str x, value v)) = "=\"\""; + +str rawText(list[value] xs) = ( "" | it + "" | x <- xs ); + +// TODO: escaping +str escapableRawText(list[value] xs) = ( "" | it + "" | x <- xs ); + +str kidsToString(list[value] kids) = ( "" | it + kidToString(k) | k <- kids ); + +str kidToString(HTML5Node elt) = toString(elt); +str kidToString(HTML5Attr x) = ""; + +default str kidToString(value x) = ""; + +str nodeToString(str n, set[HTML5Attr] attrs, list[value] kids) { + str s = ""; + if (isVoid(n)) { + // ignore kids... + s += startTag(n, attrs); + } + else if (isRawText(n)) { + s += startTag(n, attrs); + s += rawText(kids); + s += endTag(n); + } + else if (isEscapableRawText(n)) { + s += startTag(n, attrs); + s += escapableRawText(kids); + s += endTag(n); + } + else if (isBlockLevel(n)) { + s + += " + ' <for (k <- kids) {> + ' <}>"; + } + else { + s += startTag(n, attrs); + s += kidsToString(kids); + s += endTag(n); + } + return s; +} + +private HTML5Node example + = html( + head(title("something")), + body(ul(li("bla"), li("foo", 3, img(ahref("someref"))))) + ) ; + +@synopsis{pretty print HTML5Node DOM to a string} +str toString(HTML5Node x) { + attrs = {k| HTML5Attr k <- x.kids}; + kids = [k | value k <- x.kids, !(HTML5Attr _ := k)]; + return nodeToString(x.name, attrs, kids); +} +### |project://rascal/src/org/rascalmpl/library/lang/java/Compiler.rsc| +@license{ +Copyright (c) 2018-2025, NWO-I CWI, Swat.engineering and Paul Klint +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +} +@synopsis{Provides access to the JDK javac compiler, to compile (generated) Java code.} +@benefits{ +* Compile any Java code, stored anywhere reachable through a `loc` and using any library +reachable through a `loc`, to JVM bytecode using the standard JDK compiler. +} +@pitfalls{ +* If you are looking for Java analysis and transformation support in Rascal please go find +the [java-air](http://www.rascalmpl.org/docs/Packages/RascalAir) package. The current module +only provides a Java to bytecode compilation API. +} +module lang::java::Compiler + +extend Message; + +import IO; +import Location; +import String; +import util::FileSystem; +import util::PathConfig; +import util::Reflective; +import util::UUID; + +@synopsis{Compile all the .java files in source folders in PathConfig} +@description{ +See ((compileJavaSourceFolders)) for more information. +} +list[Message] compileJava(PathConfig pcfg) + = compileJavaSourceFolders(pcfg.srcs, pcfg.bin, libs = pcfg.libs); + +@synopsis{Compile a single Java source file} +@pitfalls{ +* `file` has to be reachable from `srcs`, because a fully qualified class name is computing by relativizing the source file `loc` against the `srcs` folders. +* The source files is read using ((readFile)). +* All class files, which could be many in the case of anonymous or nested classes, are written directly to the `bin` folder. +* `libs` is typically a list of `jar+file:///` or `mvn:///` locations, one for each (transitive) compile-time dependency. +Without these the compiler will complain about missing symbols and return error messages. +} +list[Message] compileJavaSourceFile( + loc file, + loc bin, + list[loc] srcs, + list[loc] libs = [] +) + = compileJava( + {}, bin, libs = libs + ); + +@synopsis{Compile all java source files from a list of source folders.} +@description{ +* Qualified class names are obtained by relativizing against the source folders. +* Source code is read in using ((readFile)). +* Bytecode files are written directly into the `bin` folder +} +list[Message] compileJavaSourceFolders( + list[loc] srcFolders, + loc bin, + list[loc] libs = [] +) + = compileJava( + { + | src <- srcFolders, f <- find(src, "java") + }, bin, + libs = libs + ); + +@synopsis{Call the Java compiler on a list of files, reading in their contents first and computing their qualified names} +list[Message] compileJava( + list[loc] sources, + loc bin, + list[loc] srcs = [], + list[loc] libs = [] +) + = compileJava( + {| s <- sources}, bin, libs = libs + ); + +@synopsis{Main workhorse for compiling Java source code} +@benefits{ +* Use in memory cache to optimize compiling a lot of files at once +* Can work with any kind of source loc or target loc +} +@pitfalls{ +* The sources relation must match the right file name to the right source code, for accurate error messages. +* The sources relation must give the right qualified name to the right source code, otherwise the compilation fails. +* While `compileJava` is running all source code and all generated bytecode will be in memory; this can be significant. +if you need to use less memory then call `compileJava` several times with a smaller relation. +} +@javaClass{org.rascalmpl.library.lang.java.JavaCompilerForRascal} +java list[Message] compileJava( + rel[loc fileName, str qualifiedName, str sourceCode] sources, + loc bin, + list[loc] libs = [] +); + +@synopsis{computes the `packageName.className` fully qualified class name from a source filename `/.../src/packageName/className.java`} +private +str qualifiedName(loc path, list[loc] srcs) + = replaceAll(relativize(srcs, path)[extension = ""].path[1..], "/", ".") + when path.extension == "java"; + +@synopsis{tests folder structure of input .java files against the folder structure of the output .class files} +test bool compilerInputOutputFileTest() { + root = uuid()[scheme = "memory"]; + target = root + "target"; + + writeFile(root + "A.java", "class A { }"); + writeFile(root + "B.java", "class B extends A { }"); + writeFile(root + "pack/C.java", "package pack; \npublic class C { }"); + writeFile( + root + "pack/pack/D.java", + "package pack.pack; \nclass D extends pack.C { }" + ); + + errors = compileJavaSourceFolders([root], target); + assert errors == [] : "unexpected errors: "; + + return + find(target, "class") == {target + "pack/C.class", + target + "pack/pack/D.class", + target + "B.class", + target + "A.class"}; +} + +@synopsis{tests Java compilation with required libraries on the classpath} +test bool compilerClasspathTest() { + root = uuid()[scheme = "memory"]; + target = root + "target"; + + writeFile( + root + "A.java", + "class A { org.rascalmpl.uri.URIResolverRegistry reg = org.rascalmpl.uri.URIResolverRegistry.getInstance(); }" + ); + + rascalLib = resolvedCurrentRascalJar(); + + errors = compileJavaSourceFolders([root], target, libs = [rascalLib]); + + assert errors == [] : "no errors expected: "; + + return find(target, "class") == {target + "A.class"}; +} +### |project://rascal/src/org/rascalmpl/library/lang/java/Runner.rsc| +@license{ +Copyright (c) 2018-2025, NWO-I CWI, Swat.engineering and Paul Klint +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +} +@synopsis{Simple Rascal API for executing JVM bytecode, as static main method or as Junit4 test class.} +@benefits{ +* Run the static `main` method of any .class file in JVM bytecode format, stored anywhere reachable through a `loc` and using any library +reachable through a `loc`, to JVM bytecode using the current JVM. +* Run any Junit4 test class in JVM bytecode format, stored anywhere reachable through a `loc` and using +any library also reachable through a `loc` +} +@pitfalls{ +* If you are looking for Java analysis and transformation support in Rascal please go find +the [java-air](http://www.rascalmpl.org/docs/Packages/RascalAir) package. The current module +only provides a Java to bytecode compilation API. +} +module lang::java::Runner + +import IO; +import lang::java::Compiler; +import String; +import util::FileSystem; +import util::Reflective; +import util::UUID; +import Location; + +@synopsis{Execute the static main function of a (compiled) java class} +@benefits{ +* This function can use class files from any support loc scheme +} +@pitfalls{ +* The current Rascal runtime/interpreter classloaders, including vallang, are always used +before any other class. +} +@javaClass{org.rascalmpl.library.lang.java.JavaRunner} +java void runJavaMain( + str qualifiedName, + list[str] args, + list[loc] classpath = [] +); + +data JUnitVersion = junit4(); + +@synsopsis{Execute JUnit test classes directly from Rascal} +@benefits{ +* This function can use class files from any support loc scheme +* Classes are loaded from the `classpath` parameter with any `loc` scheme that supports class loading. +} +@pitfalls{ +* The current Rascal runtime/interpreter classloaders, including vallang, are always used +before any other class. +} +@javaClass{org.rascalmpl.library.lang.java.JavaRunner} +java list[Message] runJUnitTestClass( + str qualifiedName, + list[loc] classpath = [], + JUnitVersion version = junit4() +); + +@synopsis{Locate the right classpath for JUnit} +@javaClass{org.rascalmpl.library.lang.java.JavaRunner} +@benefits{ +* Yhis comes in handy for the compiler `libs` parameter, if the test still needs to be compiled from source. +} +java loc getJUnitClassPath(JUnitVersion version = junit4()); + +test bool factorialMainTest() { + root = uuid()[scheme = "memory"]; + target = root + "target"; + + source + = |project://rascal/test/org/rascalmpl/benchmark/Factorial/Factorial.java|; + qname = "org.rascalmpl.benchmark.Factorial.Factorial"; + + messages + = compileJavaSourceFile ( + source, + target, + [|project://rascal/test/|] + ); + + runJavaMain ( + qname, + [] + , classpath = [target, resolvedCurrentRascalJar()] + ); + + return true; +} + +test bool junitTestRunTest() { + root = uuid()[scheme = "memory"]; + target = root + "target"; + sources = root + "sources"; + sourceFile = sources + "TheTestClass.java"; + + code + = "import org.junit.Test; + 'import static org.junit.Assert.assertTrue; + 'public class TheTestClass { + ' @Test + ' public void aTestExample() { + ' assertTrue(true); + ' } + '}"; + + writeFile(sourceFile, code); + + messages + = compileJavaSourceFile ( + sourceFile, + target, + [sources] + , libs = [resolvedCurrentRascalJar(), getJUnitClassPath()] + ); + + assert + messages == [] : "example should compile without errors: "; + + qname + = replaceAll( + relativize([sources], sourceFile)[extension = ""].path[1..], "/", "." + ); + + results + = runJUnitTestClass(qname, classpath = [target, getJUnitClassPath()]); + + assert [info("aTestExample(TheTestClass) started", loc _), + info("aTestExample(TheTestClass) finished", _)] := results; + + code + = "import org.junit.Test; + 'import static org.junit.Assert.assertTrue; + 'public class TheTestClass { + ' @Test + ' public void aTestExample() { + ' assertTrue(false); + ' } + '}"; + + writeFile(sourceFile, code); + + messages + = compileJavaSourceFile ( + sourceFile, + target, + [sources] + , libs = [resolvedCurrentRascalJar(), getJUnitClassPath()] + ); + + assert + messages == [] : "example should compile without errors: "; + + results + = runJUnitTestClass(qname, classpath = [target, getJUnitClassPath()]); + + assert [info("aTestExample(TheTestClass) started", _), + error("aTestExample(TheTestClass) failed", _), + info("aTestExample(TheTestClass) finished", _)] := results; + + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/javascript/saner/Syntax.rsc| +@synopsis{A saner grammar for JavaScript + +It assumes: +- semicolons are present +- there is no comma expression} +@contributor{ +Tijs van der Storm - CWI (storm@cwi.nl) +} +module lang::javascript::saner::Syntax + +start syntax Source = source: Statement* statements; + +syntax Statement + = varDecl: VarDecl varDecl + | empty: ";" + | block: "{" Statement* statements "}" + | expression: Expression ! function expression ";" + // Block level things + | function: Function function + | ifThen: "if" "(" Expression cond ")" Statement body () !>> "else" + | ifThenElse: "if" "(" Expression cond ")" Statement body "else" Statement elseBody + | doWhile: "do" Statement body "while" "(" Expression cond ")" ";" + | whileDo: "while" "(" Expression cond ")" Statement body + | forDo: "for" "(" {Expression ","}* inits ";" {Expression ","}* conds ";" {Expression ","}* ops ")" Statement body + | forDoDeclarations: "for" "(" "var" {VariableDeclarationNoIn ","}+ decls ";" {Expression ","}* conds ";" {Expression ","}* ops ")" Statement body + | forIn: "for" "(" Expression var "in" Expression obj ")" Statement body + | forInDeclaration: "for" "(" "var" Id varId "in" Expression obj ")" Statement body + | with: "with" "(" Expression scope ")" Statement body + // Non local control flow + | returnExp: "return" Expression result ";" + | returnNoExp: "return" ";" + | throwExp: "throw" Expression result ";" + | throwNoExp: "throw" ";" + | continueLabel: "continue" Id label ";" + | continueNoLabel: "continue" ";" + | breakLabel: "break" Id label ";" + | breakNoLabel: "break" ";" + | debugger: "debugger" ";" + | labeled: Id label ":" Statement statement + | switchCase: "switch" "(" Expression cond ")" "{" CaseClause* clauses "}" + | tryCatch: "try" Statement body "catch" "(" Id varId ")" Statement catchBody + | tryFinally: "try" Statement body "finally" Statement finallyBody + | tryCatchFinally: "try" Statement body "catch" "(" Id varId ")" Statement catchBody "finally" Statement finallyBody + ; + +syntax VariableDeclaration + = init: Id id "=" Expression init + | nonInit: Id id + ; + +syntax VariableDeclarationNoIn + = init: Id id "=" Expression ! inn init + | nonInit: Id id + ; + +syntax CaseClause + = caseOf: "case" Expression guard ":" Statement* body + | defaultCase: "default" ":" Statement* body + ; + +syntax Function + = "function" Id name "(" {Id ","}* parameters ")" "{" Statement* statements "}" + | "function" "(" {Id ","}* parameters ")" "{" Statement* statements "}" + ; + +// Todo: Check associativity https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence +// Todo: Right now you can put any type of Expression on the lhs of a variableAssignment like: 5 = y; We only want to do this for a few cases however +// Rather than exclude everything other than those cases it would be much easier to whitelist the few that ARE allowed. +syntax Expression + = array: "[" {Expression ","}* elements ","? "]" + | objectDefinition: "{" {PropertyAssignment ","}* properties ","? "}" + | this: "this" + | var: Id name + | literal: Literal literal + | bracket \bracket: "(" Expression arg ")" + | function: Function function + > property: Expression obj "." Id fieldId + | call: Expression func "(" {Expression ","}* params ")" + | member: Expression obj "[" Expression field "]" + > new: "new" Expression cons + > postIncr: Expression arg "++" + | postDec: Expression arg "--" + > delete: "delete" Expression arg + | typeof: "typeof" Expression arg + | preIncr: "++" Expression arg + | preDecr: "--" Expression arg + | prefixPlus: "+" !>> [+=] Expression arg + | prefixMin: "-" !>> [\-=] Expression arg + | binNeg: "~" Expression arg + | not: "!" !>> [=] Expression arg + > left ( mul: Expression lhs "*" !>> [*=] Expression rhs + | div: Expression lhs "/" !>> [/=] Expression rhs + | rem: Expression lhs "%" !>> [%=] Expression rhs + ) + > left ( add: Expression lhs "+" !>> [+=] Expression rhs + | sub: Expression lhs "-" !>> [\-=] Expression rhs + ) + > // right??? + left ( shl: Expression lhs "\<\<" Expression rhs + | shr: Expression lhs "\>\>" !>> [\>] Expression rhs + | shrr: Expression lhs "\>\>\>" Expression rhs + ) + > non-assoc ( lt: Expression lhs "\<" Expression rhs + | leq: Expression lhs "\<=" Expression rhs + | gt: Expression lhs "\>" Expression rhs + | geq: Expression lhs "\>=" Expression rhs + | instanceof: Expression lhs "instanceof" Expression rhs + | inn: Expression lhs "in" Expression rhs + ) + > right ( eqq: Expression lhs "===" Expression rhs + | neqq: Expression lhs "!==" Expression rhs + | eq: Expression lhs "==" !>> [=] Expression rhs + | neq: Expression lhs "!=" !>> [=] Expression rhs + ) + > right binAnd: Expression lhs "&" !>> [&=] Expression rhs + > right binXor: Expression lhs "^" !>> [=] Expression rhs + > right binOr: Expression lhs "|" !>> [|=] Expression rhs + > left and: Expression lhs "&&" Expression rhs + > left or: Expression lhs "||" Expression rhs + > cond: Expression ! cond cond "?" Expression ! cond then ":" Expression elseExp + > right ( assign: Expression lhs "=" !>> [=] Expression rhs + | assignMul: Expression lhs "*=" Expression rhs + | assignDiv: Expression lhs "/=" Expression rhs + | assignRem: Expression lhs "%=" Expression rhs + | assignAdd: Expression lhs "+=" Expression rhs + | assignSub: Expression lhs "-=" Expression rhs + | assignShl: Expression lhs "\<\<=" Expression rhs + | assignShr: Expression lhs "\>\>=" Expression rhs + | assignShrr: Expression lhs "\>\>\>=" Expression rhs + | assignBinAnd: Expression lhs "&=" Expression rhs + | assignBinXor: Expression lhs "^=" Expression rhs + | assignBinOr: Expression lhs "|=" Expression rhs + ) + ; + +syntax VarDecl = "var" {VariableDeclaration ","}+ declarations ";"; + +syntax PropertyName + = id: Id name + | string: String key + | numeric: Numeric numeric + ; + +syntax PropertyAssignment + = property: PropertyName name ":" Expression value + | get: "get" PropertyName name "(" ")" "{" Statement* body "}" + | \set: "set" PropertyName name "(" Id x ")" "{" Statement* body "}" + ; + +syntax Literal + = null: "null" + | boolean: Boolean bool + | numeric: Numeric num + | string: String str + | regexp: RegularExpression regexp + ; + +syntax Boolean + = t: "true" + | f: "false" + ; + +syntax Numeric + = decimal: [a-zA-Z$_0-9] !<< Decimal decimal + | hexadecimal: [a-zA-Z$_0-9] !<< HexInteger hexInt + ; + +lexical Decimal + = DecimalInteger [.] [0-9]* ExponentPart? + | [.] [0-9]+ ExponentPart? + | DecimalInteger ExponentPart? + ; + +lexical DecimalInteger + = [0] + | [1-9] [0-9]* !>> [0-9] + ; + +lexical ExponentPart = [eE] SignedInteger; + +lexical SignedInteger = [+\-]? [0-9]+ !>> [0-9]; + +lexical HexInteger = [0] [Xx] [0-9a-fA-F]+ !>> [a-zA-Z_]; + +lexical String + = [\"] DoubleStringChar* [\"] + | [\'] SingleStringChar* [\'] + ; + +lexical DoubleStringChar + = ![\"\\\n] + | [\\] EscapeSequence + ; + +lexical SingleStringChar + = ![\'\\\n] + | [\\] EscapeSequence + ; + +lexical EscapeSequence + = CharacterEscapeSequence + | [0] !>> [0-9] + | HexEscapeSequence + | UnicodeEscapeSequence + ; + +lexical CharacterEscapeSequence + = SingleEscapeCharacter + | NonEscapeCharacter + ; + +lexical SingleEscapeCharacter = [\'\"\\bfnrtv]; + +lexical NonEscapeCharacter // SourceCharacter but not one of EscapeCharacter or LineTerminator + = ![\n\'\"\\bfnrtv0-9xu]; + +lexical EscapeCharacter + = SingleEscapeCharacter + | [0-9] + | [xu] + ; + +lexical HexDigit = [a-fA-F0-9]; + +lexical HexEscapeSequence = [x] HexDigit HexDigit; + +lexical UnicodeEscapeSequence = "u" HexDigit HexDigit HexDigit HexDigit; + +lexical RegularExpression = [/] RegularExpressionBody [/] RegularExpressionFlags; + +lexical RegularExpressionBody = RegularExpressionFirstChar RegularExpressionChar*; + +lexical RegularExpressionFirstChar + = ![*/\[\n\\] + | RegularExpressionBackslashSequence + | RegularExpressionClass + ; + +lexical RegularExpressionChar + = ![/\[\n\\] + | RegularExpressionBackslashSequence + | RegularExpressionClass + ; + +lexical RegularExpressionBackslashSequence = [\\] ![\n]; + +lexical RegularExpressionClass = [\[] RegularExpressionClassChar* [\]]; + +lexical RegularExpressionClassChar + = ![\n\]\\] + | RegularExpressionBackslashSequence + ; + +lexical RegularExpressionFlags = [a-zA-Z]* !>> [a-zA-Z]; + +lexical Whitespace = [\t-\n\r\ ]; + +lexical Comment + = @category="comment" "/*" CommentChar* "*/" + | @category="comment" "//" ![\n]*$ + ; + +lexical CommentChar + = ![*] + | [*] !>> [/] + ; + +lexical LAYOUT + = Whitespace + | Comment + ; + +layout LAYOUTLIST = LAYOUT* !>> [\t\ \n] !>> "/*" !>> "//"; + +lexical Id = ([a-zA-Z$_0-9] !<< [$_a-zA-Z] [a-zA-Z$_0-9]* !>> [a-zA-Z$_0-9]) \ Reserved; + +keyword Reserved + = "break" + | "case" + | "catch" + | "continue" + | "debugger" + | "default" + | "delete" + | "do" + | "else" + | "finally" + | "for" + | "function" + | "if" + | "instanceof" + | "in" + | "new" + | "return" + | "switch" + | "this" + | "throw" + | "try" + | "typeof" + | "var" + | "void" + | "while" + | "with" + | "abstract" + | "boolean" + | "byte" + | "char" + | "class" + | "const" + | "double" + | "enum" + | "export" + | "extends" + | "final" + | "float" + | "goto" + | "implements" + | "import" + | "interface" + | "int" + | "long" + | "native" + | "package" + | "private" + | "protected" + | "public" + | "short" + | "static" + | "super" + | "synchronized" + | "throws" + | "transient" + | "volatile" + | "null" + | "true" + | "false" + ; +### |project://rascal/src/org/rascalmpl/library/lang/json/IO.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Serialization of Rascal values to JSON format and deserialization back from JSON format to Rascal values.} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Mark Hills - Mark.Hills@cwi.nl (CWI)} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Tijs van der Storm - storm@cwi.nl (CWI)} +@contributor{Davy Landman - landman@cwi.nl (CWI)} +@description{ +The pairs ((asJSON)):((parseJSON)) and ((writeJSON)):((readJSON)) are both bi-directional +transformations between serializable Rascal values (all except function instances) and JSON strings. +The ((asJSON)) and ((parseJSON)) work on `str` representations, while ((writeJSON)) and ((readJSON)) +stream to/from files directly. + +The basic principle of the bi-directional mapping is that constructors of algebraic data-types +map one-to-one to JSON object notation, and vice versa. The other builtin Rascal data-structures +are judiciously mapped to objects and arrays, and strings, etc. The goal is that their representation +is natural on the receiving end (e.g. TypeScript, Javascript and Python code), without sacrificing +on the naturalness of the Rascal representation. +} +@pitfalls{ +* ((asJSON)) and ((writeJSON)) are not isomorphisms. They are homomorphisms that choose +JSON arrays or JSON objects for multiple different kinds of Rascal values. For example +maps and nodes and ADT's are all mapped to JSON object notation (homonyms). +* JSON is a serialization format that does not deal with programming language numerical +encodings such as `double`, `float`` or `long`. ((writeJSON)) and ((asJSON)) might write numbers +beyond the limits and beyond the accuracy of what the other programming language can deal with. +You can expect errors at the time when the other language (Javascript, Python, TypeScript) reads these numbers from JSON. +} +@benefits{ +* Using the `expected`` type arguments of ((parseJSON)) and ((readJSON)) the homonyms created by ((asJSON)) and ((writeJSON)) can be converted back to their +original Rascal structures. If the expected type contains only _concrete types_, and no _abstract types_ then +then pairs ((asJSON))/((parseJSON)) and ((writeJSON))/((readJSON)) are isomorphic. + * The _abstract types_ are `value`, `node`, `num` or any composite type that contains it. `Maybe[value]` is an example of an abstract type. + * The _concrete types_ are all types which are not _abstract types_. `Maybe[int]` is an example of a concrete type. + * Run-time values always have concrete types, while variables in code often have abstract types. +* If you provide `value` or `node` as an expected type, you will always get a useful representation +on the Rascal side. It is not guaranteed to be the same representation as before. +* ((readJSON)) and ((parseJSON)) can read numbers beyond the bounds of normal `int`, `long` and `doubles`. As +long as the number is syntactically correct, they will bind to the right Rascal number representation. +} +@examples{ +This example demonstrates serializing: +* constructors without parameters as "enums" +* constructors with both positional fields and keyword fields +* datetime serialization +* integer serialization +```rascal-shell +import lang::json::IO; +data Size = xxs() | xs() | s() | m() | l() | xl() | xxl(); +data Person = person(str firstName, str lastName, datetime birth=$1977-05-17T06:00:00.000+00:00$, int height=0, Size size = m()); +example = person("Santa", "Class", height=175, size=xxl()); +jsonExample = asJSON(example, dateTimeFormat="YYYY-MM-DD"); +``` + +On the way back we can also track origins for constructors: +```rascal-shell,continue +parseJSON(#Person, jsonExample, trackOrigins=true) +``` +} +module lang::json::IO + +import util::Maybe; +import Exception; + +@synopsis{JSON parse errors have more information than general parse errors} +@description{ +* `location` is the place where the parsing got stuck (going from left to right). +* `reason` is a factual diagnosis of what was expected at that position, versus what was found. +* `path` is a path query string into the JSON value from the root down to the leaf where the error was detected. +} +@benefits{ +* ((NoOffsetParseError)) is for when accurate offset tracking is turned off. Typically this is _on_ +even if `trackOrigins=false`, when we call the json parsers from Rascal. +} +data RuntimeException (str reason = "", str path = "") + = ParseError(loc location) + | NoOffsetParseError(loc location, int line, int column) + ; + +private str DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd\'T\'HH:mm:ssZ" ; + +@javaClass{org.rascalmpl.library.lang.json.IO} +@synopsis{reads JSON values from a stream} +@description{ +In general the translation behaves as follows: +* Objects translate to map[str,value] by default, unless a node is expected (properties are then translated to keyword fields) +* Arrays translate to lists by default, or to a set if that is expected or a tuple if that is expected. Arrays may also be interpreted as constructors or nodes (see below) +* Booleans translate to bools +* If the expected type provided is a datetime then an int instant is mapped and if a string is found then the dateTimeFormat parameter will be used to configure the parsing of a date-time string +* If the expected type provided is an ADT then this reader will try to "parse" each object as a constructor for that ADT. It helps if there is only one constructor for that ADT. Positional parameters will be mapped by name as well as keyword parameters. +* If the expected type provided is a node then it will construct a node named "object" and map the fields to keyword fields. +* If num, int, real or rat are expected both strings and number values are mapped +* If loc is expected than strings which look like URI are parsed (containing :/) or a file:/// URI is build, or if an object is found each separate field of + a location object is read from the respective properties: { scheme : str, authority: str?, path: str?, fragment: str?, query: str?, offset: int, length: int, begin: [bl, bc], end: [el, ec]} +* Go to ((JSONParser)) to find out how to use the optional `parsers` parameter. +* if the parser finds a `null` JSON value, it will lookup in the `nulls` map based on the currently expected type which value to return, or throw an exception otherwise. +First the expected type is used as a literal lookup, and then each value is tested if the current type is a subtype of it. +} +java &T readJSON( + type[&T] expected, + loc src, + str dateTimeFormat = DEFAULT_DATETIME_FORMAT, + bool lenient = false, + bool trackOrigins = false, + JSONParser[value] parser = (type[value] _, str _) { throw ""; }, + map[type[value] forType, value nullValue] nulls = defaultJSONNULLValues, + bool explicitConstructorNames = false, + bool explicitDataTypes = false +); + +public map[type[value] forType, value nullValue] defaultJSONNULLValues + = ( + #Maybe[value] : nothing(), + #node : "null"(), + #int : -1, + #num : -1.0, + #real : -1.0, + #rat : -1r1, + #value : "null"(), + #str : "", + #list[value] : [], + #set[value] : {}, + #map[value, value] : (), + #loc : |unknown:///|, + #bool : false + ) ; + +@javaClass{org.rascalmpl.library.lang.json.IO} +@synopsis{parses JSON values from a string. +In general the translation behaves as the same as for ((readJSON)).} +java &T parseJSON( + type[&T] expected, + str src, + str dateTimeFormat = DEFAULT_DATETIME_FORMAT, + bool lenient = false, + bool trackOrigins = false, + JSONParser[value] parser = (type[value] _, str _) { throw ""; }, + map[type[value] forType, value nullValue] nulls = defaultJSONNULLValues, + bool explicitConstructorNames = false, + bool explicitDataTypes = false +); + +@javaClass{org.rascalmpl.library.lang.json.IO} +@synopsis{Serializes a value as a JSON string and stream it} +@description{ +This function tries to map Rascal values to JSON values in a natural way. +In particular it tries to create a value that has the same number of recursive levels, +such that one constructor maps to one object. The serialization is typically _lossy_ since +JSON values by default do not explicitly encode the class or constructor while Rascal data types do. + +If you need the names of constructors or data-types in your result, then use the parameters: +* `explicitConstructorNames=true` will store the name of every constructor in a field `_constructor` +* `explicitDataTypes=true` will store the name of the ADT in a field called `_type` +* Check out ((JSONFormatter)) on how to use the `formatters` parameter +* The `dateTimeFormat` parameter dictates how `datetime` values will be printed. +* The `unpackedLocations` parameter will produce an object with many fields for every property of a `loc` value, but +if set to false a `loc` will be printed as a string. +} +@pitfalls{ +* It is understood that Rascal's number types have arbitrary precision, but this is not supported by the JSON writer. +As such when an `int` is printed that does not fit into a JVM `long`, there will be truncation to the lower 64 bits. +For `real` numbers that are larger than JVM's double you get "negative infinity" or "positive infinity" as a result. +} +java void writeJSON( + loc target, + value val, + bool unpackedLocations = false, + str dateTimeFormat = DEFAULT_DATETIME_FORMAT, + bool dateTimeAsInt = false, + bool rationalsAsString = false, + int indent = 0, + bool dropOrigins = true, + JSONFormatter[value] formatter = str (value _){ fail; }, + bool explicitConstructorNames = false, + bool explicitDataTypes = false +); + +@javaClass{org.rascalmpl.library.lang.json.IO} +@synopsis{Does what ((writeJSON)) does but serializes to a string instead of a location target.} +@synopsis{Serializes a value as a JSON string and stores it as a string} +@description{ +This function uses `writeJSON` and stores the result in a string. +} +java str asJSON( + value val, + bool unpackedLocations = false, + str dateTimeFormat = DEFAULT_DATETIME_FORMAT, + bool dateTimeAsInt = false, + bool rationalsAsString = false, + int indent = 0, + bool dropOrigins = true, + JSONFormatter[value] formatter = str (value _){ fail; }, + bool explicitConstructorNames = false, + bool explicitDataTypes = false +); + +@synopsis{((writeJSON)) and ((asJSON)) uses `Formatter` functions to flatten structured data to strings, on-demand} +@description{ +A JSONFormatter can be passed to the ((writeJSON)) and ((asJSON)) functions. When/if the type matches an algebraic data-type +to be serialized, then it is applied and the resulting string is serialized to the JSON stream instead of the structured data. + +The goal of JSONFormat and its dual JSONParser is to bridge the gap between string-based JSON encodings and typical +Rascal algebraic combinators. +} +alias JSONFormatter[&T] = str(&T); + +@synopsis{((readJSON)) and ((parseJSON)) use JSONParser functions to turn unstructured data into structured data.} +@description{ +A parser JSONParser can be passed to ((readJSON)) and ((parseJSON)). When the reader expects an algebraic data-type +or a syntax type, but the input at that moment is a JSON string, then the parser is called on that string (after string.trim()). + +The resulting data constructor is put into the resulting value instead of a normal string. + +The goal of JSONParser and its dual JSONFormatter is to bridge the gap between string-based JSON encodings and typical +Rascal algebraic combinators. +} +@benefits{ +* Use parsers to create more structure than JSON provides. +} +@pitfalls{ +* The `type[&T]` argument is called dynamically by the JSON reader; it does not contain the +grammar. It does encode the expected type of the parse result. +* The expected types can only be `data` types, not syntax types. +} +alias JSONParser[&T] = &T(type[&T], str); +### |project://rascal/src/org/rascalmpl/library/lang/json/ast/Implode.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Mark Hills - Mark.Hills@cwi.nl (CWI)} +module lang::json::ast::Implode + +import lang::json::\syntax::JSON; +import lang::json::ast::JSON; +import String; + +private str removeEnds(str s) { + return substring(substring(s, 0, size(s) - 1), 1); +} + +public JSON buildAST(start[JSONText] jt) = buildAST(jt.top); + +public +JSON buildAST((JSONText)``) + = object(convertObject(obj)); +public JSON buildAST((JSONText)``) = array(convertArray(a)); + +private +JSON convertValue((Value)``) + = object(convertObject(obj)); +private JSON convertValue((Value)``) = array(convertArray(a)); +private +JSON convertValue((Value)``) + = number(toReal("")); +private +JSON convertValue((Value)``) + = number(toReal("")); +private +JSON convertValue((Value)``) + = string(removeEnds("")); +private JSON convertValue((Value)`false`) = boolean(false); +private JSON convertValue((Value)`null`) = null(); +private JSON convertValue((Value)`true`) = boolean(true); + +private +map[str, JSON] convertObject((Object)`{ < {Member ","}* ms > }`) { + map[str, JSON] res = (); + for ((Member) ` : ` <- ms) { + mn = removeEnds(""); + av = convertValue(memberValue); + if (mn notin res) { + res[mn] = av; + } + else { + throw "Duplicate field in object"; + } + } + return res; +} + +private +list[JSON] convertArray((Array)`[ < {Value ","}* vs > ]`) + = [convertValue(v) | v <- vs]; +### |project://rascal/src/org/rascalmpl/library/lang/json/ast/JSON.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Mark Hills - Mark.Hills@cwi.nl (CWI)} +@contributor{Tijs van der Storm - storm@cwi.nl (CWI)} +module lang::json::ast::JSON + +data JSON + = null() + | object(map[str, JSON] properties) + | array(list[JSON] values) + | number(real n) + | string(str s) + | boolean(bool b) + | ivalue(type[value] t, value v) + ; +### |project://rascal/src/org/rascalmpl/library/lang/json/syntax/JSON.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Mark Hills - Mark.Hills@cwi.nl (CWI)} +module lang::json::\syntax::JSON + +/* This JSON parser is based on the JSON grammar found in RFC4627. + http://www.ietf.org/rfc/rfc4627 + *//* The definition of a JSON Text is taken from section 2 */start syntax JSONText + = Object + | Array + ; + +/* The definition of value is taken from section 2.1 */syntax Value + = Object + | Array + | NumericLiteral + | StringLiteral + | False: "false" + | Null: "null" + | True: "true" + ; + +/* The definition of objects is taken from section 2.2 */syntax Object = Object: "{" {Member ","}* members "}"; + +syntax Member = Member: StringLiteral memberName ":" Value memberValue; + +/* The definition of arrays is taken from section 2.3 */syntax Array = Array: "[" {Value ","}* values "]"; + +/* The definition of numbers is taken from section 2.4 */lexical NumericLiteral + = RealLiteral + | IntegerLiteral + ; + +lexical RealLiteral + = [\-]? [0] [Ee] [+\-]? [0-9]+ + | [\-]? [1-9] [0-9]* [Ee] [+\-]? [0-9]+ + | [\-]? [0] "." !>> "." [0-9]* + | [\-]? [1-9] [0-9]* "." !>> "." [0-9]* + | [\-]? [0] "." [0-9]* [Ee] [+\-]? [0-9]+ + | [\-]? [1-9] [0-9]* "." [0-9]* [Ee] [+\-]? [0-9]+ + ; + +lexical IntegerLiteral + = [\-]? "0" !>> [0-9] + | [\-]? [1-9] [0-9]* !>> [0-9] + ; + +/* The definition of strings is taken from section 2.5 */lexical StringLiteral = "\"" StringChar* chars "\""; + +lexical StringChar + = ![\"\\] + | "\\" [\"\\/bfnrt] + | UnicodeEscape + ; + +lexical UnicodeEscape + = utf16: "\\" [u] [0-9A-Fa-f] [0-9A-Fa-f] [0-9A-Fa-f] [0-9A-Fa-f] + | utf32: "\\" [U] [0-9A-Fa-f] [0-9A-Fa-f] [0-9A-Fa-f] [0-9A-Fa-f] [0-9A-Fa-f] [0-9A-Fa-f] + ; + +/* The definition of keywords is taken from section 2.1 */keyword JSONKeywords + = "false" + | "null" + | "true" + ; + +/* The definition of layout is taken from section 2 */layout LAYOUT = [\t-\n\r\ ]* !>> [\t-\n\r\ ]; +### |project://rascal/src/org/rascalmpl/library/lang/manifest/IO.rsc| +@license{ + Copyright (c) 2009-2020 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Jar Manifest files are a kind of property files typically stored inside Jar files. + They contain meta information about the other files stored in the jar file.} +@contributor{Jurgen Vinju} +module lang::manifest::IO + +import Type; +import String; + +@synopsis{reads a manifest file and returns its main attributes as a map} +@javaClass{org.rascalmpl.library.lang.manifest.IO} +java map[str key, str val] readManifest(loc input); + +@synopsis{reads a manifest and converts the resulting map to keyword parameters for the given type} +(&T <: node) readManifest(type[&T <: node] t, loc input) { + value convert(\list(\str()), str i) = [trim(x) | x <- split(",", i)]; + value convert(\set(\str()), str i) = [trim(x) | x <- split(",", i)]; + default value convert(Symbol _, str i) = i; + + m = readManifest(input); + + if (/\cons(label(name, _), args, kws, _) := t.definitions) { + return + make( + t, + name, + [convert(f, m[l]) | label(l, f) <- args, m[l]?], + (l: convert(f, m[l])| label(l, f) <- kws, m[l]?) + ); + } + throw "no valid constructor found for to store manifest in values in"; +} +### |project://rascal/src/org/rascalmpl/library/lang/paths/Unix.rsc| +@synopsis{Defines the syntax of filesystem and network drive paths on DOS and Windows Systems.} +@description{ +This syntax definition of POSIX paths and file names, including some of the conventions +with ~ for the home folder and . and .. for relative directories. + +The main function of this module, ((parseUnixPath)): +* faithfully maps any syntactically correctly Unix paths to syntactically correct `loc` values. +* throws a ParseError if the path does not comply. +* ensures that if the file exists on system A, then the `loc` representation +resolves to the same file on system A via any ((Library:module:IO)) function. +* and nothing more. No normalization, no interpretation of `.` and `..`, no changing of cases. +This is left to downstream processors of `loc` values, if necessary. The current transformation +is purely syntactical, and tries to preserve the semantics of the path as much as possible. +} +@pitfalls{ +* the `~` notation is typically a feature of the shell and not of system paths. However it is so commonly +used to refer to the home directories of users that we've added an interpretation here with the `home:///` scheme. +* otherwise, the path syntax may be different from what you have to type in _bash_ or _zsh_. This is because shells +need to reserve characters, like spaces, for different purposes (commandline argument separation). The +current definition is about the path notation that shells like _zsh_ and _bash_, and other programs, have to pass into the string arguments of +OS features, after their own concatenation, splicing, variable expansion, de-escaping and unquoting routines have finished.. +} +module lang::paths::Unix + +lexical UnixPath + = absolute: Slashes UnixFilePath? + | relative: UnixFilePath + | home: "~" (Slashes UnixFilePath)? + | user: "~" UserName uname (Slashes UnixFilePath)? + ; + +lexical UserName = ![/~]+; + +lexical PathChar = ![/]; + +lexical PathSegment + = current: "." + | parent: ".." + | pname: (PathChar \ "~" PathChar*) \ ".." \ "." \ "~" + ; + +lexical Slashes = Slash+ !>> [/]; + +lexical Slash = [/]; + +lexical UnixFilePath = {PathSegment Slashes}+ segments Slashes?; + +import ParseTree; + +@synopsis{Convert a Unix path literal to a source location URI} +@description{ +1. parses the path using the grammar for ((UnixPath)) +2. takes the _literal_ name components using string interpolation `""`. This means no decoding/encoding happens at all while extracting +hostname, share name and path segment names. Also all superfluous path separators are skipped. +3. uses `loc + str` path concatenation with its builtin character encoding to construct the URI. Also +the right path separators are introduced. + +This conversion supports generic Unix path syntax, including: +* Absolute: `/usr/local/bin` +* Relative: `hello.txt` +* Home: `~/hello.txt` +* User: `~userName\hello.txt` +} +loc parseUnixPath(str input, loc src = |unknown:///|) + = mapPathToLoc(parse(#UnixPath, input, src)); + +@synopsis{Root is a special case} +private loc mapPathToLoc((UnixPath) ``) = |file:///|; + +@synopsis{Absolute: given the drive and relative to its root.} +private +loc mapPathToLoc((UnixPath) ``) + = appendPath(|file:///|, path); + +@synopsis{Relative: relative to the current working directory.} +private +loc mapPathToLoc((UnixPath) ``) + = appendPath(|cwd:///|, path); + +@synopsis{Home relative: relative to the current users home directory} +private +loc mapPathToLoc((UnixPath) `~`) + = appendPath(|home:///|, path); + +@synopsis{Home relative: relative to the current users home directory} +private loc mapPathToLoc((UnixPath) `~`) = |home:///|; + +@synopsis{User relative: relative to any specific user's home directory} +private +loc mapPathToLoc( + (UnixPath) `~` +) + = appendPath(|home:///../< uname >/|, path); + +@synopsis{User relative: relative to any specific user's home directory} +private +loc mapPathToLoc((UnixPath) `~`) + = |home:///../< uname >/|; + +private +loc appendPath(loc root, UnixFilePath path) + = ( root | it + "" | segment <- path.segments ); + +test bool root() = parseUnixPath("/") == |file:///|; + +test bool absolutePath() + = parseUnixPath("/usr/local/bin") == |file:///usr/local/bin|; + +test bool relativePath() = parseUnixPath(".bash_rc") == |cwd:///.bash_rc|; + +test bool homePath() + = parseUnixPath("~/.bash_profile") == |home:///.bash_profile|; + +test bool userPath() + = parseUnixPath("~root/.bash_profile") == |home:///../root/.bash_profile|; +### |project://rascal/src/org/rascalmpl/library/lang/paths/Windows.rsc| +@synopsis{Defines the syntax of filesystem and network drive paths on DOS and Windows Systems.} +@description{ +This syntax definition of file paths and file names in Windows formalizes open-source implementations +manually written in Java, C++ and C# code. These are parsers for Windows syntax of file and directory names, +as well as shares on local networks (UNC notation). It also derives from openly available documentation +sources on Windows and the .NET platform for confirmation and test examples. + +The main function of this module, ((parseWindowsPath)): +* faithfully maps any syntactically correctly Windows paths to syntactically correct `loc` values. +* throws a ParseError if the path does not comply. Typically file names ending in spaces do not comply. +* ensures that if the file exists on system A, then the `loc` representation +resolves to the same file on system A via any ((Library:module:IO)) function. +* and nothing more. No normalization, no interpretation of `.` and `..`, no changing of cases. +This is left to downstream processors of `loc` values, if necessary. The current transformation +is purely syntactical, and tries to preserve the semantics of the path as much as possible. +} +@pitfalls{ +* Length limitations are not implemented by this parser. This means that overly long names will lead +to IO exceptions when they are finally used. +* The names of drives, files and devices are mapped as-is, without normalization. This means that +the resulting `loc` value may not be a _canonical_ representation of the identified resource. +Normalization of `loc` values is for a different function TBD. +} +module lang::paths::Windows + +import IO; +import util::SystemAPI; + +lexical WindowsPath + = unc: Slash Slash Slashes? PathChar* \ "." Slashes PathChar* Slashes WindowsFilePath + | uncDOSDrive: Slash Slash Slashes? DOSDevice Slashes Drive ":" OptionalWindowsFilePath + | uncDOSPath: Slash Slash Slashes? DOSDevice Slashes PathChar* Slashes WindowsFilePath + | absolute: Drive ":" Slashes WindowsFilePath + | driveRelative: Drive ":" WindowsFilePath + | directoryRelative: Slash WindowsFilePath + | relative: WindowsFilePath + ; + +lexical OptionalWindowsFilePath + = () + | Slashes WindowsFilePath + ; + +lexical DOSDevice = [.?]; + +lexical PathChar = !([\a00-\a20\<\>:\"|?*\\/] - [\ ]); + +lexical PathSegment + = current: "." + | parent: ".." + | name: PathChar+ \ ".." \ "." + ; + +lexical Drive = [A-Za-z]; + +lexical Slashes = Slash+ !>> [\\/]; + +lexical Slash = [\\/]; + +lexical WindowsFilePath = {PathSegment Slashes}* segments Slashes? [\ .] !<< (); + +// only the last segment must not end in spaces. +import ParseTree; + +@synopsis{Convert a windows path literal to a source location URI} +@description{ +1. parses the path using the grammar for ((WindowsPath)) +2. takes the _literal_ name components using string interpolation `""`. This means no decoding/encoding happens at all while extracting +hostname, share name and path segment names. Also all superfluous path separators are skipped. +3. uses `loc + str` path concatenation with its builtin character encoding to construct the URI. Also +the right path separators are introduced. + +This conversion supports generic Windows path syntax, including: +* Absolute drive-specific: `C:\Program Files` +* Relative drive-specific: `C:hello.txt` +* Relative: `hello.txt` +* Directory-relative: `\hello.txt` +* UNC format: `\\system07\C$\` + +Windows paths, against popular believe, support both `/` and `\` as path separators. +} +loc parseWindowsPath(str input, loc src = |unknown:///|) + = mapPathToLoc(parse(#WindowsPath, input, src)); + +@synopsis{UNC} +private +loc mapPathToLoc( + (WindowsPath) `` +) + = appendPath(|unc://< hostName >/| + "", path); + +@synopsis{DOS UNC Device Drive} +private +loc mapPathToLoc( + (WindowsPath) `:` +) + = appendPath(|unc://< deviceIndicator(dq) >/| + ":", path); + +@synopsis{DOS UNC Device Path} +private +loc mapPathToLoc( + (WindowsPath) `` +) + = appendPath(|unc://< deviceIndicator(dq) >/| + "", path); + +private str deviceIndicator((DOSDevice) `?`) = "%3F"; +private str deviceIndicator((DOSDevice) `.`) = "."; + +@synopsis{DOS UNCPath} +private +loc mapPathToLoc( + (WindowsPath) `?` +) + = appendPath(|unc://%3F/| + "", path); + +@synopsis{Absolute: given the drive and relative to its root.} +private +loc mapPathToLoc( + (WindowsPath) `:` +) + = appendPath(|file:///< drive >:/|, path); + +@synopsis{Drive relative: relative to the current working directory on the given drive.} +private +loc mapPathToLoc((WindowsPath) `:`) + = appendPath(|file:///< drive >:.|, path); + +@synopsis{Directory relative: relative to the root of the current drive.} +private +loc mapPathToLoc((WindowsPath) ``) + = appendPath(|cwdrive:///|, path); + +@synopsis{Relative to the current working directory on the current drive.} +private +loc mapPathToLoc((WindowsPath) ``) + = appendPath(|cwd:///|, path); + +private +loc appendPath(loc root, WindowsFilePath path) + = ( root | it + "" | segment <- path.segments ); + +private loc appendPath(loc root, (OptionalWindowsFilePath) ``) = root; + +private +loc appendPath( + loc root, (OptionalWindowsFilePath) `` +) + = appendPath(root, path); + +private bool IS_WINDOWS = /win/i := getSystemProperty("os.name") ; + +test bool uncSharePath() + = parseWindowsPath("\\\\Server2\\Share\\Test\\Foo.txt") == |unc://Server2/Share/Test/Foo.txt|; + +test bool uncDrivePath() + = parseWindowsPath("\\\\system07\\C$\\") == |unc://system07/C$|; + +test bool uncDOSDevicePathLocalFileQuestion() { + loc l = parseWindowsPath("\\\\?\\c:\\windows\\system32\\cmd.exe"); + + if (IS_WINDOWS) { + assert exists(l); + } + return l == |unc://%3F/c:/windows/system32/cmd.exe|; +} + +test bool uncDOSDevicePathLocalFileDot() { + loc l = parseWindowsPath("\\\\.\\C:\\Test\\Foo.txt"); + + return l == |unc://./C:/Test/Foo.txt|; +} + +test bool uncDOSDeviceUNCSharePath() { + // the entire UNC namespace is looped back into the DOS Device UNC encoding via + // the reserved name "UNC": + loc m1 = parseWindowsPath("\\\\?\\UNC\\Server\\Share\\Test\\Foo.txt"); + loc m2 = parseWindowsPath("\\\\.\\UNC\\Server\\Share\\Test\\Foo.txt"); + + return + m1 == |unc://%3F/UNC/Server/Share/Test/Foo.txt| + && m2 == |unc://./UNC/Server/Share/Test/Foo.txt|; +} + +test bool uncDOSDeviceVolumeGUIDReference() { + loc l + = parseWindowsPath( + "\\\\.\\Volume{b75e2c83-0000-0000-0000-602f00000000}\\Test\\Foo.txt" + ); + + return + l == |unc://./Volume%7Bb75e2c83-0000-0000-0000-602f00000000%7D/Test/Foo.txt|; +} + +test bool uncDOSDeviceBootPartition() { + loc l = parseWindowsPath("\\\\.\\BootPartition\\"); + return l == |unc://./BootPartition|; +} + +test bool simpleDrivePathC() + = parseWindowsPath("C:\\Program Files\\Rascal") == |file:///C:/Program%20Files/Rascal|; + +test bool mixedSlashesDrivePathC() + = parseWindowsPath("C:\\Program Files/Rascal") == |file:///C:/Program%20Files/Rascal|; + +test bool trailingSlashesDrivePathC() + = parseWindowsPath("C:\\Program Files\\Rascal\\\\") == |file:///C:/Program%20Files/Rascal|; + +test bool simpleDrivePathD() + = parseWindowsPath("D:\\Program Files\\Rascal") == |file:///D:/Program%20Files/Rascal|; + +test bool uncNetworkShareOk() { + loc l = parseWindowsPath("\\\\localhost\\ADMIN$\\System32\\cmd.exe"); + + if (IS_WINDOWS) { + return exists(l); + } + else { + return |unc://localhost/ADMIN$/System32/cmd.exe| == l; + } +} +### |project://rascal/src/org/rascalmpl/library/lang/pico/HiFiDemo.rsc| +@synopsis{Demonstrates HiFi source-to-source transformations through concrete syntax rewrites and text edits.} +module lang::pico::HiFiDemo + +import lang::pico::\syntax::Main; +import IO; +import ParseTree; +import analysis::diff::edits::HiFiTreeDiff; +import analysis::diff::edits::ExecuteTextEdits; + +@synopsis{Blindly swaps the branches of all the conditionals in a program} +@description{ +This rule is syntactically correct and has a clear semantics. The +layout of the resulting if-then-else-fi statement is also clear. +} +start[Program] flipConditionals(start[Program] program) + = visit(program) { + case (Statement) `if then  + ' <{Statement ";"}* ifBranch> + 'else  + ' <{Statement ";"}* elseBranch> + 'fi` + => (Statement) `if then  + ' <{Statement ";"}* elseBranch> + 'else  + ' <{Statement ";"}* ifBranch> + 'fi` + }; + +void main() { + t + = parse( + #start[Program], + |project://rascal/src/org/rascalmpl/library/lang/pico/examples/flip.pico| + ); + println("The original: + '"); + + u = flipConditionals(t); + println("Branches swapped, comments and indentation lost: + '"); + + edits = treeDiff(t, u); + println("Smaller text edits:"); + iprintln(edits); + + newContent = executeTextEdits("", edits); + println("Better output after executeTextEdits: + '"); + + newU = parse(#start[Program], newContent); + + assert u := newU : "the rewritten tree matches the newly parsed"; +} +### |project://rascal/src/org/rascalmpl/library/lang/pico/format/Formatting.rsc| +@synopsis{Demonstrates ((Tree2Box)), ((Box2Text)) and ((HiFiLayoutDiff)) for constructing a declarative and HiFi Pico formatting pipeline} +@description{ +Using four generic or generated, "language parametric", building blocks we construct a Pico formatting pipeline: + +* ((ParseTree)) is used to _generate_ a parser for Pico. +* ((Tree2Box)) provides the extensible/overridable and declarative ((toBox)) function which maps language constructs to Box expressions. +The ((toBox)) function combines generic language-parametric rules, as well as bespoke language specific rules.. +* ((Box2Text)) is a _generic_ reusable algorithm for two-dimensional string layout. +* Finally, ((HiFiLayoutDiff)) _generically_ extracts ((TextEdit))s from two trees which are equal modulo whitespace and comments. +} +@benefits{ +* The formatting is style is programmed _declaratively_ by mapping language patterns to Box expressions. +* The pipeline never loses source code comments, and this requires no attention from the language engineer. +} +@pitfalls{ +* ((Box2Text)) must be _extended_ for the open recursive calls of ((toBox)) to reach the extensions in the current module. +If you import ((Box2Text)) the extended ((toBox)) rules will only be found if they describe top-level tree nodes. +} +module lang::pico::format::Formatting + +extend lang::box::util::Tree2Box; + +import ParseTree; +import analysis::diff::edits::ExecuteTextEdits; +import analysis::diff::edits::HiFiLayoutDiff; +import lang::box::\syntax::Box; +import lang::box::util::Box2Text; +import lang::pico::\syntax::Main; + +@synopsis{In-place formatting of an entire Pico file} +void formatPicoFile(loc file) { + edits = formatPicoTree(parse(#start[Program], file)); + executeFileSystemChanges([changed(file, edits)]); +} + +@synopsis{Format a string that contains an entire Pico program} +str formatPicoString(str file) { + start[Program] tree = parse(#start[Program], file, |unknown:///|); + return executeTextEdits(file, formatPicoTree(tree)); +} + +@synopsis{Pico Format function for reuse in file, str or IDE-based formatting contexts} +list[TextEdit] formatPicoTree(start[Program] file) { + formatted = format(toBox(file)); + return layoutDiff(file, parse(#start[Program], formatted, file@\loc.top)); +} + +@synopsis{Format while} +Box toBox( + (Statement) `while do <{Statement ";"}* block> od`, + FO opts = fo() +) + = V( + H(L("while"), HV(toBox(e, opts = opts)), L("do")), + I(toClusterBox(block, opts = opts)), + L("od") + ); + +@synopsis{Format if-then-else } +Box toBox( + (Statement) `if then <{Statement ";"}* thenPart> else <{Statement ";"}* elsePart> fi`, + FO opts = fo() +) + = V( + H(L("if"), HV(toBox(e, opts = opts)), L("then")), + I(toClusterBox(thenPart, opts = opts)), + L("else"), + I(toClusterBox(elsePart, opts = opts)), + L("fi") + ); +### |project://rascal/src/org/rascalmpl/library/lang/pico/syntax/Main.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +module lang::pico::\syntax::Main + +import ParseTree; + +start syntax Program = program: "begin" Declarations decls {Statement ";"}* body "end"; + +syntax Declarations = "declare" {IdType ","}* decls ";"; + +syntax IdType = idtype: Id id ":" Type t; + +syntax Statement + = assign: Id var ":=" Expression val + | cond: "if" Expression cond "then" {Statement ";"}* thenPart "else" {Statement ";"}* elsePart "fi" + | cond: "if" Expression cond "then" {Statement ";"}* thenPart "fi" + | loop: "while" Expression cond "do" {Statement ";"}* body "od" + ; + +syntax Type + = natural: "natural" + | string: "string" + | nil: "nil-type" + ; + +syntax Expression + = id: Id name + | strcon: String string + | natcon: Natural natcon + | bracket "(" Expression e ")" + > left concat: Expression lhs "||" Expression rhs + > left ( add: Expression lhs "+" Expression rhs | min: Expression lhs "-" Expression rhs ) + ; + +lexical Id = [a-z] [a-z0-9]* !>> [a-z0-9]; +lexical Natural = [0-9]+; +lexical String = "\"" ![\"]* "\""; + +layout Layout = WhitespaceAndComment* !>> [\ \t\n\r%]; + +lexical WhitespaceAndComment + = [\ \t\n\r] + | @category="comment" "%" ![%]+ "%" + | @category="comment" "%%" ![\n]*$ + ; + +public start[Program] program(str s) { + return parse(#start[Program], s); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/format/Escape.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@bootstrapParser +module lang::rascal::format::Escape + +import String; + +@synopsis{A good old ASCII table in order to convert numbers < 128 to readable (properly escaped) string + characters. For instance, ascii((10)) maps to the string "\\n".} +public list[str] ascii + = [ + //Decimal Value Description + //------- ------- -------------------------------- + /* 000 */ "\\a00", + // NUL (Null char.) + /* 001 */ "\\a01", + // SOH (Start of Header) + /* 002 */ "\\a02", + // STX (Start of Text) + /* 003 */ "\\a03", + // ETX (End of Text) + /* 004 */ "\\a04", + // EOT (End of Transmission) + /* 005 */ "\\a05", + // ENQ (Enquiry) + /* 006 */ "\\a06", + // ACK (Acknowledgment) + /* 007 */ "\\a07", + // BEL (Bell) + /* 008 */ "\\b", + // BS (Backspace) + /* 009 */ "\\t", + // HT (Horizontal Tab) + /* 010 */ "\\n", + // LF (Line Feed) + /* 011 */ "\\a0B", + // VT (Vertical Tab) + /* 012 */ "\\a0C", + // FF (Form Feed) + /* 013 */ "\\a0D", + // CR (Carriage Return) + /* 014 */ "\\a0E", + // SO (Shift Out) + /* 015 */ "\\a0F", + // SI (Shift In) + /* 016 */ "\\a10", + // DLE (Data Link Escape) + /* 017 */ "\\a11", + // DC1 (Device Control 1) + /* 018 */ "\\a12", + // DC2 (Device Control 2) + /* 019 */ "\\a13", + // DC3 (Device Control 3) + /* 020 */ "\\a14", + // DC4 (Device Control 4) + /* 021 */ "\\a16", + // NAK (Negative Acknowledgement) + /* 022 */ "\\a16", + // SYN (Synchronous Idle) + /* 023 */ "\\a17", + // ETB (End of Trans. Block) + /* 024 */ "\\a18", + // CAN (Cancel) + /* 025 */ "\\a19", + // EM (End of Medium) + /* 026 */ "\\a1A", + // SUB (Substitute) + /* 027 */ "\\a1B", + // ESC (Escape) + /* 028 */ "\\a1C", + // FS (File Separator) + /* 029 */ "\\a1D", + // GS (Group Separator) + /* 030 */ "\\a1E", + // RS (Reqst to Send)(Rec. Sep.) + /* 031 */ "\\a1F", + // US (Unit Separator) + /* 032 */ " ", + // SP (Space) + /* 033 */ "!", + // ! (exclamation mark) + /* 034 */ "\\\"", + // " (double quote) + /* 035 */ "#", + // # (number sign) + /* 036 */ "$", + // $ (dollar sign) + /* 037 */ "%", + // % (percent) + /* 038 */ "&", + // & (ampersand) + /* 039 */ "\\\'", + // ' (single quote) + /* 040 */ "(", + // ( (left/open parenthesis) + /* 041 */ ")", + // ) (right/closing parenth.) + /* 042 */ "*", + // * (asterisk) + /* 043 */ "+", + // + (plus) + /* 044 */ ",", + // , (comma) + /* 045 */ "-", + // - (minus or dash) + /* 046 */ ".", + // . (dot) + /* 047 */ "/", + // / (forward slash) + /* 048 */ "0", + // 0 + /* 049 */ "1", + // 1 + /* 050 */ "2", + // 2 + /* 051 */ "3", + // 3 + /* 052 */ "4", + // 4 + /* 053 */ "5", + // 5 + /* 054 */ "6", + // 6 + /* 055 */ "7", + // 7 + /* 056 */ "8", + // 8 + /* 057 */ "9", + // 9 + /* 058 */ ":", + // : (colon) + /* 059 */ ";", + // ; (semi-colon) + /* 060 */ "\\\<", + // < (less than) + /* 061 */ "=", + // = (equal sign) + /* 062 */ "\\\>", + // > (greater than) + /* 063 */ "?", + // ? (question mark) + /* 064 */ "@", + // @ (AT symbol) + /* 065 */ "A", + // A + /* 066 */ "B", + // B + /* 067 */ "C", + // C + /* 068 */ "D", + // D + /* 069 */ "E", + // E + /* 070 */ "F", + // F + /* 071 */ "G", + // G + /* 072 */ "H", + // H + /* 073 */ "I", + // I + /* 074 */ "J", + // J + /* 075 */ "K", + // K + /* 076 */ "L", + // L + /* 077 */ "M", + // M + /* 078 */ "N", + // N + /* 079 */ "O", + // O + /* 080 */ "P", + // P + /* 081 */ "Q", + // Q + /* 082 */ "R", + // R + /* 083 */ "S", + // S + /* 084 */ "T", + // T + /* 085 */ "U", + // U + /* 086 */ "V", + // V + /* 087 */ "W", + // W + /* 088 */ "X", + // X + /* 089 */ "Y", + // Y + /* 090 */ "Z", + // Z + /* 091 */ "[", + // [ (left/opening bracket) + /* 092 */ "\\\\", + // \ (back slash) + /* 093 */ "]", + // ] (right/closing bracket) + /* 094 */ "^", + // ^ (caret/circumflex) + /* 095 */ "_", + // _ (underscore) + /* 096 */ "`", + // ` (backquote) + /* 097 */ "a", + // a + /* 098 */ "b", + // b + /* 099 */ "c", + // c + /* 100 */ "d", + // d + /* 101 */ "e", + // e + /* 102 */ "f", + // f + /* 103 */ "g", + // g + /* 104 */ "h", + // h + /* 105 */ "i", + // i + /* 106 */ "j", + // j + /* 107 */ "k", + // k + /* 108 */ "l", + // l + /* 109 */ "m", + // m + /* 110 */ "n", + // n + /* 111 */ "o", + // o + /* 112 */ "p", + // p + /* 113 */ "q", + // q + /* 114 */ "r", + // r + /* 115 */ "s", + // s + /* 116 */ "t", + // t + /* 117 */ "u", + // u + /* 118 */ "v", + // v + /* 119 */ "w", + // w + /* 120 */ "x", + // x + /* 121 */ "y", + // y + /* 122 */ "z", + // z + /* 123 */ "{", + // { (left/opening brace) + /* 124 */ "|", + // | (vertical bar) + /* 125 */ "}", + // } (right/closing brace) + /* 126 */ "~", + // ~ (tilde) + /* 127 */ "\\a7F" + // DEL (delete) + ] ; + +@synopsis{Creates a Rascal-character-classes escaped string character from a given + decimal index into the UTF8 table.} +public str makeCharClassChar(int ch) { + switch(ch) { + case 32: + return "\\ "; + // space ( ) + case 45: + return "\\-"; + // minus (-) + case 91: + return "\\["; + // left bracket ([) + case 93: + return "\\]"; + // right bracket (]) + case 95: + return "_"; + // underscore (_) + default: + return makeStringChar(ch); + } +} + +private list[str] hex = ["" | i <- [0..10]] + ["A", "B", "C", "D", "E", "F"] ; + +@synopsis{Creates a Rascal escaped string character from a given decimal index into the UTF8 table.} +public str makeStringChar(int ch) { + if (ch < 128) + return ascii[ch]; + else + if (ch >= 128 && ch <= 0xFFF) { + d1 = ch % 8; + r1 = ch / 8; + d2 = r1 % 8; + r2 = r1 / 8; + d3 = r2 % 8; + r3 = r2 / 8; + d4 = r3; + return "\\u"; + } + d1 = ch % 16; + r1 = ch / 16; + d2 = r1 % 16; + r2 = r1 / 16; + d3 = r2 % 16; + r3 = r2 / 16; + d4 = r3 % 16; + r4 = r3 / 16; + d5 = r4 % 16; + r5 = r4 / 16; + d6 = r5; + return "\\U"; +} + +test bool testA() = makeStringChar(97) == "a"; +test bool testNl() = makeStringChar(10) == "\\n"; +test bool testQuote() = makeStringChar(34) == "\\\""; +test bool testEOF() = makeStringChar(255) == "\\u0377"; +test bool testHex() = makeStringChar(0xABCDEF) == "\\UABCDEF"; + +@synopsis{Escapes the characters of the given string using the Rascal escaping conventions.} +public str escape(str s) { + if (s == "") + return s; + return ( "" | it + makeStringChar(charAt(s, i)) | i <- [0..size(s)] ); +} + +@synopsis{Escapes the characters of the given string using the Rascal escaping conventions. + and surround by " quotes.} +public str quote(str s) { + return "\"\""; +} + +@synopsis{Escapes the characters of the given string using the Rascal escaping conventions. + and surround by ' quotes.} +public str ciquote(str s) { + return "\'\'"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/format/Grammar.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Anya Helene Bagge - anya@ii.uib.no (Univ. Bergen)} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@synopsis{Convert the Rascal internal grammar representation format (Grammar) to + a syntax definition in Rascal source code.} +@pitfalls{ +This function does not use advanced formatting feature because it is a part of +components early in Rascal's bootstrapping and standard library construction cycle. +} +module lang::rascal::format::Grammar + +import ParseTree; +import Grammar; +import lang::rascal::grammar::definition::Characters; +import lang::rascal::grammar::definition::Literals; +import analysis::grammars::Dependency; +import lang::rascal::format::Escape; +import IO; +import Set; +import List; +import String; +import ValueIO; +import analysis::graphs::Graph; +import Relation; + +public void definition2disk(loc prefix, GrammarDefinition def) { + for (m <- def.modules) { + writeFile( + (prefix + "/" + visit(m) { + case /::/ => "/" + })[extension = ".rsc"], + module2rascal(def.modules[m]) + ); + } +} + +public str definition2rascal(GrammarDefinition def) { + return ( "" | it + "\n\n" | m <- def.modules ); +} + +public str module2rascal(GrammarModule m) { + return + "module + '<for (i <- m.imports) {>import ; + '<}> + '<for (i <- m.extends) {>extend ; + '<}> + '"; +} + +public str grammar2rascal(Grammar g, str name) { + return "module "; +} + +public str grammar2rascal(Grammar g) { + g = cleanIdentifiers(g); + deps = symbolDependencies(g); + ordered = order(deps); + unordered = [e | e <- (g.rules<0> - carrier(deps))]; + + //return " + // '"; + return grammar2rascal ( + g, + [] + ); +} + +private Grammar cleanIdentifiers(Grammar g) { + return + visit(g) { + case s: sort(/.*-.*/) => sort(replaceAll(s.name, "-", "_")) + case s: layouts(/.*-.*/) => layouts(replaceAll(s.name, "-", "_")) + case s: lex(/.*-.*/) => lex(replaceAll(s.name, "-", "_")) + case s: keywords(/.*-.*/) => keywords(replaceAll(s.name, "-", "_")) + case label(/-/, s) => label("\\
-", s)
+        }
+}
+
+public str grammar2rascal(Grammar g, list[Symbol] _/*nonterminals*/                                                   ) {
+    return "<for (nont <- g.rules) {>
+           '
+           '<}>";
+}
+
+bool same(Production p, Production q) {
+    return p.def == q.def;
+}
+
+public str topProd2rascal(Production p) {
+    if (regular(_) := p || p.def == empty() || p.def == \layouts("$default$"))
+        return "";
+    if (choice(nt, {q: priority(_, _), *r}) := p, r != {}) {
+        return "
+               '
+               '";
+    }
+    kind = "syntax";
+    if (/layouts(n) := p.def)
+        kind = "layout ";
+    else if (/lex(_) := p.def || /\parameterized-lex(_, _) := p.def)
+        kind = "lexical";
+    else
+        if (/keywords(_) := p.def)
+            kind = "keyword";
+    if (\start(_) := p.def)
+        kind = "start " + kind;
+    return "  =
+           '  
+           '  ;";
+}
+
+str layoutname(Symbol s) {
+    if (\layouts(str name) := s)
+        return name;
+    throw "unexpected ";
+}
+
+private
+str alt2r(Symbol _def, Production p, str sep = "=")
+    = "  ";
+public str alt2rascal(Production p: prod(def, _, _)) = alt2r(def, p);
+public
+str alt2rascal(Production p: priority(def, _))
+    = alt2r(def, p, sep = "\>");
+public str alt2rascal(Production p: \associativity(def, a, _)) {
+    sepVal = "= ";
+    // Compiler does not yet support string interpolation as keyword parameter expression
+    return alt2r(def, p, sep = sepVal);
+}
+
+public str alt2rascal(Production p: regular(_)) = symbol2rascal(p.def);
+public default str alt2rascal(Production p) {
+    throw "forgot 

"; +} + +public str prod2rascal(Production p) { + switch(p) { + case choice(_, alts): { + = takeOneFrom(alts); + return + "<for (pr: prod(_, _, _) <- rest) {> + '| <}><for (pr <- rest, prod(_, _, _) !:= pr) {> + '| <}>"; + } + case priority(_, alts): + return + "<for (pr <- tail(alts)) {> + '\> <}>"; + case associativity(_, a, alts): { + = takeOneFrom(alts); + return + " + ' ( <for (pr <- rest) {> + ' | <}> + ' )"; + } + + case prod(label(str n, Symbol _), list[Symbol] lhs, set[Attr] as): + return + "<for (a <- as) {> <}>: <for (s <- lhs) {> <}>"; + + case prod(Symbol _, list[Symbol] lhs, {}): + return "<for (s <- lhs) {> <}>"; + + case prod(Symbol _, list[Symbol] lhs, set[Attr] as): + return + "<for (a <- as) {> <}><for (s <- lhs) {> <}>"; + + case regular(_): + return ""; + + default: + throw "missed a case

"; + } +} + +str associativity(\left()) = "left"; +str associativity(\right()) = "right"; +str associativity(\assoc()) = "assoc"; +str associativity(\non-assoc()) = "non-assoc"; + +private set[str] rascalKeywords + = {"value", + "loc", + "node", + "num", + "type", + "bag", + "int", + "rat", + "rel", + "real", + "tuple", + "str", + "bool", + "void", + "datetime", + "set", + "map", + "list", + "int", + "break", + "continue", + "rat", + "true", + "bag", + "num", + "node", + "finally", + "private", + "real", + "list", + "fail", + "filter", + "if", + "tag", + "extend", + "append", + "rel", + "void", + "non-assoc", + "assoc", + "test", + "anno", + "layout", + "data", + "join", + "it", + "bracket", + "in", + "import", + "false", + "all", + "dynamic", + "solve", + "type", + "try", + "catch", + "notin", + "else", + "insert", + "switch", + "return", + "case", + "while", + "str", + "throws", + "visit", + "tuple", + "for", + "assert", + "loc", + "default", + "map", + "alias", + "any", + "module", + "mod", + "bool", + "public", + "one", + "throw", + "set", + "start", + "datetime", + "value"} ; + +public str reserved(str name) { + return name in rascalKeywords ? "\\" : name; +} + +test bool noAttrs() + = prod2rascal( + prod(sort("ID-TYPE"), [sort("PICO-ID"), lit(":"), sort("TYPE")], {}) + ) == "PICO-ID \":\" TYPE "; + +test bool AttrsAndCons() + = prod2rascal( + prod( + label("decl", sort("ID-TYPE")), + [sort("PICO-ID"), lit(":"), sort("TYPE")], + {} + ) + ) == "decl: PICO-ID \":\" TYPE "; + +test bool CC() + = prod2rascal( + prod( + label("whitespace", sort("LAYOUT")), + [\char-class([range(9, 9), range(10, 10), range(13, 13), range(32, 32)])], + {} + ) + ) == "whitespace: [\\t \\n \\a0D \\ ] "; + +test bool Prio() + = prod2rascal( + priority ( + sort("EXP"), + [ + prod(sort("EXP"), [sort("EXP"), lit("||"), sort("EXP")], {}), + prod(sort("EXP"), [sort("EXP"), lit("-"), sort("EXP")], {}), + prod(sort("EXP"), [sort("EXP"), lit("+"), sort("EXP")], {}) + ] + ) + ) == "EXP \"||\" EXP \n\> EXP \"-\" EXP \n\> EXP \"+\" EXP "; + +public str attr2mod(Attr a) { + switch(a) { + case \bracket(): + return "bracket"; + case \tag(str x(str y)): + return "@=\"\""; + case \tag(str x()): + return "@"; + case \assoc(Associativity as): + return associativity(as); + default: + return "@Unsupported(\"")>\")"; + } +} + +public str symbol2rascal(Symbol sym) { + switch(sym) { + case label(str l, x): + return " "; + case sort(x): + return x; + case lit(x): + return "\"\""; + case cilit(x): + return "\'\'"; + case \lex(x): + return x; + case \keywords(x): + return x; + case \parameterized-sort(str name, list[Symbol] parameters): + return "[]"; + case \parameterized-lex(str name, list[Symbol] parameters): + return "[]"; + case parameter(str t, _): + return "&"; + case \char-class(x): + if (\char-class(y) := complement(sym)) { + str norm = cc2rascal(x); + str comp = cc2rascal(y); + return size(norm) > size(comp) ? "!" : norm; + } + else + throw "weird result of character class complement"; + case \seq(syms): + return "( <for (s <- syms) {> <}> )"; + case opt(x): + return "?"; + case iter(x): + return "+"; + case \iter-star(x): + return "*"; + case \iter-seps(x, seps): + return iterseps2rascal(x, seps, "+"); + case \iter-star-seps(x, seps): + return iterseps2rascal(x, seps, "*"); + case alt(set[Symbol] alts): { + = takeOneFrom(alts); + return "(" + ( symbol2rascal(f) | "<it> | " | a <- as ) + ")"; + } + case seq(list[Symbol] ss): { + = takeOneFrom(ss); + return "(" + ( symbol2rascal(f) | "<it> " | a <- as ) + ")"; + } + case \layouts(str _): + return ""; + case \start(x): + return symbol2rascal(x); + + // Following are type-incorrect, PK. + //case intersection(lhs, rhs): + // return " && "; + //case union(lhs, rhs): + // return " || "; + //case difference(Class lhs, Class rhs): + // return " - "; + //case complement(Class lhs): + // return "!"; + case conditional(Symbol s, {Condition c, Condition d, *Condition r}): + return symbol2rascal(conditional(conditional(s, {c}), {d, *r})); + case conditional(s, {delete(t)}): + return " \\ "; + case conditional(s, {follow(t)}): + return " \>\> "; + case conditional(s, {\not-follow(t)}): + return " !\>\> "; + case conditional(s, {precede(t)}): + return " \<\< "; + case conditional(s, {\not-precede(t)}): + return " !\<\< "; + case conditional(s, {\at-column(int i)}): + return "@"; + case conditional(s, {\begin-of-line()}): + return "^"; + case conditional(s, {\end-of-line()}): + return "$"; + case conditional(s, {\except(str x)}): + return "!"; + case conditional(s, {}): { + println("WARNING: empty conditional "); + return symbol2rascal(s); + } + case empty(): + return "()"; + } + + throw "symbol2rascal: missing case "; +} + +public str iterseps2rascal(Symbol sym, list[Symbol] seps, str iter) { + separators = "<for (sp <- seps) {><}>"; + if (separators != "") + return "{ }"; + else + return ""; +} + +public str params2rascal(list[Symbol] params) { + len = size(params); + if (len == 0) + return ""; + if (len == 1) + return symbol2rascal(params[0]); + sep = ""; + res = ""; + for (Symbol p <- params) { + res += sep + symbol2rascal(p); + sep = ", "; + } + return res; +} + +public str cc2rascal(list[CharRange] ranges) { + if (ranges == []) + return "[]"; + return + "[<for (r <- tail(ranges)) {> <}>]"; +} + +public str range2rascal(CharRange r) { + switch(r) { + case range(c, c): + return makeCharClassChar(c); + case range(c, d): + return "-"; + + //TODO: + //case \empty-range(): + // return ""; + default: + throw "range2rascal: missing case "; + } +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/format/Rascal.rsc| +@license{ +Copyright (c) 2022, NWO-I Centrum Wiskunde & Informatica (CWI) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +} +@synopsis{Composes a default formatter for Rascal modules} +@description{ +This module composes and describes a "standard" formatting style for Rascal. +There could be other styles of course. Other styles can be build by +writing different `toBox` rules. + +TODO's: + // * HOV instead of HV for constructor parameters (this is probably a good thing) + * global variable assignment indentation too deep (has todo with visibility keywords?) + * alias has no spacing around = + * `public` indents function declarations too deeply if there is no function body `;` (see also globals) + // * parse error in IO output: + // * multiple empty lines between function definitions in IO.rsc + // * copyFile has is=2? + // * missing brackets around parameters in final closure calls + * parameter lists of function calls go to vertical too soon + * single line comment at end of line should not go to its own line + * if brackets go vertical of call syntax, parameters also always go vertical. not necessary. + * "if" can get "\{" below it (HV?) in some corner cases. Not good. + * do-while +} +@bootstrapParser +module lang::rascal::format::Rascal + +// by extending these modules we compose a `toBox` function +// which handles all relevant constructs of Rascal +extend lang::box::util::Tree2Box; +extend lang::rascal::\syntax::Rascal; + +import IO; +import ParseTree; +import String; +import analysis::diff::edits::ExecuteTextEdits; +import analysis::diff::edits::HiFiLayoutDiff; +import analysis::diff::edits::TextEdits; +import lang::box::\syntax::Box; +import lang::box::util::Box2Text; +import util::Formatters; +import util::Reflective; + +@synopsis{Format any Rascal module and dump the result as a string} +void debugFormatRascalFile( + loc \module, + bool console = false, + bool HTML = !console, + FormattingOptions opts = formattingOptions(), + bool dumpEdits = false +) { + debugFileFormat( + #start[Module], toBox, \module, + console = console, + HTML = HTML, + opts = opts, + dumpEdits = dumpEdits + ); +} + +void testOnLibrary() { + debugFilesFormat( + #start[Module], toBox, |project://rascal/src/org/rascalmpl/library/|, "rsc", + ansi = true, + shadowFiles = false, + appendFile = true, + console = false + ); +} + +/* Modules */Box toBox(Toplevel* toplevels) = toClusterBox(toplevels); + +Box toBox( + (Module) ` module ` +) + = V( + V(toBox(tags), H(L("module"), toBox(name))), toClusterBox(imports), toBox(body), + vs = 1 + ); + +Box toBox(Import* imports) = toClusterBox(imports); + +Box toBox((Import) `import ;`) + = H(L("import"), H0(toBox(m), L(";"))); + +Box toBox((Import) `extend ;`) + = H(L("extend"), H0(toBox(m), L(";"))); + +Box toBox((Visibility) ``) = NULL(); + +/* Syntax definitions */Box toBox( + (SyntaxDefinition) ` syntax = ;` +) + = (production is \all || production is \first) + ? V( + H(toBox(st), L("syntax"), toBox(defined)), + I(G(L("="), toBox(production), gs = 2, op = H([])), L(";")) + ) + : // single rule case + H( + toBox(st), + L("syntax"), + toBox(defined), + L("="), + H0(toBox(production), L(";")) + ); + +Box toBox( + (SyntaxDefinition) `lexical = ;` +) + = (production is \all || production is \first) + ? V( + H(L("lexical"), toBox(defined)), + I(G(L("="), toBox(production), gs = 2, op = H([])), L(";")) + ) + : // single rule case + H(L("lexical"), toBox(defined), L("="), H0(toBox(production), L(";"))); + +Box toBox( + (SyntaxDefinition) `keyword = ;` +) + = (production is \all || production is \first) + ? V( + H(L("keyword"), toBox(defined)), + I(G(L("="), toBox(production), gs = 2, op = H([])), L(";")) + ) + : // single rule case + H(L("keyword"), toBox(defined), L("="), H0(toBox(production), L(";"))); + +Box toBox( + (SyntaxDefinition) ` layout = ;` +) + = (production is \all || production is \first) + ? V( + H(toBox(v), L("layout"), toBox(defined)), + I(G(L("="), toBox(production), gs = 2, op = H([])), L(";")) + ) + : // single rule case + H( + toBox(v), + L("layout"), + toBox(defined), + L("="), + H0(toBox(production), L(";")) + ); + +Box toBox((Prod) ` | `) + = U(toBox(lhs), L("|"), toBox(rhs)); + +Box toBox((Prod) ` \> `) + = U(toBox(lhs), L("\>"), toBox(rhs)); + +Box toBox((Prod) `:`) = H0(L(":"), toBox(n)); + +Box toBox( + (Prod) ` : ` +) + = H([toBox(modifiers), H0(toBox(name), L(":")), *[toBox(s) | s <- syms]]); + +Box toBox((Prod) ` `) + = H([toBox(modifiers), *[toBox(s) | s <- syms]]); + +Box toBox((Prod) ` ()`) + = H(toBox(a), HOV(G(L("("), U(toBox(g)), L(")"), gs = 2, op = H([])))); + +/* symbols */Box toBox((Sym) `{ }*`) + = H0(L("{"), H1(toBox(e), toBox(sep)), L("}"), L("*")); +Box toBox((Sym) `{ }+`) + = H0(L("{"), H1(toBox(e), toBox(sep)), L("}"), L("+")); +Box toBox((Sym) `*`) = H0(toBox(e), L("*")); +Box toBox((Sym) `+`) = H0(toBox(e), L("+")); +Box toBox((Sym) `?`) = H0(toBox(e), L("?")); +Box toBox((Sym) `()`) = H0(L("("), L(")")); + +Box toBox((Sym) `( )`) + = H0(L("("), H1([toBox(first), *[toBox(e) | Sym e <- sequence]]), L(")")); + +Box toBox((Sym) `start[]`) + = H0(L("start"), L("["), toBox(s), L("]")); + +Box toBox((Sym) `( | <{Sym "|"}+ alternatives>)`) + = H0( + L("("), + H1([toBox(first), *[L("|"), toBox(e) | Sym e <- alternatives]]), + L(")") + ); + +Box toBox((Class) `[]`) + = H0([L("["), *[toBox(r) | r <- ranges], L("]")]); + +Box toBox((Range) ` - `) = H0(toBox(s), L("-"), toBox(e)); + +/* Declarations */Box toBox((QualifiedName) `<{Name "::"}+ names>`) = L(""); + +Box toBox((Tag) `@ `) + = H0(L("@"), toBox(n), toBox(contents)); + +Box toBox((Tag) `@ = `) + = H0(L("@"), toBox(n), L("="), toBox(exp)); + +Box toBox((Tag) `@`) = H0(L("@"), toBox(n)); + +Box toBox(QualifiedName n) = L(""); + +Box toBox( + (Declaration) ` alias = ;` +) + = V( + toBox(t), + H(toBox(v), L("alias"), toBox(user), L("="), H0(toBox(base), L(";"))) + ); + +Box toBox( + (Declaration) ` data ;` +) + = V(toBox(tg), H(toBox(v), L("data"), H0(toBox(typ), toBox(ps), L(";")))); + +Box toBox( + (Declaration) ` data = ;` +) + = HV( + V(toBox(tg), H(toBox(v), L("data"), H0(toBox(typ)), toBox(ps))), + I(H(L("="), H0(toBox(va), L(";")))) + ); + +Box toBox( + (Declaration) ` data = | <{Variant "|"}+ vs>;` +) + = V( + toBox(tg), + H(toBox(v), L("data"), H0(toBox(typ)), toBox(ps)), + I([G([L("="), toBox(va), *[L("|"), toBox(vax) | Variant vax <- vs]// hoist the bars `|` up to the same level of `=` + ]), L(";")]) + ); + +Box toBox( + (Declaration) ` = ;` +) + = HV( + V(toBox(tags), H1(toBox(visibility), toBox(typ), toBox(name))), + I(HOV(G(L("="), U([toBox(initial)])))), + L(";") + ); + +Box toBox( + (Declaration) ` , <{Variable ","}+ variables>;` +) + = HV( + V(toBox(tags), H1(toBox(visibility), toBox(typ))), + I(HOV(H0(toBox(first), L(",")), SL([toBox(v) | v <- variables], L(",")))), + L(";") + ); + +Box toBox((Declarator) ` `) + = H1(toBox(typ), toBox(name)); + +Box toBox( + (Declarator) ` = ` +) + = HV(H(toBox(typ), toBox(name)), I(toExpBox(L("="), initial))); + +Box toBox( + (Declarator) ` , <{Variable ","}+ variables>` +) + = HV( + I( + HOV( + H(toBox(typ), toBox(first)), L(","), SL([toBox(v) | v <- variables], L(",")) + ) + ) + ); + +Box toBox((CommonKeywordParameters) `(<{KeywordFormal ","}+ fs>)`) + = H0(L("("), HOV(toBox(fs)), L(")")); + +Box toBox( + (Variant) `(<{TypeArg ","}* args>, <{KeywordFormal ","}+ kws>)` +) + = HV( + H0(toBox(n), L("(")), HOV(I(H0(toBox(args), L(","))), I(toBox(kws)), hs = 1), L(")"), + hs = 0 + ); + +Box toBox((Variant) `(<{TypeArg ","}* args>)`) + = HV(H0(toBox(n), L("(")), I(toBox(args)), L(")"), hs = 0); + +Box toBox( + (Variant) `(<{TypeArg ","}* args> + '<{KeywordFormal ","}+ kws>)` +) + = HV( + H0(toBox(n), L("(")), HOV(I(H0(toBox(args))), I(toBox(kws)), hs = 1), L(")"), + hs = 0 + ); + +Box toBox(FunctionModifier* modifiers) = H([toBox(b) | b <- modifiers]); + +Box toBox( + (Signature) ` throws <{Type ","}+ exs>` +) + = HOV([ + H(toBox(modifiers), toBox(typ), H0(toBox(name), L("("))), + G(toBox(parameters), gs = 1, op = I()), + H([ L(")"), L("throws"), SL([toBox(e) | e <- exs], L(",")) ], hs = 1 ) + ], + hs = 0 + ); + +Box toBox( + (Signature) ` ` +) + = HOV( + H(toBox(modifiers), toBox(typ), H0(toBox(name), L("("))), G(toBox(parameters), gs = 1, op = I()), L(")"), + hs = 0 + ); + +Box toBox( + (FunctionDeclaration) ` ;` +) + = V(toBox(tags), HOV(toBox(vis), H0(toBox(sig), L(";")))); + +Box toBox( + (FunctionDeclaration) ` = ;` +) + = V( + toBox(tags), + HOV( + toBox(vis), + toBox(sig), + I(H(HOV(G(L("="), toBox(exp), gs = 2, op = H())), L(";"), hs = 0)) + ) + ) + when !(exp is \visit || exp is voidClosure || exp is closure); + +Box toBox( + (Expression) ` { }` +) + = HOV( + toBox(typ), + H0(L("("), HOV(G(toBox(parameters), gs = 1, op = I())), H(L(")")), L("{")), + I(V(toClusterBox(statements))), + L("}") + ); + +Box toBox( + (Expression) ` { }` +) + = HOV( + H0(L("("), HOV(G(toBox(parameters), gs = 1, op = I())), H(L(")"), L("{"))), + I(V(toClusterBox(statements))), + L("}") + ); + +Box toBox( + (FunctionDeclaration) ` = { };` +) + = V( + toBox(tags), + HOV( + toBox(vis), + toBox(sig), + I( + HOV( + H(L("="), H0(toBox(typ), L("("))), + G(toBox(parameters), gs = 1, op = I()), + H(L(")"), L("{")) + ) + ) + ), + I(V(toClusterBox(statements))), + H0(L("}"), L(";")) + ); + +Box toBox( + (FunctionDeclaration) ` = { };` +) + = V( + toBox(tags), + HOV( + toBox(vis), + toBox(sig), + I( + HOV( + H(L("="), L("(")), G(toBox(parameters), gs = 1, op = I()), H(L(")"), L("{")) + ) + ) + ), + I(V(toClusterBox(statements))), + H0(L("}"), L(";")) + ); + +Box toBox( + (FunctionDeclaration) ` =

"; + } +} + +private Production associativity(Symbol nt, nothing(), Production p) = p; +private +default Production associativity( + Symbol nt, just(Associativity a), Production p +) + = associativity(nt, a, {p}); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/definition/References.rsc| +@synopsis{This module implements the semantics of the ... and :cons notations in Rascal grammars. + + To give the proper semantics to priorities, this must be run before the priority relation + is expanded.} +module lang::rascal::grammar::definition::References + +extend Grammar; +extend ParseTree; +import lang::rascal::grammar::definition::Symbols; + +Grammar references(Grammar g) + = visit(g) { + case reference(Symbol s, str name) + => p + when ss := striprec(s), + ss in g.rules, + /Production p: prod(label(name, t), _, _) := g.rules[ss], + ss == striprec(t) + }; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/definition/Regular.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +module lang::rascal::grammar::definition::Regular + +import lang::rascal::grammar::definition::Modules; +import lang::rascal::grammar::definition::Productions; +import Grammar; +import ParseTree; + +public Grammar expandRegularSymbols(Grammar G) { + for (Symbol def <- G.rules) { + if (choice(def, {regular(def)}) := G.rules[def]) { + Production init = choice(def, {}); + + for (p <- expand(def)) { + G.rules[p.def] = choice(p.def, {p, G.rules[p.def] ? \init}); + } + } + } + return G; +} + +public set[Production] expand(Symbol s) { + switch(s) { + case \opt(t): + return + {choice( + s, {prod(label("absent", s), [], {}), prod(label("present", s), [t], {})} + )}; + case \iter(t): + return + {choice( + s, {prod(label("single", s), [t], {}), prod(label("multiple", s), [t, s], {})} + )}; + case \iter-star(t): + return + {choice( + s, + {prod(label("empty", s), [], {}), prod(label("nonEmpty", s), [\iter(t)], {})} + )} + + expand(\iter(t)); + case \iter-seps(t, list[Symbol] seps): + return + {choice( + s, + {prod(label("single", s), [t], {}), + prod(label("multiple", s), [t, *seps, s], {})} + )}; + case \iter-star-seps(t, list[Symbol] seps): + return + {choice( + s, + {prod(label("empty", s), [], {}), + prod(label("nonEmpty", s), [\iter-seps(t, seps)], {})} + )} + + expand(\iter-seps(t, seps)); + case \alt(set[Symbol] alts): + return {choice(s, {prod(s, [a], {})| a <- alts})}; + case \seq(list[Symbol] elems): + return {prod(s, elems, {})}; + case \empty(): + return {prod(s, [], {})}; + } + + throw "expand, missed a case "; +} + +public Grammar makeRegularStubs(Grammar g) { + prods = {g.rules[nont]| Symbol nont <- g.rules}; + stubs = makeRegularStubs(prods); + return compose(g, grammar({}, stubs)); +} + +public set[Production] makeRegularStubs(set[Production] prods) { + return + {regular(reg) + | /Production p: prod(_, _, _) <- prods, sym <- p.symbols, reg <- getRegular(sym) + }; +} + +private set[Symbol] getRegular(Symbol s) = {t| /Symbol t := s, isRegular(t)}; + +public default bool isRegular(Symbol s) = false; +public bool isRegular(opt(Symbol _)) = true; +public bool isRegular(iter(Symbol _)) = true; +public bool isRegular(\iter-star(Symbol _)) = true; +public bool isRegular(\iter-seps(Symbol _, list[Symbol] _)) = true; +public bool isRegular(\iter-star-seps(Symbol _, list[Symbol] _)) = true; +public bool isRegular(alt(set[Symbol] _)) = true; +public bool isRegular(seq(list[Symbol] _)) = true; +public bool isRegular(empty()) = true; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/definition/Symbols.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@bootstrapParser +module lang::rascal::grammar::definition::Symbols + +import lang::rascal::grammar::definition::Literals; +import lang::rascal::grammar::definition::Characters; +import lang::rascal::\syntax::Rascal; +import ParseTree; +import String; + +default Symbol striprec(Symbol s_ori) + = visit(s_ori) { + case label(str _, Symbol s) => strip(s) + case conditional(Symbol s, set[Condition] _) => strip(s) + }; + +//default Symbol striprec(Symbol s) = visit(s) { case Symbol t => strip(t) }; +Symbol strip(label(str _, Symbol s)) = strip(s); +Symbol strip(conditional(Symbol s, set[Condition] _)) = strip(s); +default Symbol strip(Symbol s) = s; + +public bool match(Symbol checked, Symbol referenced) { + while(checked is conditional || checked is label) + checked = checked.symbol; + while(referenced is conditional || referenced is label) + referenced = referenced.symbol; + + return referenced == checked; +} + +public Symbol delabel(Symbol s) = visit(s) { + case label(_, t) => t + }; + +public Symbol sym2symbol(Sym sym) { + switch(sym) { + case nonterminal(Nonterminal n): + return Symbol::sort(""); + case \start(Nonterminal n): + return Symbol::\start(sort("")); + case literal(StringConstant l): + return Symbol::lit(unescapeLiteral(l)); + case caseInsensitiveLiteral(CaseInsensitiveStringConstant l): + return Symbol::cilit(unescapeLiteral(l)); + case \parametrized(Nonterminal n, {Sym ","}+ syms): + return Symbol::\parameterized-sort("", separgs2symbols(syms)); + case labeled(Sym s, NonterminalLabel n): + return Symbol::label("", sym2symbol(s)); + case optional(Sym s): + return Symbol::opt(sym2symbol(s)); + case characterClass(Class cc): + return cc2ranges(cc); + case parameter(Nonterminal n): + return Symbol::\parameter("", adt ( + "Tree", + [] + )); + case empty(): + return Symbol::\empty(); + case alternative(Sym first, {Sym "|"}+ alts): + return alt({sym2symbol(first)} + {sym2symbol(elem)| elem <- alts}); + case iterStar(Sym s): + return Symbol::\iter-star(sym2symbol(s)); + case iter(Sym s): + return Symbol::\iter(sym2symbol(s)); + case iterStarSep(Sym s, Sym sep): + return Symbol::\iter-star-seps ( + sym2symbol(s), + [sym2symbol(sep)] + ); + case iterSep(Sym s, Sym sep): + return Symbol::\iter-seps ( + sym2symbol(s), + [sym2symbol(sep)] + ); + case sequence(Sym first, Sym+ sequence): + return seq([sym2symbol(first)] + [sym2symbol(elem) | elem <- sequence]); + case startOfLine(Sym s): + return conditional(sym2symbol(s), {\begin-of-line()}); + case endOfLine(Sym s): + return conditional(sym2symbol(s), {\end-of-line()}); + case column(Sym s, IntegerLiteral i): + return conditional(sym2symbol(s), {\at-column(toInt(""))}); + case follow(Sym s, Sym r): + return conditional(sym2symbol(s), {\follow(sym2symbol(r))}); + case notFollow(Sym s, Sym r): + return conditional(sym2symbol(s), {\not-follow(sym2symbol(r))}); + case precede(Sym s, Sym r): + return conditional(sym2symbol(r), {\precede(sym2symbol(s))}); + case notPrecede(Sym s, Sym r): + return conditional(sym2symbol(r), {\not-precede(sym2symbol(s))}); + case unequal(Sym s, Sym r): + return conditional(sym2symbol(s), {\delete(sym2symbol(r))}); + case except(Sym s, NonterminalLabel n): + return conditional(sym2symbol(s), {\except("")}); + default: + throw "sym2symbol, missed a case "; + } +} + +public list[Symbol] args2symbols(Sym* args) { + return [sym2symbol(s) | Sym s <- args]; +} + +public list[Symbol] separgs2symbols({Sym ","}+ args) { + return [sym2symbol(s) | Sym s <- args]; +} + +// flattening rules for regular expressions +public +Symbol \seq([*Symbol a, \seq(list[Symbol] b), *Symbol c]) + = \seq(a + b + c); + +public Symbol \alt({*Symbol a, \alt(set[Symbol] b)}) = \alt(a + b); + +// flattening for conditionals +public +Symbol \conditional( + \conditional(Symbol s, set[Condition] cs1), set[Condition] cs2 +) + = \conditional(s, cs1 + cs2); + +public Symbol \conditional(Symbol s, set[Condition] cs) { + // if there is a nested conditional, lift the nested conditions toplevel and make the nested symbol unconditional. + if (c <- cs, c has symbol, c.symbol is conditional) { + return + \conditional( + s, {c[symbol = c.symbol.symbol], *c.symbol.conditions, *(cs - {c})} + ); + //SPLICING + } + else + fail; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/storage/ModuleParserStorage.rsc| +@synopsis{Functionality for caching module parsers} +@description{ +The Rascal interpreter can take a lot of time while loading modules. +In particular in deployed situations (Eclipse and VScode plugins), the +time it takes to load the parser generator for generating the parsers +which are required for analyzing concrete syntax fragments is prohibitive (20s). +This means that the first syntax highlighting sometimes can only appear +after more than 20s after loading an extension (VScode) or plugin (Eclipse). + +This "compiler" takes any number of Rascal modules and extracts a grammar +for each of them, in order to use the ((ParseTree)) module's +functions ((storeParsers)) on them respectively to store each parser +in a `.parsers` file. + +After that the Rascal interpreter has a special mode for using ((loadParsers)) +while importing a new module if a cache `.parsers` file is present next to +the `.rsc` respective file. +} +@benefits{ +* loading modules without having to first load and use a parser generator can be up 1000 times faster. +} +@pitfalls{ +:::warning +This caching feature is _static_. There is no automated cache clearance. +If your grammars change, any saved `.parsers` files do not change with it. +It is advised that you programmatically execute this compiler at deployment time +to store the `.parsers` file _only_ in deployed `jar` files. That way, you can not +be bitten by a concrete syntax parser that is out of date at development time. +::: +} +@license{ + Copyright (c) 2009-2023 NWO-I CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@bootstrapParser +module lang::rascal::grammar::storage::ModuleParserStorage + +import lang::rascal::grammar::definition::Modules; +import lang::rascal::grammar::definition::Names; +import lang::rascal::grammar::definition::Layout; +import lang::rascal::\syntax::Rascal; +import util::Reflective; +import util::FileSystem; +import Location; +import ParseTree; +import Grammar; +import IO; +import Exception; + +@synopsis{For all modules in pcfg.srcs this will produce a `.parsers` stored parser capable of parsing concrete syntax fragment in said module.} +@description{ +Use ((loadParsers)) to retrieve the parsers stored by this function. In particular the +Rascal interpreter will use this instead of spinning up its own parser generator. +} +@benefits{ +* the single pathConfig parameter makes it easy to wire this function into Maven scripts (see generate-sources maven plugin) +* time spent here generating parsers, once, does not have to be spent while running IDE plugins, many times. +} +@pitfalls{ +* this compiler has very weak error reporting. it just crashes with stacktraces in case of trouble. +* for large projects running this can take a few minutes; it is slower than importing the same modules in the interpreter. +* this compiler assumes the grammars are all correct and can be used to parse the concrete syntax fragments in each respective module. +* this compiler may have slight differences in semantics with the way the interpreter composes grammars for modules, since +it is implemented differently. However, no such issues are currently known. +} +@examples{ +Typically you would call the generate-sources MOJO from the rascal-maven-plugin, in `pom.xml`, like so: + +```xml + + org.rascalmpl + rascal-maven-plugin + 0.14.6 + + YourMainModule + + + + it-compile + generate-test-sources + + generate-sources + + + + +``` + +And you'd write this module to make it work: + +```rascal +module YourMainModule + +import util::Reflective; +import lang::rascal::grammar::storage::ModuleParserStorage; + +int main(list[str] args) { + pcfg = getProjectPathConfig(|project://yourProject|); + storeParsersForModules(pcfg); +} +``` +} +void storeParsersForModules(PathConfig pcfg) { + storeParsersForModules( + {*find(src, "rsc")| src <- pcfg.srcs, bprintln("Crawling ")}, pcfg + ); +} + +void storeParsersForModules(set[loc] moduleFiles, PathConfig pcfg) { + storeParsersForModules( + {parseModule(m)| m <- moduleFiles, bprintln("Loading ")}, pcfg + ); +} + +void storeParsersForModules(set[Module] modules, PathConfig pcfg) { + for (m <- modules) { + storeParserForModule("", m@\loc, modules, pcfg); + } +} + +void storeParserForModule( + str main, loc file, set[Module] modules, PathConfig pcfg +) { + // this has to be done from scratch due to different ways combining layout definitions + // with import and extend. Each main module has a different grammar because of this. + def = modules2definition(main, modules); + + // here the layout semantics comes really into action + gr = resolve(fuse(layouts(def))); + + // find a file in the target folder to write to + target = pcfg.bin + relativize(pcfg.srcs, file)[extension = "parsers"].path; + + try { + println("Generating parser for

at "); + if (type[Tree] rt := type(sort("Tree"), gr.rules)) { + storeParsers(rt, target); + } + } + catch e: JavaCompilation( + str message, int line, int column, str source, list[loc] classpath + ): { + println( + "Generated parser could not be compiled: + ' grammar: + ' error : + ' pos : line: , column: + ' path : + ' source : \"\"" + ); + throw e; + } +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/CGrammar.rsc| +module lang::rascal::grammar::tests::CGrammar + +import IO; +import Grammar; +import ParseTree; +import lang::rascal::grammar::ParserGenerator; +import lang::rascal::grammar::Lookahead; +import util::Benchmark; +import util::Reflective; +import lang::rascal::grammar::tests::ParserGeneratorTests; + +public Grammar C + = grammar( + {sort("TranslationUnit")}, + ( + empty() : choice(empty(), {prod(empty(), [], {})}), + sort("MoreParameters") : choice( + sort("MoreParameters"), + {prod( + sort("MoreParameters"), [lit(","), layouts("LAYOUTLIST"), lit("...")], {} + )} + ), + sort("Specifier") : choice( + sort("Specifier"), + {prod( + label("typeSpecifier", sort("Specifier")), [sort("TypeSpecifier")], {} + ), + prod( + label("typeQualifier", sort("Specifier")), [sort("TypeQualifier")], {} + ), + prod( + label("storageClass", sort("Specifier")), [sort("StorageClass")], {} + )} + ), + sort("Declarator") : choice( + sort("Declarator"), + {priority ( + sort("Declarator"), + [ + choice( + sort("Declarator"), + {prod( + label("bracket", sort("Declarator")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("decl", sort("Declarator")), + layouts("LAYOUTLIST"), + lit(")") + ], + {\bracket()} + ), + prod( + label("functionDeclarator", sort("Declarator")), + [ + label("decl", sort("Declarator")), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("params", opt(sort("Parameters"))), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod(label("identifier", sort("Declarator")), [lex("Identifier")], {}), + prod( + label("arrayDeclarator", sort("Declarator")), + [ + label("decl", sort("Declarator")), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label("exp", opt(sort("Expression"))), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + )} + ), + prod( + label("pointerDeclarator", sort("Declarator")), + [ + lit("*"), + layouts("LAYOUTLIST"), + label( + "qualifiers", + \iter-star-seps ( + sort("TypeQualifier"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("decl", sort("Declarator")) + ], + {} + ) + ] + )} + ), + sort("TypeQualifier") : choice( + sort("TypeQualifier"), + {prod(sort("TypeQualifier"), [lit("volatile")], {}), + prod(sort("TypeQualifier"), [lit("const")], {})} + ), + sort("TranslationUnit") : choice( + sort("TranslationUnit"), + {prod( + sort("TranslationUnit"), + [\iter-seps ( + sort("ExternalDeclaration"), + [layouts("LAYOUTLIST")] + )], + {} + )} + ), + sort("AnonymousIdentifier") : choice( + sort("AnonymousIdentifier"), {prod(sort("AnonymousIdentifier"), [], {})} + ), + sort("FunctionDefinition") : choice( + sort("FunctionDefinition"), + {prod( + label("defaultFunctionDefinition", sort("FunctionDefinition")), + [ + label( + "specs", \iter-star-seps ( + sort("Specifier"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + sort("Declarator"), + layouts("LAYOUTLIST"), + \iter-star-seps ( + sort("Declaration"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + \iter-star-seps ( + sort("Declaration"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + )} + ), + lex("MultiLineCommentBodyToken") : choice( + lex("MultiLineCommentBodyToken"), + {prod( + lex("MultiLineCommentBodyToken"), + [\char-class([range(1, 41), range(43, 16777215)])], + {} + ), + prod(lex("MultiLineCommentBodyToken"), [lex("Asterisk")], {})} + ), + sort("NonCommaExpression") : choice( + sort("NonCommaExpression"), + {prod( + label("nonCommaExpression", sort("NonCommaExpression")), + [label("expr", sort("Expression"))], + {} + )} + ), + \start(sort("TranslationUnit")) : choice( + \start(sort("TranslationUnit")), + {prod( + \start(sort("TranslationUnit")), + [ + layouts("LAYOUTLIST"), + label("top", sort("TranslationUnit")), + layouts("LAYOUTLIST") + ], + {} + )} + ), + sort("Expression") : choice( + sort("Expression"), + {priority ( + sort("Expression"), + [ + choice( + sort("Expression"), + {prod( + sort("Expression"), + [lex("FloatingPointConstant")], + {\tag("category"("Constant"))} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("-\>"), + layouts("LAYOUTLIST"), + lex("Identifier") + ], + {} + ), + prod(label("variable", sort("Expression")), [lex("Identifier")], {}), + prod( + sort("Expression"), + [lex("HexadecimalConstant")], + {\tag("category"("Constant"))} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + \iter-star-seps ( + sort("NonCommaExpression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("."), + layouts("LAYOUTLIST"), + lex("Identifier") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + sort("Expression"), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + sort("Expression"), + [sort("Expression"), layouts("LAYOUTLIST"), lit("--")], + {} + ), + prod( + sort("Expression"), + [lex("IntegerConstant")], + {\tag("category"("Constant"))} + ), + prod( + sort("Expression"), + [lex("StringConstant")], + {\tag("category"("Constant"))} + ), + prod( + sort("Expression"), + [ + lit("sizeof"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + sort("TypeName"), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + sort("Expression"), + [sort("Expression"), layouts("LAYOUTLIST"), lit("++")], + {} + ), + prod( + label("bracket", sort("Expression")), + [ + lit("("), + layouts("LAYOUTLIST"), + sort("Expression"), + layouts("LAYOUTLIST"), + lit(")") + ], + {\bracket()} + ), + prod( + sort("Expression"), + [lex("CharacterConstant")], + {\tag("category"("Constant"))} + )} + ), + choice( + sort("Expression"), + {prod( + sort("Expression"), + [lit("~"), layouts("LAYOUTLIST"), sort("Expression")], + {} + ), + prod( + label("sizeOfExpression", sort("Expression")), + [lit("sizeof"), layouts("LAYOUTLIST"), label("exp", sort("Expression"))], + {} + ), + prod( + sort("Expression"), + [ + conditional(lit("--"), {\not-precede(\char-class([range(45, 45)]))}), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + conditional(lit("++"), {\not-precede(\char-class([range(43, 43)]))}), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [lit("-"), layouts("LAYOUTLIST"), sort("Expression")], + {} + ), + prod( + sort("Expression"), + [lit("+"), layouts("LAYOUTLIST"), sort("Expression")], + {} + ), + prod( + sort("Expression"), + [lit("*"), layouts("LAYOUTLIST"), sort("Expression")], + {} + ), + prod( + sort("Expression"), + [ + lit("("), + layouts("LAYOUTLIST"), + sort("TypeName"), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [lit("!"), layouts("LAYOUTLIST"), sort("Expression")], + {} + ), + prod( + sort("Expression"), + [lit("&"), layouts("LAYOUTLIST"), sort("Expression")], + {} + )} + ), + associativity( + sort("Expression"), + left(), + {prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("%"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("/"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + label("multiplicationExpression", sort("Expression")), + [ + label("lexp", sort("Expression")), + layouts("LAYOUTLIST"), + lit("*"), + layouts("LAYOUTLIST"), + label("rexp", sort("Expression")) + ], + {} + )} + ), + associativity( + sort("Expression"), + left(), + {prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("+"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("-"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + )} + ), + associativity( + sort("Expression"), + left(), + {prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("\<\<"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("\>\>"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + )} + ), + associativity( + sort("Expression"), + left(), + {prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("\>="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("\<="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("\>"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("\<"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + )} + ), + associativity( + sort("Expression"), + left(), + {prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("=="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("!="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + )} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("&"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("^"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("|"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("&&"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("||"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("?"), + layouts("LAYOUTLIST"), + sort("Expression"), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + ), + associativity( + sort("Expression"), + right(), + {prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("\>\>="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("+="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("*="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("-="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("^="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("\n\t | ="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("/="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("&="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("%="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + ), + prod( + sort("Expression"), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit("\<\<="), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {\assoc(right())} + )} + ), + prod( + label("commaExpression", sort("Expression")), + [ + sort("Expression"), + layouts("LAYOUTLIST"), + lit(","), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ) + ] + )} + ), + layouts("$default$") : choice(layouts("$default$"), {prod(layouts("$default$"), [], {})}), + lex("Asterisk") : choice( + lex("Asterisk"), + {prod( + lex("Asterisk"), + [ + conditional( + \char-class([range(42, 42)]), + {\not-follow(\char-class([range(47, 47)]))} + ) + ], + {} + )} + ), + sort("Declaration") : choice( + sort("Declaration"), + {prod( + label("declarationWithoutInitDecls", sort("Declaration")), + [ + label("specs", \iter-seps ( + sort("Specifier"), + [layouts("LAYOUTLIST")] + )), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + label("declarationWithInitDecls", sort("Declaration")), + [ + label("specs", \iter-seps ( + sort("Specifier"), + [layouts("LAYOUTLIST")] + )), + layouts("LAYOUTLIST"), + label( + "initDeclarators", + \iter-seps ( + sort("InitDeclarator"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + )} + ), + sort("StorageClass") : choice( + sort("StorageClass"), + {prod(sort("StorageClass"), [lit("auto")], {}), + prod(label("typeDef", sort("StorageClass")), [lit("typedef")], {}), + prod(sort("StorageClass"), [lit("extern")], {}), + prod(sort("StorageClass"), [lit("static")], {}), + prod(sort("StorageClass"), [lit("register")], {})} + ), + lex("StringConstant") : choice( + lex("StringConstant"), + {prod( + lex("StringConstant"), + [ + opt(\char-class([range(76, 76)])), + \char-class([range(34, 34)]), + \iter-star(lex("StringConstantContent")), + \char-class([range(34, 34)]) + ], + {} + )} + ), + sort("AbstractDeclarator") : choice( + sort("AbstractDeclarator"), + {priority ( + sort("AbstractDeclarator"), + [ + choice( + sort("AbstractDeclarator"), + {prod( + label("bracket", sort("AbstractDeclarator")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("decl", sort("AbstractDeclarator")), + layouts("LAYOUTLIST"), + lit(")") + ], + {\bracket()} + ), + prod( + label("arrayDeclarator", sort("AbstractDeclarator")), + [ + label("decl", sort("AbstractDeclarator")), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label("exp", opt(sort("Expression"))), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("functionDeclarator", sort("AbstractDeclarator")), + [ + label("decl", sort("AbstractDeclarator")), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("params", opt(sort("Parameters"))), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("identifier", sort("AbstractDeclarator")), + [sort("AnonymousIdentifier")], + {} + )} + ), + prod( + label("pointerDeclarator", sort("AbstractDeclarator")), + [ + lit("*"), + layouts("LAYOUTLIST"), + label( + "qualifiers", + \iter-star-seps ( + sort("TypeQualifier"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("decl", sort("AbstractDeclarator")) + ], + {} + ) + ] + )} + ), + sort("PrototypeDeclarator") : choice( + sort("PrototypeDeclarator"), + {priority ( + sort("PrototypeDeclarator"), + [ + choice( + sort("PrototypeDeclarator"), + {prod( + label("identifier", sort("PrototypeDeclarator")), + [lex("Identifier")], + {} + ), + prod( + label("arrayDeclarator", sort("PrototypeDeclarator")), + [ + label("proto_decl", sort("PrototypeDeclarator")), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label("exp", opt(sort("Expression"))), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("functionDeclarator", sort("PrototypeDeclarator")), + [ + label("proto_decl", sort("PrototypeDeclarator")), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("params", opt(sort("PrototypeParameters"))), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("bracket", sort("PrototypeDeclarator")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("abs_decl", sort("AbstractDeclarator")), + layouts("LAYOUTLIST"), + lit(")") + ], + {\bracket()} + )} + ), + prod( + label("pointerDeclarator", sort("PrototypeDeclarator")), + [ + lit("*"), + layouts("LAYOUTLIST"), + label( + "qualifiers", + \iter-star-seps ( + sort("TypeQualifier"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("decl", sort("PrototypeDeclarator")) + ], + {} + ) + ] + )} + ), + layouts("LAYOUTLIST") : choice( + layouts("LAYOUTLIST"), + {prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow(\char-class([range(9, 10), range(13, 13), range(32, 32)]))} + ) + ], + {} + )} + ), + sort("GlobalDeclaration") : choice( + sort("GlobalDeclaration"), + {prod( + label("globalDeclarationWithInitDecls", sort("GlobalDeclaration")), + [ + label( + "specs0", \iter-star-seps ( + sort("Specifier"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label( + "initDeclarators", + \iter-seps ( + sort("InitDeclarator"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + label("globalDeclarationWithoutInitDecls", sort("GlobalDeclaration")), + [ + label("specs1", \iter-seps ( + sort("Specifier"), + [layouts("LAYOUTLIST")] + )), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + )} + ), + lex("Comment") : choice( + lex("Comment"), + {prod( + lex("Comment"), + [ + lit("//"), + \iter-star(\char-class([range(1, 9), range(11, 16777215)])), + \char-class([range(10, 10)]) + ], + {} + ), + prod( + lex("Comment"), + [ + \char-class([range(47, 47)]), + \char-class([range(42, 42)]), + \iter-star(lex("MultiLineCommentBodyToken")), + \char-class([range(42, 42)]), + \char-class([range(47, 47)]) + ], + {} + )} + ), + sort("Parameter") : choice( + sort("Parameter"), + {prod( + sort("Parameter"), + [ + \iter-star-seps ( + sort("Specifier"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + sort("Declarator") + ], + {} + )} + ), + keywords("Keyword") : choice( + keywords("Keyword"), + {prod(keywords("Keyword"), [lit("extern")], {}), + prod(keywords("Keyword"), [lit("int")], {}), + prod(keywords("Keyword"), [lit("signed")], {}), + prod(keywords("Keyword"), [lit("char")], {}), + prod(keywords("Keyword"), [lit("const")], {}), + prod(keywords("Keyword"), [lit("typedef")], {}), + prod(keywords("Keyword"), [lit("register")], {}), + prod(keywords("Keyword"), [lit("union")], {}), + prod(keywords("Keyword"), [lit("unsigned")], {}), + prod(keywords("Keyword"), [lit("auto")], {}), + prod(keywords("Keyword"), [lit("goto")], {}), + prod(keywords("Keyword"), [lit("do")], {}), + prod(keywords("Keyword"), [lit("continue")], {}), + prod(keywords("Keyword"), [lit("for")], {}), + prod(keywords("Keyword"), [lit("break")], {}), + prod(keywords("Keyword"), [lit("short")], {}), + prod(keywords("Keyword"), [lit("double")], {}), + prod(keywords("Keyword"), [lit("struct")], {}), + prod(keywords("Keyword"), [lit("case")], {}), + prod(keywords("Keyword"), [lit("while")], {}), + prod(keywords("Keyword"), [lit("switch")], {}), + prod(keywords("Keyword"), [lit("default")], {}), + prod(keywords("Keyword"), [lit("float")], {}), + prod(keywords("Keyword"), [lit("long")], {}), + prod(keywords("Keyword"), [lit("static")], {}), + prod(keywords("Keyword"), [lit("sizeof")], {}), + prod(keywords("Keyword"), [lit("volatile")], {}), + prod(keywords("Keyword"), [lit("void")], {}), + prod(keywords("Keyword"), [lit("enum")], {}), + prod(keywords("Keyword"), [lit("if")], {}), + prod(keywords("Keyword"), [lit("return")], {}), + prod(keywords("Keyword"), [lit("else")], {})} + ), + sort("Parameters") : choice( + sort("Parameters"), + {prod( + sort("Parameters"), + [ + \iter-seps ( + sort("Parameter"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + opt(sort("MoreParameters")) + ], + {} + ), + prod(sort("Parameters"), [lit("void")], {})} + ), + lex("IntegerConstant") : choice( + lex("IntegerConstant"), + {prod( + lex("IntegerConstant"), + [ + iter(\char-class([range(48, 57)])), + conditional( + \iter-star( + \char-class([ + range(76, 76), range(85, 85), range(108, 108), range(117, 117) + ]) + ), + {\not-follow(\char-class([range(48, 57)]))} + ) + ], + {} + )} + ), + sort("FunctionPrototype") : choice( + sort("FunctionPrototype"), + {prod( + label("defaultFunctionPrototype", sort("FunctionPrototype")), + [ + label( + "specs", \iter-star-seps ( + sort("Specifier"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("decl", sort("PrototypeDeclarator")), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + )} + ), + lex("StringConstantContent") : choice( + lex("StringConstantContent"), + {prod( + lex("StringConstantContent"), + [\char-class([range(92, 92)]), \char-class([range(1, 16777215)])], + {} + ), + prod( + lex("StringConstantContent"), + [\char-class([range(1, 33), range(35, 91), range(93, 16777215)])], + {} + )} + ), + sort("PrototypeParameters") : choice( + sort("PrototypeParameters"), + {prod( + sort("PrototypeParameters"), + [ + \iter-seps ( + sort("PrototypeParameter"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + opt(sort("MoreParameters")) + ], + {} + ), + prod(sort("PrototypeParameters"), [lit("void")], {})} + ), + sort("Statement") : choice( + sort("Statement"), + {prod( + sort("Statement"), + [sort("Expression"), layouts("LAYOUTLIST"), lit(";")], + {} + ), + prod( + sort("Statement"), + [ + lit("{"), + layouts("LAYOUTLIST"), + \iter-star-seps ( + sort("Declaration"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + sort("Statement"), + [ + lit("case"), + layouts("LAYOUTLIST"), + sort("Expression"), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + sort("Statement") + ], + {} + ), + prod( + sort("Statement"), + [ + lit("return"), + layouts("LAYOUTLIST"), + sort("Expression"), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + sort("Statement"), + [ + lit("if"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + sort("Expression"), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + sort("Statement"), + layouts("LAYOUTLIST"), + lit("else"), + layouts("LAYOUTLIST"), + sort("Statement") + ], + {} + ), + prod( + sort("Statement"), + [ + lex("Identifier"), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + sort("Statement") + ], + {} + ), + prod( + sort("Statement"), [lit("break"), layouts("LAYOUTLIST"), lit(";")], {} + ), + prod(sort("Statement"), [lit(";")], {}), + prod( + sort("Statement"), [lit("continue"), layouts("LAYOUTLIST"), lit(";")], {} + ), + prod( + sort("Statement"), + [ + lit("if"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + sort("Expression"), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + sort("Statement") + ], + {} + ), + prod( + sort("Statement"), + [ + lit("while"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + sort("Expression"), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + sort("Statement") + ], + {} + ), + prod( + sort("Statement"), + [ + lit("goto"), + layouts("LAYOUTLIST"), + lex("Identifier"), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + sort("Statement"), + [ + lit("do"), + layouts("LAYOUTLIST"), + sort("Statement"), + layouts("LAYOUTLIST"), + lit("while"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + sort("Expression"), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + sort("Statement"), + [ + lit("for"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + opt(sort("Expression")), + layouts("LAYOUTLIST"), + lit(";"), + layouts("LAYOUTLIST"), + opt(sort("Expression")), + layouts("LAYOUTLIST"), + lit(";"), + layouts("LAYOUTLIST"), + opt(sort("Expression")), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + sort("Statement") + ], + {} + ), + prod( + sort("Statement"), [lit("return"), layouts("LAYOUTLIST"), lit(";")], {} + ), + prod( + sort("Statement"), + [ + lit("switch"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + sort("Expression"), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + sort("Statement") + ], + {} + ), + prod( + sort("Statement"), + [ + lit("default"), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + sort("Statement") + ], + {} + )} + ), + sort("ExternalDeclaration") : choice( + sort("ExternalDeclaration"), + {prod(sort("ExternalDeclaration"), [sort("FunctionDefinition")], {}), + prod(sort("ExternalDeclaration"), [sort("GlobalDeclaration")], {}), + prod(sort("ExternalDeclaration"), [sort("FunctionPrototype")], {})} + ), + sort("InitDeclarator") : choice( + sort("InitDeclarator"), + {prod(sort("InitDeclarator"), [label("decl", sort("Declarator"))], {}), + prod( + sort("InitDeclarator"), + [ + label("decl", sort("Declarator")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + sort("Initializer") + ], + {} + )} + ), + lex("Identifier") : choice( + lex("Identifier"), + {prod( + lex("Identifier"), + [ + conditional( + seq([ + \char-class([range(65, 90), range(95, 95), range(97, 122)]), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]), + {delete(keywords("Keyword"))} + ) + ], + {} + )} + ), + sort("Initializer") : choice( + sort("Initializer"), + {prod( + sort("Initializer"), + [ + lit("{"), + layouts("LAYOUTLIST"), + \iter-seps ( + sort("Initializer"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + opt(lit(",")), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod(sort("Initializer"), [sort("NonCommaExpression")], {})} + ), + lex("LAYOUT") : choice( + lex("LAYOUT"), + {prod( + label("whitespace", lex("LAYOUT")), + [\char-class([range(9, 10), range(13, 13), range(32, 32)])], + {} + ), + prod( + label("comment", lex("LAYOUT")), + [lex("Comment")], + {\tag("category"("Comment"))} + )} + ), + lex("CharacterConstant") : choice( + lex("CharacterConstant"), + {prod( + lex("CharacterConstant"), + [ + opt(\char-class([range(76, 76)])), + \char-class([range(39, 39)]), + iter(lex("CharacterConstantContent")), + \char-class([range(39, 39)]) + ], + {} + )} + ), + sort("StructDeclaration") : choice( + sort("StructDeclaration"), + {prod( + label("structDeclWithDecl", sort("StructDeclaration")), + [ + label("specs", \iter-seps ( + sort("Specifier"), + [layouts("LAYOUTLIST")] + )), + layouts("LAYOUTLIST"), + \iter-seps ( + sort("StructDeclarator"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + label("structDeclWithoutDecl", sort("StructDeclaration")), + [ + label("specs", \iter-seps ( + sort("Specifier"), + [layouts("LAYOUTLIST")] + )), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + )} + ), + sort("TypeName") : choice( + sort("TypeName"), + {prod( + sort("TypeName"), + [ + \iter-seps ( + sort("Specifier"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + sort("AbstractDeclarator") + ], + {} + )} + ), + lex("CharacterConstantContent") : choice( + lex("CharacterConstantContent"), + {prod( + lex("CharacterConstantContent"), + [\char-class([range(1, 38), range(40, 91), range(93, 16777215)])], + {} + ), + prod( + lex("CharacterConstantContent"), + [\char-class([range(92, 92)]), \char-class([range(1, 16777215)])], + {} + )} + ), + sort("Enumerator") : choice( + sort("Enumerator"), + {prod(sort("Enumerator"), [lex("Identifier")], {}), + prod( + sort("Enumerator"), + [ + lex("Identifier"), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + sort("NonCommaExpression") + ], + {} + )} + ), + sort("TypeSpecifier") : choice( + sort("TypeSpecifier"), + {prod(label("char", sort("TypeSpecifier")), [lit("char")], {}), + prod(sort("TypeSpecifier"), [lit("unsigned")], {}), + prod( + label("struct", sort("TypeSpecifier")), + [lit("struct"), layouts("LAYOUTLIST"), lex("Identifier")], + {} + ), + prod( + label("enumDecl", sort("TypeSpecifier")), + [ + lit("enum"), + layouts("LAYOUTLIST"), + lex("Identifier"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + \iter-seps ( + sort("Enumerator"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("identifier", sort("TypeSpecifier")), [lex("Identifier")], {} + ), + prod(label("void", sort("TypeSpecifier")), [lit("void")], {}), + prod(label("int", sort("TypeSpecifier")), [lit("int")], {}), + prod( + label("union", sort("TypeSpecifier")), + [lit("union"), layouts("LAYOUTLIST"), lex("Identifier")], + {} + ), + prod( + label("structDecl", sort("TypeSpecifier")), + [ + lit("struct"), + layouts("LAYOUTLIST"), + lex("Identifier"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + \iter-star-seps ( + sort("StructDeclaration"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod(label("float", sort("TypeSpecifier")), [lit("float")], {}), + prod( + label("enumAnonDecl", sort("TypeSpecifier")), + [ + lit("enum"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + \iter-seps ( + sort("Enumerator"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod(label("long", sort("TypeSpecifier")), [lit("long")], {}), + prod(label("double", sort("TypeSpecifier")), [lit("double")], {}), + prod( + label("unionAnonDecl", sort("TypeSpecifier")), + [ + lit("union"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + \iter-star-seps ( + sort("StructDeclaration"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("enum", sort("TypeSpecifier")), + [lit("enum"), layouts("LAYOUTLIST"), lex("Identifier")], + {} + ), + prod(sort("TypeSpecifier"), [lit("signed")], {}), + prod( + label("unionDecl", sort("TypeSpecifier")), + [ + lit("union"), + layouts("LAYOUTLIST"), + lex("Identifier"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + \iter-star-seps ( + sort("StructDeclaration"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod(label("short", sort("TypeSpecifier")), [lit("short")], {}), + prod( + label("structAnonDecl", sort("TypeSpecifier")), + [ + lit("struct"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + \iter-star-seps ( + sort("StructDeclaration"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + )} + ), + lex("HexadecimalConstant") : choice( + lex("HexadecimalConstant"), + {prod( + lex("HexadecimalConstant"), + [ + \char-class([range(48, 48)]), + \char-class([range(88, 88), range(120, 120)]), + iter(\char-class([range(48, 57), range(65, 70), range(97, 102)])), + conditional( + \iter-star( + \char-class([ + range(76, 76), range(85, 85), range(108, 108), range(117, 117) + ]) + ), + {\not-follow(\char-class([range(48, 57), range(65, 70), range(97, 102)]))} + ) + ], + {} + )} + ), + lex("Exponent") : choice( + lex("Exponent"), + {prod( + lex("Exponent"), + [ + \char-class([range(69, 69), range(101, 101)]), + opt(\char-class([range(43, 43), range(45, 45)])), + conditional( + iter(\char-class([range(48, 57)])), + {\not-follow(\char-class([range(48, 57)]))} + ) + ], + {} + )} + ), + sort("PrototypeParameter") : choice( + sort("PrototypeParameter"), + {prod( + sort("PrototypeParameter"), + [ + \iter-star-seps ( + sort("Specifier"), + [layouts("LAYOUTLIST")] + ), + layouts("LAYOUTLIST"), + sort("AbstractDeclarator") + ], + {} + )} + ), + lex("FloatingPointConstant") : choice( + lex("FloatingPointConstant"), + {prod( + lex("FloatingPointConstant"), + [ + \iter-star(\char-class([range(48, 57)])), + \char-class([range(46, 46)]), + conditional( + iter(\char-class([range(48, 57)])), + {\not-follow(\char-class([range(48, 57)]))} + ), + opt(lex("Exponent")), + opt( + \char-class([ + range(70, 70), range(76, 76), range(102, 102), range(108, 108) + ]) + ) + ], + {} + ), + prod( + lex("FloatingPointConstant"), + [ + iter(\char-class([range(48, 57)])), + \char-class([range(46, 46)]), + opt(lex("Exponent")), + opt( + \char-class([ + range(70, 70), range(76, 76), range(102, 102), range(108, 108) + ]) + ) + ], + {} + ), + prod( + lex("FloatingPointConstant"), + [ + iter(\char-class([range(48, 57)])), + lex("Exponent"), + opt( + \char-class([ + range(70, 70), range(76, 76), range(102, 102), range(108, 108) + ]) + ) + ], + {} + )} + ), + sort("StructDeclarator") : choice( + sort("StructDeclarator"), + {prod( + sort("StructDeclarator"), + [ + opt(sort("Declarator")), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + sort("Expression") + ], + {} + ), + prod(sort("StructDeclarator"), [sort("Declarator")], {})} + ) + ) + ) ; + +loc CParserLoc + = |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/| + + "generated_parsers/CParser.java.gz" ; + +str generateCParser() + = newGenerate( + "org.rascalmpl.library.lang.rascal.grammar.tests.generated_parsers", + "CParser", + C + ); + +void generateAndWriteCParser() { + writeFile(CParserLoc, generateCParser()); +} + +void warmup() { + for (_ <- [0..10]) + generateCParser(); +} + +int generateAndTimeCParser() { + warmup(); + t = cpuTime(); + generateCParser(); + used = (cpuTime() - t) / 1000000; + println("GenerateAndTimeCParser: ms"); + return used; +} + +value main() { + return generateAndTimeCParser(); +} + +test bool tstGenerateCParser() + = sameLines(generateCParser(), readFile(CParserLoc)); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/CharactersTests.rsc| +module lang::rascal::grammar::tests::CharactersTests + +import lang::rascal::grammar::definition::Characters; +import ParseTree; +import String; + +test bool testFlip() + = \new-char-class([range(2, 2), range(1, 1)]) == \char-class([range(1, 2)]); +test bool testMerge() + = \new-char-class([range(3, 4), range(2, 2), range(1, 1)]) == \char-class([range(1, 4)]); +test bool testEnvelop() + = \new-char-class([range(10, 20), range(15, 20), range(20, 30)]) == \char-class([range(10, 30)]); +test bool testEnvelop2() + = \new-char-class([range(10, 20), range(10, 19), range(20, 30)]) == \char-class([range(10, 30)]); + +test bool testComp() + = complement(\char-class([])) == \char-class([range(1, 1114111)]); +test bool testComp2() + = complement(\char-class([range(0, 0)])) == \char-class([range(1, 1114111)]); +test bool testComp3() + = complement(\char-class([range(1, 1)])) == \char-class([range(2, 1114111)]); +test bool testComp4() + = complement(\char-class([range(10, 20), range(30, 40)])) == \char-class([range(1, 9), range(21, 29), range(41, 1114111)]); +test bool testComp5() + = complement(\char-class([range(10, 35), range(30, 40)])) == \char-class([range(1, 9), range(41, 1114111)]); + +test bool testUnion1() + = union(\char-class([range(10, 20)]), \char-class([range(30, 40)])) == \char-class([range(10, 20), range(30, 40)]); +test bool testUnion2() + = union(\char-class([range(10, 25)]), \char-class([range(20, 40)])) == \char-class([range(10, 40)]); + +test bool testInter1() + = intersection(\char-class([range(10, 20)]), \char-class([range(30, 40)])) == \char-class([]); +test bool testInter2() + = intersection(\char-class([range(10, 25)]), \char-class([range(20, 40)])) == \char-class([range(20, 25)]); + +test bool testDiff1() + = difference(\char-class([range(10, 30)]), \char-class([range(20, 25)])) == \char-class([range(10, 19), range(26, 30)]); +test bool testDiff2() + = difference( + \char-class([range(10, 30), range(40, 50)]), \char-class([range(25, 45)]) + ) == \char-class([range(10, 24), range(46, 50)]); + +test bool asciiEscape() + = \char-class([range(0, 127)]) == #[\a00-\a7F].symbol; +test bool utf16Escape() + = \char-class([range(0, 65535)]) == #[\u0000-\uFFFF].symbol; +test bool utf32Escape() + = \char-class([range(0, 1114111)]) == #[\U000000-\U10FFFF].symbol; +test bool highLowSurrogateRange1() + = \char-class([range(9312, 12991)]) == #[①-㊿].symbol; +test bool highLowSurrogateRange2() + = \char-class([range(127829, 127829)]) == #[🍕].symbol; +test bool differentEscapesSameResult1() = #[\a00-\a7F] == #[\u0000-\u007F]; +test bool differentEscapesSameResult2() + = #[\a00-\a7F] == #[\U000000-\U00007F]; + +/* to avoid a known ambiguity */alias NotAZ = ![A-Z]; + +test bool unicodeCharacterClassSubtype1() { + Tree t = char(charAt("⑭", 0)); + + if ([①-㊿] circled := t) { + assert [⑭] _ := circled; + assert NotAZ _ := circled; + return true; + } + return false; +} + +test bool unicodeCharacterClassSubtype2() { + Tree t = char(charAt("🍕", 0)); + + if ([🍕] pizza := t) { + assert [\a00-🍕] _ := pizza; + assert NotAZ _ := pizza; + return true; + } + return false; +} + +test bool literalAsciiEscape1() = lit("\n") == #"\a0A".symbol; +test bool literalAsciiEscape2() = lit("w") == #"\a77".symbol; +test bool literalAsciiEscape3() = lit("\f") == #"\a0C".symbol; +test bool literalAsciiEscape4() = lit("\n") == #"\n".symbol; +@ignore{vallang must re-introduce the \f notation} +test bool literalAsciiEscape5() = lit("\f") == #"\f".symbol; +test bool literalUtf16Escape() = lit("\n") == #"\u000A".symbol; +test bool literalUtf32Escape1() = lit("\n") == #"\U00000A".symbol; +test bool literalUtf32Escape2() = lit("🍕") == #"\U01F355".symbol; + +test bool ciliteralAsciiEscape1() = cilit("\n") == #'\a0A'.symbol; +test bool ciliteralAsciiEscape2() = cilit("w") == #'\a77'.symbol; +test bool ciliteralAsciiEscape3() = cilit("\f") == #'\a0C'.symbol; +test bool ciliteralAsciiEscape4() = cilit("\n") == #'\n'.symbol; +@ignore{vallang must re-introduce the \f notation} +test bool ciliteralAsciiEscape5() = cilit("\f") == #'\f'.symbol; +test bool ciliteralUtf16Escape() = cilit("\n") == #'\u000A'.symbol; +test bool ciliteralUtf32Escape1() = cilit("\n") == #'\U00000A'.symbol; +test bool ciliteralUtf32Escape2() = cilit("🍕") == #'\U01F355'.symbol; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/Compare.rsc| +module lang::rascal::grammar::tests::Compare + +import IO; +import List; +import String; +import Set; + +value main() = compare3(); + +value compare1() { + INT + = split( + "\n", + readFile( + |std:///lang/rascal/grammar/tests/generated_parsers/CParserINT.java| + ) + ); + COMP + = split( + "\n", + readFile( + |std://lang/rascal/grammar/tests/generated_parsers/CParserCOMP.java| + ) + ); + + INTSET = toSet(INT); + COMPSET = toSet(COMP); + println("INT: lines, COMP lines"); + println( + "INT - COMP : lines, COMP - INT lines" + ); + + println("INT - COMP:"); + iprintln(INT - COMP); + + println("COMP - INT:"); + iprintln(COMP - INT); + + println( + "INTSET: lines, COMPSET lines" + ); + println( + "INTSET - COMPSET : lines, COMPSET - INTSET lines" + ); + + println("INTSET - COMPSET:"); + iprintln(INTSET - COMPSET); + + println("COMPSET - INTSET:"); + iprintln(COMPSET - INTSET); + + return true; +} + +void compare2() { + dontNestCOMP + = {<284, 367 >, + <344, 474 >, + <284, 474 >, + <257, 288 >, + <284, 468 >, + <344, 468 >, + <354, 373 >, + <273, 407 >, + <294, 336 >, + <319, 336 >, + <219, 391 >, + <348, 438 >, + <302, 474 >, + <348, 342 >, + <240, 414 >, + <361, 444 >, + <306, 379 >, + <344, 367 >, + <440, 426 >, + <452, 438 >, + <440, 420 >, + <281, 348 >, + <261, 456 >, + <189, 288 >, + <240, 407 >, + <277, 481 >, + <385, 426 >, + <385, 420 >, + <407, 474 >, + <302, 367 >, + <288, 456 >, + <253, 336 >, + <350, 462 >, + <325, 397 >, + <297, 316 >, + <273, 414 >, + <166, 397 >, + <407, 468 >, + <288, 391 >, + <261, 391 >, + <189, 261 >, + <312, 361 >, + <422, 432 >, + <323, 438 >, + <332, 450 >, + <323, 342 >, + <166, 354 >, + <281, 426 >, + <219, 456 >, + <277, 294 >, + <281, 420 >, + <247, 288 >, + <177, 462 >, + <281, 310 >, + <325, 354 >, + <302, 468 >, + <281, 323 >, + <434, 468 >, + <434, 474 >, + <323, 414 >, + <363, 474 >, + <416, 468 >, + <464, 450 >, + <294, 316 >, + <336, 481 >, + <348, 407 >, + <257, 444 >, + <363, 468 >, + <297, 336 >, + <452, 414 >, + <253, 316 >, + <338, 397 >, + <393, 450 >, + <416, 474 >, + <354, 432 >, + <348, 414 >, + <240, 438 >, + <240, 342 >, + <265, 294 >, + <195, 361 >, + <381, 420 >, + <189, 444 >, + <381, 426 >, + <219, 265 >, + <189, 281 >, + <323, 407 >, + <247, 444 >, + <391, 444 >, + <379, 432 >, + <273, 342 >, + <273, 438 >, + <265, 481 >, + <316, 385 >, + <166, 247 >, + <203, 379 >, + <195, 302 >, + <350, 414 >, + <247, 361 >, + <344, 450 >, + <332, 367 >, + <375, 444 >, + <306, 348 >, + <257, 302 >, + <277, 397 >, + <189, 302 >, + <281, 379 >, + <332, 474 >, + <316, 432 >, + <325, 481 >, + <407, 450 >, + <273, 462 >, + <166, 481 >, + <177, 407 >, + <470, 432 >, + <354, 385 >, + <284, 450 >, + <438, 481 >, + <240, 462 >, + <189, 361 >, + <203, 323 >, + <387, 420 >, + <387, 426 >, + <357, 444 >, + <277, 354 >, + <166, 294 >, + <342, 336 >, + <195, 444 >, + <235, 336 >, + <399, 456 >, + <257, 361 >, + <290, 316 >, + <379, 385 >, + <332, 468 >, + <247, 302 >, + <350, 407 >, + <195, 281 >, + <306, 420 >, + <177, 414 >, + <306, 426 >, + <302, 450 >, + <312, 444 >, + <323, 462 >, + <219, 240 >, + <306, 323 >, + <203, 273 >, + <336, 397 >, + <290, 336 >, + <336, 354 >, + <316, 373 >, + <393, 474 >, + <416, 450 >, + <338, 481 >, + <393, 468 >, + <363, 450 >, + <452, 462 >, + <316, 329 >, + <361, 361 >, + <434, 450 >, + <348, 462 >, + <203, 348 >, + <195, 288 >, + <474, 481 >, + <464, 474 >, + <310, 456 >, + <329, 456 >, + <203, 420 >, + <203, 426 >, + <203, 310 >, + <235, 316 >, + <195, 261 >, + <464, 468 >, + <310, 391 >, + <329, 391 >, + <265, 397 >, + <350, 438 >, + <177, 342 >, + <265, 354 >, + <177, 438 >, + <219, 261 >, + <428, 474 >, + <257, 456 >, + <235, 468 >, + <410, 450 >, + <195, 240 >, + <177, 397 >, + <273, 294 >, + <391, 391 >, + <306, 385 >, + <247, 391 >, + <277, 407 >, + <367, 468 >, + <240, 481 >, + <354, 426 >, + <354, 420 >, + <261, 288 >, + <458, 474 >, + <381, 432 >, + <203, 235 >, + <189, 456 >, + <281, 373 >, + <277, 414 >, + <319, 450 >, + <342, 468 >, + <294, 450 >, + <288, 288 >, + <336, 438 >, + <336, 342 >, + <166, 462 >, + <257, 391 >, + <325, 462 >, + <342, 474 >, + <273, 481 >, + <240, 294 >, + <177, 354 >, + <281, 329 >, + <235, 474 >, + <367, 367 >, + <265, 438 >, + <350, 397 >, + <265, 342 >, + <369, 450 >, + <458, 468 >, + <428, 468 >, + <189, 391 >, + <379, 426 >, + <379, 420 >, + <219, 288 >, + <397, 450 >, + <367, 474 >, + <235, 367 >, + <354, 348 >, + <247, 456 >, + <391, 456 >, + <342, 367 >, + <253, 450 >, + <452, 481 >, + <385, 432 >, + <373, 468 >, + <261, 444 >, + <373, 474 >, + <336, 407 >, + <323, 481 >, + <1441, 1475 >, + <336, 414 >, + <446, 468 >, + <219, 281 >, + <361, 456 >, + <189, 265 >, + <203, 385 >, + <297, 450 >, + <288, 444 >, + <338, 462 >, + <329, 361 >, + <348, 481 >, + <310, 361 >, + <290, 474 >, + <177, 247 >, + <265, 407 >, + <290, 468 >, + <281, 432 >, + <219, 444 >, + <361, 391 >, + <316, 379 >, + <422, 420 >, + <446, 474 >, + <265, 414 >, + <422, 426 >, + <440, 432 >, + <277, 438 >, + <290, 367 >, + <277, 342 >, + <428, 450 >, + <219, 361 >, + <195, 265 >, + <235, 450 >, + <288, 361 >, + <284, 336 >, + <273, 354 >, + <319, 474 >, + <294, 474 >, + <385, 385 >, + <354, 379 >, + <325, 414 >, + <306, 329 >, + <273, 397 >, + <302, 336 >, + <166, 414 >, + <312, 456 >, + <350, 481 >, + <458, 450 >, + <369, 468 >, + <369, 474 >, + <288, 302 >, + <294, 468 >, + <306, 373 >, + <177, 294 >, + <240, 354 >, + <319, 468 >, + <342, 450 >, + <261, 302 >, + <253, 367 >, + <312, 391 >, + <277, 462 >, + <253, 474 >, + <261, 361 >, + <379, 379 >, + <367, 450 >, + <310, 444 >, + <329, 444 >, + <240, 397 >, + <397, 474 >, + <281, 385 >, + <410, 474 >, + <338, 438 >, + <219, 302 >, + <316, 323 >, + <410, 468 >, + <319, 367 >, + <166, 407 >, + <294, 367 >, + <253, 468 >, + <177, 481 >, + <203, 432 >, + <397, 468 >, + <325, 407 >, + <323, 397 >, + <189, 240 >, + <203, 373 >, + <338, 414 >, + <373, 450 >, + <297, 468 >, + <336, 462 >, + <265, 462 >, + <316, 348 >, + <297, 367 >, + <470, 426 >, + <470, 420 >, + <203, 329 >, + <203, 253 >, + <302, 316 >, + <348, 397 >, + <375, 456 >, + <284, 316 >, + <316, 310 >, + <316, 420 >, + <446, 450 >, + <348, 354 >, + <316, 426 >, + <387, 432 >, + <357, 456 >, + <338, 407 >, + <195, 456 >, + <399, 444 >, + <306, 432 >, + <297, 474 >, + <357, 391 >, + <195, 391 >, + <323, 354 >, + <166, 438 >, + <166, 342 >, + <325, 438 >, + <325, 342 >, + <290, 450 >, + <375, 391 >, + <325, 444 >, + <336, 361 >, + <166, 444 >, + <342, 385 >, + <253, 329 >, + <265, 302 >, + <354, 336 >, + <387, 468 >, + <294, 329 >, + <195, 294 >, + <265, 361 >, + <177, 265 >, + <235, 385 >, + <294, 373 >, + <319, 373 >, + <306, 468 >, + <387, 474 >, + <367, 385 >, + <281, 450 >, + <1495, 1534 >, + <332, 426 >, + <332, 420 >, + <189, 247 >, + <1206, 1225 >, + <440, 450 >, + <357, 481 >, + <302, 379 >, + <195, 481 >, + <361, 397 >, + <310, 414 >, + <329, 414 >, + <306, 474 >, + <297, 432 >, + <375, 481 >, + <344, 379 >, + <306, 367 >, + <253, 373 >, + <399, 438 >, + <284, 379 >, + <385, 450 >, + <166, 281 >, + <310, 407 >, + <329, 407 >, + <363, 379 >, + <166, 288 >, + <177, 456 >, + <219, 462 >, + <397, 432 >, + <203, 474 >, + <189, 354 >, + <277, 361 >, + <177, 391 >, + <166, 261 >, + <277, 302 >, + <297, 373 >, + <369, 432 >, + <247, 354 >, + <350, 456 >, + <312, 481 >, + <373, 385 >, + <391, 397 >, + <410, 432 >, + <247, 397 >, + <203, 468 >, + <253, 432 >, + <464, 420 >, + <464, 426 >, + <399, 414 >, + <257, 354 >, + <288, 462 >, + <399, 407 >, + <290, 385 >, + <338, 444 >, + <381, 450 >, + <257, 397 >, + <350, 391 >, + <1453, 1475 >, + <393, 420 >, + <261, 462 >, + <393, 426 >, + <310, 438 >, + <294, 432 >, + <329, 438 >, + <203, 367 >, + <319, 432 >, + <329, 342 >, + <310, 342 >, + <189, 397 >, + <297, 329 >, + <253, 385 >, + <302, 310 >, + <235, 373 >, + <440, 474 >, + <290, 432 >, + <348, 391 >, + <281, 367 >, + <385, 468 >, + <369, 385 >, + <357, 397 >, + <329, 462 >, + <310, 462 >, + <446, 432 >, + <261, 438 >, + <261, 342 >, + <195, 397 >, + <440, 468 >, + <361, 481 >, + <235, 329 >, + <177, 240 >, + <281, 474 >, + <288, 342 >, + <332, 379 >, + <288, 438 >, + <338, 361 >, + <387, 450 >, + <284, 348 >, + <265, 288 >, + <281, 468 >, + <302, 426 >, + <302, 420 >, + <342, 373 >, + <306, 450 >, + <323, 456 >, + <344, 420 >, + <344, 426 >, + <284, 426 >, + <284, 420 >, + <284, 310 >, + <316, 316 >, + <219, 342 >, + <219, 438 >, + <407, 426 >, + <367, 373 >, + <407, 420 >, + <385, 474 >, + <294, 385 >, + <319, 385 >, + <375, 397 >, + <348, 456 >, + <323, 391 >, + <195, 354 >, + <452, 456 >, + <302, 348 >, + <373, 432 >, + <277, 444 >, + <240, 391 >, + <265, 444 >, + <166, 361 >, + <325, 361 >, + <277, 288 >, + <235, 432 >, + <261, 407 >, + <302, 323 >, + <367, 432 >, + <399, 462 >, + <288, 407 >, + <290, 373 >, + <240, 456 >, + <373, 373 >, + <290, 329 >, + <219, 414 >, + <312, 354 >, + <257, 294 >, + <391, 481 >, + <247, 481 >, + <297, 385 >, + <336, 444 >, + <203, 450 >, + <189, 294 >, + <166, 302 >, + <195, 247 >, + <416, 420 >, + <416, 426 >, + <458, 432 >, + <273, 456 >, + <381, 474 >, + <428, 432 >, + <312, 397 >, + <219, 407 >, + <257, 481 >, + <284, 323 >, + <316, 336 >, + <434, 420 >, + <247, 294 >, + <434, 426 >, + <273, 391 >, + <381, 468 >, + <288, 414 >, + <363, 426 >, + <363, 420 >, + <342, 432 >, + <261, 414 >, + <189, 481 >, + <235, 379 >, + <281, 336 >, + <177, 444 >, + <357, 414 >, + <361, 462 >, + <195, 414 >, + <302, 385 >, + <375, 407 >, + <393, 432 >, + <369, 420 >, + <338, 391 >, + <369, 426 >, + <350, 444 >, + <342, 379 >, + <253, 420 >, + <253, 426 >, + <297, 323 >, + <312, 342 >, + <312, 438 >, + <253, 310 >, + <464, 432 >, + <166, 265 >, + <397, 420 >, + <323, 361 >, + <397, 426 >, + <367, 379 >, + <379, 450 >, + <410, 420 >, + <294, 348 >, + <319, 348 >, + <410, 426 >, + <329, 481 >, + <177, 281 >, + <310, 481 >, + <348, 361 >, + <319, 426 >, + <294, 426 >, + <375, 414 >, + <319, 420 >, + <294, 420 >, + <219, 247 >, + <294, 310 >, + <203, 257 >, + <338, 456 >, + <195, 407 >, + <332, 373 >, + <284, 385 >, + <354, 450 >, + <432, 481 >, + <344, 385 >, + <253, 348 >, + <357, 407 >, + <325, 456 >, + <426, 481 >, + <273, 361 >, + <297, 310 >, + <375, 438 >, + <325, 391 >, + <288, 397 >, + <281, 316 >, + <357, 438 >, + <219, 354 >, + <166, 456 >, + <316, 367 >, + <312, 414 >, + <297, 348 >, + <312, 407 >, + <219, 397 >, + <332, 432 >, + <391, 462 >, + <247, 462 >, + <177, 288 >, + <316, 474 >, + <422, 450 >, + <297, 420 >, + <297, 426 >, + <253, 323 >, + <240, 302 >, + <261, 354 >, + <288, 354 >, + <177, 261 >, + <316, 468 >, + <399, 481 >, + <257, 462 >, + <166, 391 >, + <189, 462 >, + <240, 361 >, + <195, 342 >, + <195, 438 >, + <470, 474 >, + <261, 397 >, + <290, 379 >, + <294, 323 >, + <373, 379 >, + <273, 302 >, + <363, 385 >, + <470, 468 >, + <265, 391 >, + <265, 456 >, + <336, 456 >, + <203, 277 >, + <342, 348 >, + <284, 329 >, + <354, 474 >, + <416, 432 >, + <363, 432 >, + <354, 468 >, + <253, 379 >, + <344, 373 >, + <284, 373 >, + <189, 342 >, + <189, 438 >, + <195, 462 >, + <379, 474 >, + <240, 444 >, + <361, 414 >, + <332, 385 >, + <357, 462 >, + <329, 397 >, + <310, 397 >, + <367, 426 >, + <336, 391 >, + <367, 420 >, + <257, 438 >, + <257, 342 >, + <354, 367 >, + <379, 468 >, + <235, 348 >, + <319, 379 >, + <294, 379 >, + <428, 420 >, + <428, 426 >, + <273, 444 >, + <458, 426 >, + <458, 420 >, + <247, 342 >, + <247, 438 >, + <391, 438 >, + <306, 336 >, + <302, 329 >, + <235, 310 >, + <203, 316 >, + <369, 379 >, + <342, 426 >, + <342, 420 >, + <302, 373 >, + <166, 240 >, + <235, 426 >, + <375, 462 >, + <235, 420 >, + <361, 407 >, + <310, 354 >, + <329, 354 >, + <434, 432 >, + <290, 323 >, + <257, 407 >, + <240, 288 >, + <288, 294 >, + <350, 361 >, + <363, 373 >, + <177, 361 >, + <273, 288 >, + <257, 414 >, + <288, 481 >, + <348, 444 >, + <446, 426 >, + <316, 450 >, + <446, 420 >, + <422, 474 >, + <219, 481 >, + <297, 379 >, + <422, 468 >, + <247, 414 >, + <391, 414 >, + <302, 432 >, + <261, 294 >, + <177, 302 >, + <470, 450 >, + <203, 336 >, + <235, 323 >, + <407, 432 >, + <277, 391 >, + <312, 462 >, + <284, 432 >, + <323, 444 >, + <247, 407 >, + <290, 310 >, + <391, 407 >, + <444, 481 >, + <344, 432 >, + <290, 426 >, + <290, 420 >, + <373, 426 >, + <452, 444 >, + <219, 294 >, + <373, 420 >, + <277, 456 >, + <361, 438 >, + <456, 481 >, + <189, 414 >, + <261, 481 >, + <290, 348 >, + <189, 407 >, + <277, 379 >, + <195, 367 >, + <166, 273 >, + <306, 481 >, + <261, 316 >, + <470, 438 >, + <219, 277 >, + <297, 456 >, + <325, 348 >, + <195, 468 >, + <387, 481 >, + <350, 373 >, + <219, 316 >, + <354, 462 >, + <166, 310 >, + <375, 474 >, + <325, 426 >, + <325, 420 >, + <281, 354 >, + <166, 420 >, + <166, 426 >, + <177, 329 >, + <332, 444 >, + <281, 397 >, + <240, 385 >, + <357, 474 >, + <297, 391 >, + <288, 316 >, + <379, 462 >, + <195, 474 >, + <361, 450 >, + <166, 348 >, + <357, 468 >, + <316, 342 >, + <316, 438 >, + <375, 468 >, + <177, 373 >, + <273, 385 >, + <177, 253 >, + <385, 397 >, + <312, 468 >, + <348, 385 >, + <253, 456 >, + <410, 456 >, + <265, 379 >, + <336, 379 >, + <261, 336 >, + <253, 391 >, + <288, 336 >, + <312, 474 >, + <166, 323 >, + <323, 385 >, + <350, 432 >, + <470, 414 >, + <294, 391 >, + <319, 391 >, + <219, 336 >, + <247, 450 >, + <391, 450 >, + <422, 462 >, + <284, 361 >, + <464, 444 >, + <397, 456 >, + <344, 361 >, + <302, 302 >, + <203, 481 >, + <177, 432 >, + <312, 367 >, + <316, 414 >, + <319, 456 >, + <462, 481 >, + <294, 456 >, + <316, 407 >, + <393, 444 >, + <257, 450 >, + <381, 397 >, + <203, 294 >, + <189, 450 >, + <338, 420 >, + <338, 426 >, + <369, 391 >, + <369, 456 >, + <302, 361 >, + <354, 407 >, + <281, 481 >, + <379, 414 >, + <361, 474 >, + <379, 407 >, + <219, 257 >, + <265, 323 >, + <195, 450 >, + <361, 468 >, + <440, 481 >, + <357, 450 >, + <446, 456 >, + <387, 397 >, + <177, 385 >, + <273, 373 >, + <273, 329 >, + <306, 397 >, + <277, 348 >, + <302, 444 >, + <361, 367 >, + <407, 444 >, + <284, 444 >, + <373, 391 >, + <344, 444 >, + <323, 432 >, + <422, 438 >, + <306, 354 >, + <240, 373 >, + <290, 456 >, + <373, 456 >, + <281, 294 >, + <277, 420 >, + <452, 432 >, + <277, 426 >, + <350, 385 >, + <240, 329 >, + <277, 310 >, + <385, 481 >, + <290, 391 >, + <375, 450 >, + <348, 432 >, + <325, 379 >, + <354, 414 >, + <166, 379 >, + <203, 247 >, + <310, 336 >, + <329, 336 >, + <316, 462 >, + <203, 397 >, + <189, 367 >, + <323, 329 >, + <312, 450 >, + <336, 348 >, + <434, 444 >, + <323, 373 >, + <240, 432 >, + <367, 456 >, + <247, 474 >, + <391, 474 >, + <257, 367 >, + <348, 373 >, + <265, 420 >, + <265, 426 >, + <422, 414 >, + <336, 426 >, + <302, 288 >, + <336, 420 >, + <367, 391 >, + <391, 468 >, + <247, 468 >, + <265, 310 >, + <470, 462 >, + <277, 323 >, + <203, 354 >, + <428, 456 >, + <458, 456 >, + <273, 432 >, + <189, 474 >, + <379, 438 >, + <338, 379 >, + <257, 474 >, + <414, 481 >, + <332, 361 >, + <310, 316 >, + <342, 391 >, + <416, 444 >, + <1191, 1225 >, + <235, 391 >, + <363, 444 >, + <257, 468 >, + <342, 456 >, + <381, 481 >, + <177, 235 >, + <235, 456 >, + <247, 367 >, + <265, 348 >, + <189, 468 >, + <354, 438 >, + <354, 342 >, + <369, 444 >, + <306, 407 >, + <177, 273 >, + <397, 444 >, + <440, 462 >, + <354, 354 >, + <166, 373 >, + <385, 462 >, + <387, 407 >, + <393, 456 >, + <247, 316 >, + <329, 367 >, + <203, 438 >, + <310, 367 >, + <203, 342 >, + <277, 385 >, + <350, 426 >, + <350, 420 >, + <306, 414 >, + <177, 420 >, + <177, 426 >, + <166, 329 >, + <189, 316 >, + <410, 444 >, + <177, 310 >, + <281, 462 >, + <290, 361 >, + <257, 316 >, + <379, 397 >, + <253, 444 >, + <329, 474 >, + <240, 379 >, + <464, 456 >, + <310, 474 >, + <329, 468 >, + <310, 468 >, + <387, 414 >, + <325, 373 >, + <166, 253 >, + <338, 432 >, + <189, 277 >, + <319, 444 >, + <294, 444 >, + <354, 397 >, + <273, 379 >, + <177, 348 >, + <247, 336 >, + <253, 288 >, + <203, 414 >, + <235, 361 >, + <189, 336 >, + <381, 462 >, + <387, 438 >, + <261, 450 >, + <306, 342 >, + <306, 438 >, + <399, 474 >, + <219, 450 >, + <332, 456 >, + <338, 373 >, + <177, 323 >, + <342, 361 >, + <332, 391 >, + <257, 336 >, + <297, 444 >, + <336, 385 >, + <316, 481 >, + <265, 385 >, + <399, 468 >, + <323, 379 >, + <288, 450 >, + <294, 288 >, + <235, 302 >, + <367, 361 >, + <325, 432 >, + <348, 379 >, + <203, 407 >, + <166, 432 >, + <195, 257 >, + <470, 481 >, + <381, 438 >, + <387, 462 >, + <306, 462 >, + <265, 432 >, + <379, 481 >, + <189, 257 >, + <336, 432 >, + <240, 348 >, + <363, 391 >, + <416, 456 >, + <273, 310 >, + <323, 323 >, + <166, 385 >, + <297, 361 >, + <420, 481 >, + <310, 450 >, + <329, 450 >, + <273, 348 >, + <177, 379 >, + <367, 444 >, + <440, 414 >, + <240, 310 >, + <385, 407 >, + <240, 426 >, + <240, 420 >, + <277, 329 >, + <281, 414 >, + <277, 373 >, + <273, 426 >, + <458, 444 >, + <273, 420 >, + <281, 407 >, + <428, 444 >, + <325, 385 >, + <434, 456 >, + <385, 414 >, + <363, 456 >, + <354, 481 >, + <235, 444 >, + <195, 336 >, + <350, 379 >, + <342, 444 >, + <323, 348 >, + <312, 336 >, + <284, 456 >, + <235, 288 >, + <344, 391 >, + <446, 444 >, + <468, 481 >, + <219, 474 >, + <399, 450 >, + <385, 438 >, + <381, 407 >, + <195, 277 >, + <166, 235 >, + <302, 391 >, + <316, 397 >, + <219, 468 >, + <422, 481 >, + <302, 456 >, + <288, 367 >, + <253, 302 >, + <261, 367 >, + <203, 462 >, + <240, 323 >, + <348, 348 >, + <323, 426 >, + <323, 420 >, + <344, 456 >, + <338, 385 >, + <290, 444 >, + <253, 361 >, + <261, 474 >, + <281, 342 >, + <281, 438 >, + <407, 456 >, + <265, 329 >, + <195, 316 >, + <288, 474 >, + <348, 420 >, + <348, 426 >, + <316, 354 >, + <265, 373 >, + <219, 367 >, + <288, 468 >, + <381, 414 >, + <336, 373 >, + <452, 426 >, + <284, 391 >, + <452, 420 >, + <277, 432 >, + <440, 438 >, + <373, 444 >, + <273, 323 >, + <261, 468 >, + <294, 302 >, + <319, 361 >, + <294, 361 >, + <481, 481 >, + <235, 407 >, + <342, 407 >, + <288, 432 >, + <385, 444 >, + <277, 474 >, + <344, 481 >, + <458, 414 >, + <253, 462 >, + <367, 414 >, + <373, 438 >, + <440, 444 >, + <361, 420 >, + <247, 323 >, + <361, 426 >, + <367, 407 >, + <277, 468 >, + <302, 294 >, + <261, 432 >, + <342, 414 >, + <446, 438 >, + <235, 414 >, + <294, 462 >, + <319, 462 >, + <166, 450 >, + <177, 336 >, + <407, 481 >, + <325, 450 >, + <203, 361 >, + <189, 323 >, + <284, 481 >, + <257, 323 >, + <369, 462 >, + <428, 414 >, + <410, 462 >, + <329, 385 >, + <310, 385 >, + <281, 444 >, + <219, 432 >, + <332, 397 >, + <357, 379 >, + <397, 462 >, + <290, 438 >, + <277, 367 >, + <422, 456 >, + <290, 342 >, + <203, 302 >, + <302, 481 >, + <195, 379 >, + <261, 329 >, + <379, 391 >, + <297, 462 >, + <177, 316 >, + <434, 481 >, + <354, 391 >, + <306, 361 >, + <416, 481 >, + <428, 438 >, + <261, 373 >, + <336, 474 >, + <373, 407 >, + <354, 456 >, + <247, 348 >, + <189, 310 >, + <363, 481 >, + <338, 450 >, + <189, 273 >, + <381, 444 >, + <189, 420 >, + <189, 426 >, + <312, 379 >, + <373, 414 >, + <367, 438 >, + <219, 373 >, + <265, 367 >, + <257, 420 >, + <257, 426 >, + <257, 310 >, + <219, 253 >, + <336, 367 >, + <290, 414 >, + <219, 329 >, + <379, 456 >, + <458, 438 >, + <257, 348 >, + <177, 277 >, + <247, 310 >, + <189, 348 >, + <281, 288 >, + <290, 407 >, + <265, 468 >, + <391, 420 >, + <247, 426 >, + <391, 426 >, + <247, 420 >, + <336, 468 >, + <288, 373 >, + <342, 342 >, + <288, 329 >, + <342, 438 >, + <235, 342 >, + <265, 474 >, + <446, 414 >, + <235, 438 >, + <195, 348 >, + <166, 474 >, + <325, 367 >, + <367, 462 >, + <316, 456 >, + <195, 273 >, + <332, 481 >, + <369, 407 >, + <253, 414 >, + <397, 414 >, + <325, 468 >, + <302, 354 >, + <397, 407 >, + <253, 407 >, + <166, 468 >, + <458, 462 >, + <344, 397 >, + <428, 462 >, + <369, 414 >, + <284, 397 >, + <310, 373 >, + <470, 456 >, + <329, 373 >, + <310, 329 >, + <1507, 1534 >, + <329, 329 >, + <297, 438 >, + <297, 342 >, + <450, 481 >, + <312, 323 >, + <319, 414 >, + <375, 420 >, + <294, 414 >, + <375, 426 >, + <235, 462 >, + <325, 474 >, + <342, 462 >, + <240, 336 >, + <277, 450 >, + <219, 235 >, + <319, 407 >, + <294, 407 >, + <166, 367 >, + <357, 420 >, + <203, 261 >, + <387, 444 >, + <357, 426 >, + <195, 310 >, + <195, 426 >, + <195, 420 >, + <399, 432 >, + <203, 288 >, + <306, 444 >, + <410, 414 >, + <284, 354 >, + <302, 397 >, + <316, 391 >, + <273, 336 >, + <361, 379 >, + <261, 385 >, + <240, 316 >, + <281, 361 >, + <363, 397 >, + <297, 407 >, + <177, 257 >, + <373, 462 >, + <348, 336 >, + <219, 385 >, + <247, 379 >, + <273, 316 >, + <281, 302 >, + <397, 438 >, + <338, 474 >, + <410, 438 >, + <257, 379 >, + <338, 468 >, + <393, 481 >, + <446, 462 >, + <294, 342 >, + <319, 438 >, + <294, 438 >, + <329, 432 >, + <310, 432 >, + <319, 342 >, + <203, 281 >, + <297, 414 >, + <312, 348 >, + <323, 336 >, + <338, 367 >, + <464, 481 >, + <369, 438 >, + <203, 444 >, + <336, 450 >, + <195, 323 >, + <265, 450 >, + <290, 462 >, + <288, 385 >, + <253, 438 >, + <253, 342 >, + <312, 426 >, + <189, 379 >, + <312, 420 >, + <434, 438 >, + <273, 367 >, + <219, 323 >, + <363, 438 >, + <257, 432 >, + <316, 361 >, + <235, 481 >, + <350, 450 >, + <302, 407 >, + <344, 414 >, + <325, 336 >, + <240, 468 >, + <294, 354 >, + <367, 481 >, + <319, 354 >, + <354, 444 >, + <294, 397 >, + <319, 397 >, + <189, 432 >, + <273, 474 >, + <342, 481 >, + <381, 456 >, + <381, 391 >, + <240, 367 >, + <253, 354 >, + <407, 414 >, + <273, 468 >, + <375, 385 >, + <261, 323 >, + <369, 397 >, + <284, 414 >, + <428, 481 >, + <361, 373 >, + <458, 481 >, + <288, 323 >, + <416, 438 >, + <195, 385 >, + <284, 407 >, + <332, 462 >, + <357, 385 >, + <391, 432 >, + <247, 432 >, + <344, 407 >, + <253, 397 >, + <379, 444 >, + <177, 450 >, + <302, 414 >, + <166, 336 >, + <235, 294 >, + <329, 379 >, + <397, 397 >, + <240, 474 >, + <310, 379 >, + <363, 407 >, + <297, 354 >, + <247, 329 >, + <385, 391 >, + <290, 481 >, + <416, 414 >, + <344, 438 >, + <219, 310 >, + <281, 391 >, + <446, 481 >, + <257, 329 >, + <361, 432 >, + <297, 397 >, + <189, 329 >, + <203, 240 >, + <323, 367 >, + <440, 456 >, + <166, 316 >, + <323, 474 >, + <452, 468 >, + <261, 310 >, + <363, 414 >, + <261, 426 >, + <219, 348 >, + <261, 420 >, + <393, 462 >, + <312, 385 >, + <373, 481 >, + <288, 310 >, + <348, 468 >, + <247, 373 >, + <434, 414 >, + <385, 456 >, + <288, 420 >, + <288, 426 >, + <348, 474 >, + <302, 342 >, + <302, 438 >, + <195, 235 >, + <323, 468 >, + <166, 277 >, + <452, 474 >, + <189, 253 >, + <464, 462 >, + <288, 348 >, + <422, 444 >, + <284, 438 >, + <284, 342 >, + <189, 373 >, + <261, 348 >, + <281, 456 >, + <219, 426 >, + <407, 438 >, + <219, 420 >, + <257, 373 >, + <348, 367 >, + <219, 273 >, + <350, 468 >, + <357, 373 >, + <344, 462 >, + <265, 316 >, + <195, 329 >, + <342, 397 >, + <203, 391 >, + <310, 310 >, + <393, 438 >, + <410, 481 >, + <302, 462 >, + <397, 481 >, + <310, 348 >, + <329, 348 >, + <407, 462 >, + <273, 450 >, + <369, 481 >, + <464, 438 >, + <195, 253 >, + <195, 373 >, + <332, 407 >, + <312, 432 >, + <284, 462 >, + <319, 481 >, + <253, 294 >, + <294, 481 >, + <177, 367 >, + <350, 474 >, + <235, 397 >, + <342, 354 >, + <277, 336 >, + <240, 450 >, + <235, 354 >, + <177, 474 >, + <367, 397 >, + <350, 367 >, + <329, 420 >, + <310, 426 >, + <329, 426 >, + <310, 420 >, + <189, 235 >, + <203, 456 >, + <332, 414 >, + <361, 385 >, + <294, 294 >, + <253, 481 >, + <177, 468 >, + <203, 265 >, + <393, 414 >, + <375, 432 >, + <373, 397 >, + <434, 462 >, + <348, 450 >, + <354, 361 >, + <277, 316 >, + <464, 414 >, + <312, 329 >, + <265, 336 >, + <416, 462 >, + <189, 385 >, + <323, 450 >, + <288, 379 >, + <332, 438 >, + <452, 450 >, + <363, 462 >, + <387, 456 >, + <357, 432 >, + <306, 391 >, + <247, 385 >, + <399, 426 >, + <399, 420 >, + <195, 432 >, + <312, 373 >, + <166, 257 >, + <219, 379 >, + <297, 481 >, + <393, 407 >, + <316, 444 >, + <306, 456 >, + <290, 354 >, + <310, 323 >, + <329, 323 >, + <257, 385 >, + <470, 444 >, + <336, 336 >, + <290, 397 >, + <261, 379 >}; + + dontNestGroupsCOMP + = {<{1475}, {} >, + <{474, + 438, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 367, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, {} >, + <{474, 438, 391, 426, 432, 462, 414, 385, 420, 407, 450, 397, 444, 456, 481, 468}, {} >, + <{1534}, {} >, + <{474, 438, 391, 426, 432, 462, 414, 385, 407, 450, 397, 444, 456, 481, 468, 379, 420}, {} >, + <{1225}, {} >, + <{474, 438, 391, 426, 432, 462, 414, 420, 407, 450, 397, 444, 456, 481, 468}, {} >, + <{474, 438, 426, 432, 462, 414, 420, 407, 450, 397, 444, 456, 481, 468}, {} >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, {} >, + <{336, + 474, + 438, + 348, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, {} >, + <{474, + 438, + 391, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, {} >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, {} >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, {} >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, {} >, + <{474, 438, 426, 432, 462, 414, 420, 407, 450, 444, 456, 481, 468}, {} >, + <{481}, {} >, + <{277, + 474, + 438, + 361, + 426, + 462, + 414, + 316, + 450, + 373, + 336, + 265, + 323, + 348, + 273, + 240, + 257, + 391, + 247, + 432, + 329, + 310, + 444, + 261, + 456, + 288, + 385, + 281, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 235, + 342, + 367, + 294, + 397, + 253}, {} >}; + + dontNestINT + = {<284, 367 >, + <344, 474 >, + <284, 474 >, + <257, 288 >, + <284, 468 >, + <344, 468 >, + <354, 373 >, + <273, 407 >, + <294, 336 >, + <319, 336 >, + <219, 391 >, + <348, 438 >, + <302, 474 >, + <348, 342 >, + <240, 414 >, + <361, 444 >, + <306, 379 >, + <344, 367 >, + <440, 426 >, + <452, 438 >, + <440, 420 >, + <281, 348 >, + <261, 456 >, + <189, 288 >, + <240, 407 >, + <277, 481 >, + <385, 426 >, + <385, 420 >, + <407, 474 >, + <302, 367 >, + <288, 456 >, + <253, 336 >, + <350, 462 >, + <325, 397 >, + <297, 316 >, + <273, 414 >, + <166, 397 >, + <407, 468 >, + <288, 391 >, + <261, 391 >, + <189, 261 >, + <312, 361 >, + <422, 432 >, + <323, 438 >, + <332, 450 >, + <323, 342 >, + <166, 354 >, + <281, 426 >, + <219, 456 >, + <277, 294 >, + <281, 420 >, + <247, 288 >, + <177, 462 >, + <281, 310 >, + <325, 354 >, + <302, 468 >, + <281, 323 >, + <434, 468 >, + <434, 474 >, + <323, 414 >, + <363, 474 >, + <416, 468 >, + <464, 450 >, + <294, 316 >, + <336, 481 >, + <348, 407 >, + <257, 444 >, + <363, 468 >, + <297, 336 >, + <452, 414 >, + <253, 316 >, + <338, 397 >, + <393, 450 >, + <416, 474 >, + <354, 432 >, + <348, 414 >, + <240, 438 >, + <240, 342 >, + <265, 294 >, + <195, 361 >, + <381, 420 >, + <189, 444 >, + <381, 426 >, + <219, 265 >, + <189, 281 >, + <323, 407 >, + <247, 444 >, + <391, 444 >, + <379, 432 >, + <273, 342 >, + <273, 438 >, + <265, 481 >, + <316, 385 >, + <166, 247 >, + <203, 379 >, + <195, 302 >, + <350, 414 >, + <247, 361 >, + <344, 450 >, + <332, 367 >, + <375, 444 >, + <306, 348 >, + <257, 302 >, + <277, 397 >, + <189, 302 >, + <281, 379 >, + <332, 474 >, + <316, 432 >, + <325, 481 >, + <407, 450 >, + <273, 462 >, + <166, 481 >, + <177, 407 >, + <470, 432 >, + <354, 385 >, + <284, 450 >, + <438, 481 >, + <240, 462 >, + <189, 361 >, + <203, 323 >, + <387, 420 >, + <387, 426 >, + <357, 444 >, + <277, 354 >, + <166, 294 >, + <342, 336 >, + <195, 444 >, + <235, 336 >, + <399, 456 >, + <257, 361 >, + <290, 316 >, + <379, 385 >, + <332, 468 >, + <247, 302 >, + <350, 407 >, + <195, 281 >, + <306, 420 >, + <177, 414 >, + <306, 426 >, + <302, 450 >, + <312, 444 >, + <323, 462 >, + <219, 240 >, + <306, 323 >, + <203, 273 >, + <336, 397 >, + <290, 336 >, + <336, 354 >, + <316, 373 >, + <393, 474 >, + <416, 450 >, + <338, 481 >, + <393, 468 >, + <363, 450 >, + <452, 462 >, + <316, 329 >, + <361, 361 >, + <434, 450 >, + <348, 462 >, + <203, 348 >, + <195, 288 >, + <474, 481 >, + <464, 474 >, + <310, 456 >, + <329, 456 >, + <203, 420 >, + <203, 426 >, + <203, 310 >, + <235, 316 >, + <195, 261 >, + <464, 468 >, + <310, 391 >, + <329, 391 >, + <265, 397 >, + <350, 438 >, + <177, 342 >, + <265, 354 >, + <177, 438 >, + <219, 261 >, + <428, 474 >, + <257, 456 >, + <235, 468 >, + <410, 450 >, + <195, 240 >, + <177, 397 >, + <273, 294 >, + <391, 391 >, + <306, 385 >, + <247, 391 >, + <277, 407 >, + <367, 468 >, + <240, 481 >, + <354, 426 >, + <354, 420 >, + <261, 288 >, + <458, 474 >, + <381, 432 >, + <203, 235 >, + <189, 456 >, + <281, 373 >, + <277, 414 >, + <319, 450 >, + <342, 468 >, + <294, 450 >, + <288, 288 >, + <336, 438 >, + <336, 342 >, + <166, 462 >, + <257, 391 >, + <325, 462 >, + <342, 474 >, + <273, 481 >, + <240, 294 >, + <177, 354 >, + <281, 329 >, + <235, 474 >, + <367, 367 >, + <265, 438 >, + <350, 397 >, + <265, 342 >, + <369, 450 >, + <458, 468 >, + <428, 468 >, + <189, 391 >, + <379, 426 >, + <379, 420 >, + <219, 288 >, + <397, 450 >, + <367, 474 >, + <235, 367 >, + <354, 348 >, + <247, 456 >, + <391, 456 >, + <342, 367 >, + <253, 450 >, + <452, 481 >, + <385, 432 >, + <373, 468 >, + <261, 444 >, + <373, 474 >, + <336, 407 >, + <323, 481 >, + <1441, 1475 >, + <336, 414 >, + <446, 468 >, + <219, 281 >, + <361, 456 >, + <189, 265 >, + <203, 385 >, + <297, 450 >, + <288, 444 >, + <338, 462 >, + <329, 361 >, + <348, 481 >, + <310, 361 >, + <290, 474 >, + <177, 247 >, + <265, 407 >, + <290, 468 >, + <281, 432 >, + <219, 444 >, + <361, 391 >, + <316, 379 >, + <422, 420 >, + <446, 474 >, + <265, 414 >, + <422, 426 >, + <440, 432 >, + <277, 438 >, + <290, 367 >, + <277, 342 >, + <428, 450 >, + <219, 361 >, + <195, 265 >, + <235, 450 >, + <288, 361 >, + <284, 336 >, + <273, 354 >, + <319, 474 >, + <294, 474 >, + <385, 385 >, + <354, 379 >, + <325, 414 >, + <306, 329 >, + <273, 397 >, + <302, 336 >, + <166, 414 >, + <312, 456 >, + <350, 481 >, + <458, 450 >, + <369, 468 >, + <369, 474 >, + <288, 302 >, + <294, 468 >, + <306, 373 >, + <177, 294 >, + <240, 354 >, + <319, 468 >, + <342, 450 >, + <261, 302 >, + <253, 367 >, + <312, 391 >, + <277, 462 >, + <253, 474 >, + <261, 361 >, + <379, 379 >, + <367, 450 >, + <310, 444 >, + <329, 444 >, + <240, 397 >, + <397, 474 >, + <281, 385 >, + <410, 474 >, + <338, 438 >, + <219, 302 >, + <316, 323 >, + <410, 468 >, + <319, 367 >, + <166, 407 >, + <294, 367 >, + <253, 468 >, + <177, 481 >, + <203, 432 >, + <397, 468 >, + <325, 407 >, + <323, 397 >, + <189, 240 >, + <203, 373 >, + <338, 414 >, + <373, 450 >, + <297, 468 >, + <336, 462 >, + <265, 462 >, + <316, 348 >, + <297, 367 >, + <470, 426 >, + <470, 420 >, + <203, 329 >, + <203, 253 >, + <302, 316 >, + <348, 397 >, + <375, 456 >, + <284, 316 >, + <316, 310 >, + <316, 420 >, + <446, 450 >, + <348, 354 >, + <316, 426 >, + <387, 432 >, + <357, 456 >, + <338, 407 >, + <195, 456 >, + <399, 444 >, + <306, 432 >, + <297, 474 >, + <357, 391 >, + <195, 391 >, + <323, 354 >, + <166, 438 >, + <166, 342 >, + <325, 438 >, + <325, 342 >, + <290, 450 >, + <375, 391 >, + <325, 444 >, + <336, 361 >, + <166, 444 >, + <342, 385 >, + <253, 329 >, + <265, 302 >, + <354, 336 >, + <387, 468 >, + <294, 329 >, + <195, 294 >, + <265, 361 >, + <177, 265 >, + <235, 385 >, + <294, 373 >, + <319, 373 >, + <306, 468 >, + <387, 474 >, + <367, 385 >, + <281, 450 >, + <1495, 1534 >, + <332, 426 >, + <332, 420 >, + <189, 247 >, + <1206, 1225 >, + <440, 450 >, + <357, 481 >, + <302, 379 >, + <195, 481 >, + <361, 397 >, + <310, 414 >, + <329, 414 >, + <306, 474 >, + <297, 432 >, + <375, 481 >, + <344, 379 >, + <306, 367 >, + <253, 373 >, + <399, 438 >, + <284, 379 >, + <385, 450 >, + <166, 281 >, + <310, 407 >, + <329, 407 >, + <363, 379 >, + <166, 288 >, + <177, 456 >, + <219, 462 >, + <397, 432 >, + <203, 474 >, + <189, 354 >, + <277, 361 >, + <177, 391 >, + <166, 261 >, + <277, 302 >, + <297, 373 >, + <369, 432 >, + <247, 354 >, + <350, 456 >, + <312, 481 >, + <373, 385 >, + <391, 397 >, + <410, 432 >, + <247, 397 >, + <203, 468 >, + <253, 432 >, + <464, 420 >, + <464, 426 >, + <399, 414 >, + <257, 354 >, + <288, 462 >, + <399, 407 >, + <290, 385 >, + <338, 444 >, + <381, 450 >, + <257, 397 >, + <350, 391 >, + <1453, 1475 >, + <393, 420 >, + <261, 462 >, + <393, 426 >, + <310, 438 >, + <294, 432 >, + <329, 438 >, + <203, 367 >, + <319, 432 >, + <329, 342 >, + <310, 342 >, + <189, 397 >, + <297, 329 >, + <253, 385 >, + <302, 310 >, + <235, 373 >, + <440, 474 >, + <290, 432 >, + <348, 391 >, + <281, 367 >, + <385, 468 >, + <369, 385 >, + <357, 397 >, + <329, 462 >, + <310, 462 >, + <446, 432 >, + <261, 438 >, + <261, 342 >, + <195, 397 >, + <440, 468 >, + <361, 481 >, + <235, 329 >, + <177, 240 >, + <281, 474 >, + <288, 342 >, + <332, 379 >, + <288, 438 >, + <338, 361 >, + <387, 450 >, + <284, 348 >, + <265, 288 >, + <281, 468 >, + <302, 426 >, + <302, 420 >, + <342, 373 >, + <306, 450 >, + <323, 456 >, + <344, 420 >, + <344, 426 >, + <284, 426 >, + <284, 420 >, + <284, 310 >, + <316, 316 >, + <219, 342 >, + <219, 438 >, + <407, 426 >, + <367, 373 >, + <407, 420 >, + <385, 474 >, + <294, 385 >, + <319, 385 >, + <375, 397 >, + <348, 456 >, + <323, 391 >, + <195, 354 >, + <452, 456 >, + <302, 348 >, + <373, 432 >, + <277, 444 >, + <240, 391 >, + <265, 444 >, + <166, 361 >, + <325, 361 >, + <277, 288 >, + <235, 432 >, + <261, 407 >, + <302, 323 >, + <367, 432 >, + <399, 462 >, + <288, 407 >, + <290, 373 >, + <240, 456 >, + <373, 373 >, + <290, 329 >, + <219, 414 >, + <312, 354 >, + <257, 294 >, + <391, 481 >, + <247, 481 >, + <297, 385 >, + <336, 444 >, + <203, 450 >, + <189, 294 >, + <166, 302 >, + <195, 247 >, + <416, 420 >, + <416, 426 >, + <458, 432 >, + <273, 456 >, + <381, 474 >, + <428, 432 >, + <312, 397 >, + <219, 407 >, + <257, 481 >, + <284, 323 >, + <316, 336 >, + <434, 420 >, + <247, 294 >, + <434, 426 >, + <273, 391 >, + <381, 468 >, + <288, 414 >, + <363, 426 >, + <363, 420 >, + <342, 432 >, + <261, 414 >, + <189, 481 >, + <235, 379 >, + <281, 336 >, + <177, 444 >, + <357, 414 >, + <361, 462 >, + <195, 414 >, + <302, 385 >, + <375, 407 >, + <393, 432 >, + <369, 420 >, + <338, 391 >, + <369, 426 >, + <350, 444 >, + <342, 379 >, + <253, 420 >, + <253, 426 >, + <297, 323 >, + <312, 342 >, + <312, 438 >, + <253, 310 >, + <464, 432 >, + <166, 265 >, + <397, 420 >, + <323, 361 >, + <397, 426 >, + <367, 379 >, + <379, 450 >, + <410, 420 >, + <294, 348 >, + <319, 348 >, + <410, 426 >, + <329, 481 >, + <177, 281 >, + <310, 481 >, + <348, 361 >, + <319, 426 >, + <294, 426 >, + <375, 414 >, + <319, 420 >, + <294, 420 >, + <219, 247 >, + <294, 310 >, + <203, 257 >, + <338, 456 >, + <195, 407 >, + <332, 373 >, + <284, 385 >, + <354, 450 >, + <432, 481 >, + <344, 385 >, + <253, 348 >, + <357, 407 >, + <325, 456 >, + <426, 481 >, + <273, 361 >, + <297, 310 >, + <375, 438 >, + <325, 391 >, + <288, 397 >, + <281, 316 >, + <357, 438 >, + <219, 354 >, + <166, 456 >, + <316, 367 >, + <312, 414 >, + <297, 348 >, + <312, 407 >, + <219, 397 >, + <332, 432 >, + <391, 462 >, + <247, 462 >, + <177, 288 >, + <316, 474 >, + <422, 450 >, + <297, 420 >, + <297, 426 >, + <253, 323 >, + <240, 302 >, + <261, 354 >, + <288, 354 >, + <177, 261 >, + <316, 468 >, + <399, 481 >, + <257, 462 >, + <166, 391 >, + <189, 462 >, + <240, 361 >, + <195, 342 >, + <195, 438 >, + <470, 474 >, + <261, 397 >, + <290, 379 >, + <294, 323 >, + <373, 379 >, + <273, 302 >, + <363, 385 >, + <470, 468 >, + <265, 391 >, + <265, 456 >, + <336, 456 >, + <203, 277 >, + <342, 348 >, + <284, 329 >, + <354, 474 >, + <416, 432 >, + <363, 432 >, + <354, 468 >, + <253, 379 >, + <344, 373 >, + <284, 373 >, + <189, 342 >, + <189, 438 >, + <195, 462 >, + <379, 474 >, + <240, 444 >, + <361, 414 >, + <332, 385 >, + <357, 462 >, + <329, 397 >, + <310, 397 >, + <367, 426 >, + <336, 391 >, + <367, 420 >, + <257, 438 >, + <257, 342 >, + <354, 367 >, + <379, 468 >, + <235, 348 >, + <319, 379 >, + <294, 379 >, + <428, 420 >, + <428, 426 >, + <273, 444 >, + <458, 426 >, + <458, 420 >, + <247, 342 >, + <247, 438 >, + <391, 438 >, + <306, 336 >, + <302, 329 >, + <235, 310 >, + <203, 316 >, + <369, 379 >, + <342, 426 >, + <342, 420 >, + <302, 373 >, + <166, 240 >, + <235, 426 >, + <375, 462 >, + <235, 420 >, + <361, 407 >, + <310, 354 >, + <329, 354 >, + <434, 432 >, + <290, 323 >, + <257, 407 >, + <240, 288 >, + <288, 294 >, + <350, 361 >, + <363, 373 >, + <177, 361 >, + <273, 288 >, + <257, 414 >, + <288, 481 >, + <348, 444 >, + <446, 426 >, + <316, 450 >, + <446, 420 >, + <422, 474 >, + <219, 481 >, + <297, 379 >, + <422, 468 >, + <247, 414 >, + <391, 414 >, + <302, 432 >, + <261, 294 >, + <177, 302 >, + <470, 450 >, + <203, 336 >, + <235, 323 >, + <407, 432 >, + <277, 391 >, + <312, 462 >, + <284, 432 >, + <323, 444 >, + <247, 407 >, + <290, 310 >, + <391, 407 >, + <444, 481 >, + <344, 432 >, + <290, 426 >, + <290, 420 >, + <373, 426 >, + <452, 444 >, + <219, 294 >, + <373, 420 >, + <277, 456 >, + <361, 438 >, + <456, 481 >, + <189, 414 >, + <261, 481 >, + <290, 348 >, + <189, 407 >, + <277, 379 >, + <195, 367 >, + <166, 273 >, + <306, 481 >, + <261, 316 >, + <470, 438 >, + <219, 277 >, + <297, 456 >, + <325, 348 >, + <195, 468 >, + <387, 481 >, + <350, 373 >, + <219, 316 >, + <354, 462 >, + <166, 310 >, + <375, 474 >, + <325, 426 >, + <325, 420 >, + <281, 354 >, + <166, 420 >, + <166, 426 >, + <177, 329 >, + <332, 444 >, + <281, 397 >, + <240, 385 >, + <357, 474 >, + <297, 391 >, + <288, 316 >, + <379, 462 >, + <195, 474 >, + <361, 450 >, + <166, 348 >, + <357, 468 >, + <316, 342 >, + <316, 438 >, + <375, 468 >, + <177, 373 >, + <273, 385 >, + <177, 253 >, + <385, 397 >, + <312, 468 >, + <348, 385 >, + <253, 456 >, + <410, 456 >, + <265, 379 >, + <336, 379 >, + <261, 336 >, + <253, 391 >, + <288, 336 >, + <312, 474 >, + <166, 323 >, + <323, 385 >, + <350, 432 >, + <470, 414 >, + <294, 391 >, + <319, 391 >, + <219, 336 >, + <247, 450 >, + <391, 450 >, + <422, 462 >, + <284, 361 >, + <464, 444 >, + <397, 456 >, + <344, 361 >, + <302, 302 >, + <203, 481 >, + <177, 432 >, + <312, 367 >, + <316, 414 >, + <319, 456 >, + <462, 481 >, + <294, 456 >, + <316, 407 >, + <393, 444 >, + <257, 450 >, + <381, 397 >, + <203, 294 >, + <189, 450 >, + <338, 420 >, + <338, 426 >, + <369, 391 >, + <369, 456 >, + <302, 361 >, + <354, 407 >, + <281, 481 >, + <379, 414 >, + <361, 474 >, + <379, 407 >, + <219, 257 >, + <265, 323 >, + <195, 450 >, + <361, 468 >, + <440, 481 >, + <357, 450 >, + <446, 456 >, + <387, 397 >, + <177, 385 >, + <273, 373 >, + <273, 329 >, + <306, 397 >, + <277, 348 >, + <302, 444 >, + <361, 367 >, + <407, 444 >, + <284, 444 >, + <373, 391 >, + <344, 444 >, + <323, 432 >, + <422, 438 >, + <306, 354 >, + <240, 373 >, + <290, 456 >, + <373, 456 >, + <281, 294 >, + <277, 420 >, + <452, 432 >, + <277, 426 >, + <350, 385 >, + <240, 329 >, + <277, 310 >, + <385, 481 >, + <290, 391 >, + <375, 450 >, + <348, 432 >, + <325, 379 >, + <354, 414 >, + <166, 379 >, + <203, 247 >, + <310, 336 >, + <329, 336 >, + <316, 462 >, + <203, 397 >, + <189, 367 >, + <323, 329 >, + <312, 450 >, + <336, 348 >, + <434, 444 >, + <323, 373 >, + <240, 432 >, + <367, 456 >, + <247, 474 >, + <391, 474 >, + <257, 367 >, + <348, 373 >, + <265, 420 >, + <265, 426 >, + <422, 414 >, + <336, 426 >, + <302, 288 >, + <336, 420 >, + <367, 391 >, + <391, 468 >, + <247, 468 >, + <265, 310 >, + <470, 462 >, + <277, 323 >, + <203, 354 >, + <428, 456 >, + <458, 456 >, + <273, 432 >, + <189, 474 >, + <379, 438 >, + <338, 379 >, + <257, 474 >, + <414, 481 >, + <332, 361 >, + <310, 316 >, + <342, 391 >, + <416, 444 >, + <1191, 1225 >, + <235, 391 >, + <363, 444 >, + <257, 468 >, + <342, 456 >, + <381, 481 >, + <177, 235 >, + <235, 456 >, + <247, 367 >, + <265, 348 >, + <189, 468 >, + <354, 438 >, + <354, 342 >, + <369, 444 >, + <306, 407 >, + <177, 273 >, + <397, 444 >, + <440, 462 >, + <354, 354 >, + <166, 373 >, + <385, 462 >, + <387, 407 >, + <393, 456 >, + <247, 316 >, + <329, 367 >, + <203, 438 >, + <310, 367 >, + <203, 342 >, + <277, 385 >, + <350, 426 >, + <350, 420 >, + <306, 414 >, + <177, 420 >, + <177, 426 >, + <166, 329 >, + <189, 316 >, + <410, 444 >, + <177, 310 >, + <281, 462 >, + <290, 361 >, + <257, 316 >, + <379, 397 >, + <253, 444 >, + <329, 474 >, + <240, 379 >, + <464, 456 >, + <310, 474 >, + <329, 468 >, + <310, 468 >, + <387, 414 >, + <325, 373 >, + <166, 253 >, + <338, 432 >, + <189, 277 >, + <319, 444 >, + <294, 444 >, + <354, 397 >, + <273, 379 >, + <177, 348 >, + <247, 336 >, + <253, 288 >, + <203, 414 >, + <235, 361 >, + <189, 336 >, + <381, 462 >, + <387, 438 >, + <261, 450 >, + <306, 342 >, + <306, 438 >, + <399, 474 >, + <219, 450 >, + <332, 456 >, + <338, 373 >, + <177, 323 >, + <342, 361 >, + <332, 391 >, + <257, 336 >, + <297, 444 >, + <336, 385 >, + <316, 481 >, + <265, 385 >, + <399, 468 >, + <323, 379 >, + <288, 450 >, + <294, 288 >, + <235, 302 >, + <367, 361 >, + <325, 432 >, + <348, 379 >, + <203, 407 >, + <166, 432 >, + <195, 257 >, + <470, 481 >, + <381, 438 >, + <387, 462 >, + <306, 462 >, + <265, 432 >, + <379, 481 >, + <189, 257 >, + <336, 432 >, + <240, 348 >, + <363, 391 >, + <416, 456 >, + <273, 310 >, + <323, 323 >, + <166, 385 >, + <297, 361 >, + <420, 481 >, + <310, 450 >, + <329, 450 >, + <273, 348 >, + <177, 379 >, + <367, 444 >, + <440, 414 >, + <240, 310 >, + <385, 407 >, + <240, 426 >, + <240, 420 >, + <277, 329 >, + <281, 414 >, + <277, 373 >, + <273, 426 >, + <458, 444 >, + <273, 420 >, + <281, 407 >, + <428, 444 >, + <325, 385 >, + <434, 456 >, + <385, 414 >, + <363, 456 >, + <354, 481 >, + <235, 444 >, + <195, 336 >, + <350, 379 >, + <342, 444 >, + <323, 348 >, + <312, 336 >, + <284, 456 >, + <235, 288 >, + <344, 391 >, + <446, 444 >, + <468, 481 >, + <219, 474 >, + <399, 450 >, + <385, 438 >, + <381, 407 >, + <195, 277 >, + <166, 235 >, + <302, 391 >, + <316, 397 >, + <219, 468 >, + <422, 481 >, + <302, 456 >, + <288, 367 >, + <253, 302 >, + <261, 367 >, + <203, 462 >, + <240, 323 >, + <348, 348 >, + <323, 426 >, + <323, 420 >, + <344, 456 >, + <338, 385 >, + <290, 444 >, + <253, 361 >, + <261, 474 >, + <281, 342 >, + <281, 438 >, + <407, 456 >, + <265, 329 >, + <195, 316 >, + <288, 474 >, + <348, 420 >, + <348, 426 >, + <316, 354 >, + <265, 373 >, + <219, 367 >, + <288, 468 >, + <381, 414 >, + <336, 373 >, + <452, 426 >, + <284, 391 >, + <452, 420 >, + <277, 432 >, + <440, 438 >, + <373, 444 >, + <273, 323 >, + <261, 468 >, + <294, 302 >, + <319, 361 >, + <294, 361 >, + <481, 481 >, + <235, 407 >, + <342, 407 >, + <288, 432 >, + <385, 444 >, + <277, 474 >, + <344, 481 >, + <458, 414 >, + <253, 462 >, + <367, 414 >, + <373, 438 >, + <440, 444 >, + <361, 420 >, + <247, 323 >, + <361, 426 >, + <367, 407 >, + <277, 468 >, + <302, 294 >, + <261, 432 >, + <342, 414 >, + <446, 438 >, + <235, 414 >, + <294, 462 >, + <319, 462 >, + <166, 450 >, + <177, 336 >, + <407, 481 >, + <325, 450 >, + <203, 361 >, + <189, 323 >, + <284, 481 >, + <257, 323 >, + <369, 462 >, + <428, 414 >, + <410, 462 >, + <329, 385 >, + <310, 385 >, + <281, 444 >, + <219, 432 >, + <332, 397 >, + <357, 379 >, + <397, 462 >, + <290, 438 >, + <277, 367 >, + <422, 456 >, + <290, 342 >, + <203, 302 >, + <302, 481 >, + <195, 379 >, + <261, 329 >, + <379, 391 >, + <297, 462 >, + <177, 316 >, + <434, 481 >, + <354, 391 >, + <306, 361 >, + <416, 481 >, + <428, 438 >, + <261, 373 >, + <336, 474 >, + <373, 407 >, + <354, 456 >, + <247, 348 >, + <189, 310 >, + <363, 481 >, + <338, 450 >, + <189, 273 >, + <381, 444 >, + <189, 420 >, + <189, 426 >, + <312, 379 >, + <373, 414 >, + <367, 438 >, + <219, 373 >, + <265, 367 >, + <257, 420 >, + <257, 426 >, + <257, 310 >, + <219, 253 >, + <336, 367 >, + <290, 414 >, + <219, 329 >, + <379, 456 >, + <458, 438 >, + <257, 348 >, + <177, 277 >, + <247, 310 >, + <189, 348 >, + <281, 288 >, + <290, 407 >, + <265, 468 >, + <391, 420 >, + <247, 426 >, + <391, 426 >, + <247, 420 >, + <336, 468 >, + <288, 373 >, + <342, 342 >, + <288, 329 >, + <342, 438 >, + <235, 342 >, + <265, 474 >, + <446, 414 >, + <235, 438 >, + <195, 348 >, + <166, 474 >, + <325, 367 >, + <367, 462 >, + <316, 456 >, + <195, 273 >, + <332, 481 >, + <369, 407 >, + <253, 414 >, + <397, 414 >, + <325, 468 >, + <302, 354 >, + <397, 407 >, + <253, 407 >, + <166, 468 >, + <458, 462 >, + <344, 397 >, + <428, 462 >, + <369, 414 >, + <284, 397 >, + <310, 373 >, + <470, 456 >, + <329, 373 >, + <310, 329 >, + <1507, 1534 >, + <329, 329 >, + <297, 438 >, + <297, 342 >, + <450, 481 >, + <312, 323 >, + <319, 414 >, + <375, 420 >, + <294, 414 >, + <375, 426 >, + <235, 462 >, + <325, 474 >, + <342, 462 >, + <240, 336 >, + <277, 450 >, + <219, 235 >, + <319, 407 >, + <294, 407 >, + <166, 367 >, + <357, 420 >, + <203, 261 >, + <387, 444 >, + <357, 426 >, + <195, 310 >, + <195, 426 >, + <195, 420 >, + <399, 432 >, + <203, 288 >, + <306, 444 >, + <410, 414 >, + <284, 354 >, + <302, 397 >, + <316, 391 >, + <273, 336 >, + <361, 379 >, + <261, 385 >, + <240, 316 >, + <281, 361 >, + <363, 397 >, + <297, 407 >, + <177, 257 >, + <373, 462 >, + <348, 336 >, + <219, 385 >, + <247, 379 >, + <273, 316 >, + <281, 302 >, + <397, 438 >, + <338, 474 >, + <410, 438 >, + <257, 379 >, + <338, 468 >, + <393, 481 >, + <446, 462 >, + <294, 342 >, + <319, 438 >, + <294, 438 >, + <329, 432 >, + <310, 432 >, + <319, 342 >, + <203, 281 >, + <297, 414 >, + <312, 348 >, + <323, 336 >, + <338, 367 >, + <464, 481 >, + <369, 438 >, + <203, 444 >, + <336, 450 >, + <195, 323 >, + <265, 450 >, + <290, 462 >, + <288, 385 >, + <253, 438 >, + <253, 342 >, + <312, 426 >, + <189, 379 >, + <312, 420 >, + <434, 438 >, + <273, 367 >, + <219, 323 >, + <363, 438 >, + <257, 432 >, + <316, 361 >, + <235, 481 >, + <350, 450 >, + <302, 407 >, + <344, 414 >, + <325, 336 >, + <240, 468 >, + <294, 354 >, + <367, 481 >, + <319, 354 >, + <354, 444 >, + <294, 397 >, + <319, 397 >, + <189, 432 >, + <273, 474 >, + <342, 481 >, + <381, 456 >, + <381, 391 >, + <240, 367 >, + <253, 354 >, + <407, 414 >, + <273, 468 >, + <375, 385 >, + <261, 323 >, + <369, 397 >, + <284, 414 >, + <428, 481 >, + <361, 373 >, + <458, 481 >, + <288, 323 >, + <416, 438 >, + <195, 385 >, + <284, 407 >, + <332, 462 >, + <357, 385 >, + <391, 432 >, + <247, 432 >, + <344, 407 >, + <253, 397 >, + <379, 444 >, + <177, 450 >, + <302, 414 >, + <166, 336 >, + <235, 294 >, + <329, 379 >, + <397, 397 >, + <240, 474 >, + <310, 379 >, + <363, 407 >, + <297, 354 >, + <247, 329 >, + <385, 391 >, + <290, 481 >, + <416, 414 >, + <344, 438 >, + <219, 310 >, + <281, 391 >, + <446, 481 >, + <257, 329 >, + <361, 432 >, + <297, 397 >, + <189, 329 >, + <203, 240 >, + <323, 367 >, + <440, 456 >, + <166, 316 >, + <323, 474 >, + <452, 468 >, + <261, 310 >, + <363, 414 >, + <261, 426 >, + <219, 348 >, + <261, 420 >, + <393, 462 >, + <312, 385 >, + <373, 481 >, + <288, 310 >, + <348, 468 >, + <247, 373 >, + <434, 414 >, + <385, 456 >, + <288, 420 >, + <288, 426 >, + <348, 474 >, + <302, 342 >, + <302, 438 >, + <195, 235 >, + <323, 468 >, + <166, 277 >, + <452, 474 >, + <189, 253 >, + <464, 462 >, + <288, 348 >, + <422, 444 >, + <284, 438 >, + <284, 342 >, + <189, 373 >, + <261, 348 >, + <281, 456 >, + <219, 426 >, + <407, 438 >, + <219, 420 >, + <257, 373 >, + <348, 367 >, + <219, 273 >, + <350, 468 >, + <357, 373 >, + <344, 462 >, + <265, 316 >, + <195, 329 >, + <342, 397 >, + <203, 391 >, + <310, 310 >, + <393, 438 >, + <410, 481 >, + <302, 462 >, + <397, 481 >, + <310, 348 >, + <329, 348 >, + <407, 462 >, + <273, 450 >, + <369, 481 >, + <464, 438 >, + <195, 253 >, + <195, 373 >, + <332, 407 >, + <312, 432 >, + <284, 462 >, + <319, 481 >, + <253, 294 >, + <294, 481 >, + <177, 367 >, + <350, 474 >, + <235, 397 >, + <342, 354 >, + <277, 336 >, + <240, 450 >, + <235, 354 >, + <177, 474 >, + <367, 397 >, + <350, 367 >, + <329, 420 >, + <310, 426 >, + <329, 426 >, + <310, 420 >, + <189, 235 >, + <203, 456 >, + <332, 414 >, + <361, 385 >, + <294, 294 >, + <253, 481 >, + <177, 468 >, + <203, 265 >, + <393, 414 >, + <375, 432 >, + <373, 397 >, + <434, 462 >, + <348, 450 >, + <354, 361 >, + <277, 316 >, + <464, 414 >, + <312, 329 >, + <265, 336 >, + <416, 462 >, + <189, 385 >, + <323, 450 >, + <288, 379 >, + <332, 438 >, + <452, 450 >, + <363, 462 >, + <387, 456 >, + <357, 432 >, + <306, 391 >, + <247, 385 >, + <399, 426 >, + <399, 420 >, + <195, 432 >, + <312, 373 >, + <166, 257 >, + <219, 379 >, + <297, 481 >, + <393, 407 >, + <316, 444 >, + <306, 456 >, + <290, 354 >, + <310, 323 >, + <329, 323 >, + <257, 385 >, + <470, 444 >, + <336, 336 >, + <290, 397 >, + <261, 379 >}; + + dontNestGroupsINT + = {<{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, {310, 316, 284, 290, 297} >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, {265, 277, 281, 302, 235, 273, 240, 257, 247, 261, 288, 294, 253} >, + <{474, 438, 391, 426, 432, 462, 414, 385, 420, 407, 450, 397, 444, 456, 481, 468}, {375, 385} >, + <{481}, {474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468} >, + <{336, + 474, + 438, + 348, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, {336, 325, 348, 354, 342, 319} >, + <{474, + 438, + 391, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, {357, 363, 373} >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, {323, 312, 329, 306} >, + <{474, 438, 391, 426, 432, 462, 414, 420, 407, 450, 397, 444, 456, 481, 468}, {391, 381} >, + <{1534}, {1507, 1495} >, + <{474, + 438, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 367, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, {338, 350, 361, 344, 332, 367} >, + <{1225}, {1191, 1206} >, + <{1475}, {1441, 1453} >, + <{474, 438, 426, 432, 462, 414, 420, 407, 450, 397, 444, 456, 481, 468}, {387, 397} >, + <{474, 438, 426, 432, 462, 414, 420, 407, 450, 444, 456, 481, 468}, {399, 393} >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, {452, 440, 470, 422, 407, 464, 446, 410, 434, 416, 458, 428} >, + <{474, 438, 391, 426, 432, 462, 414, 385, 407, 450, 397, 444, 456, 481, 468, 379, 420}, {379, 369} >, + <{277, + 474, + 438, + 361, + 426, + 462, + 414, + 316, + 450, + 373, + 336, + 265, + 323, + 348, + 273, + 240, + 257, + 391, + 247, + 432, + 329, + 310, + 444, + 261, + 456, + 288, + 385, + 281, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 235, + 342, + 367, + 294, + 397, + 253}, {166, 177, 195, 189, 219, 203} >}; + + println(" dontNestCOMP == dontNestINT: "); + println( + " dontNestGroupsCOMP == dontNestGroupsINT: " + ); + + println(" size(dontNestGroupsCOMP) : "); + println(" size(dontNestGroupsINT) : "); +} + +bool compare3() { + rel[int parent, int child] dontNestINT + = {<284, 367 >, + <344, 474 >, + <284, 474 >, + <257, 288 >, + <284, 468 >, + <344, 468 >, + <354, 373 >, + <273, 407 >, + <294, 336 >, + <319, 336 >, + <219, 391 >, + <348, 438 >, + <302, 474 >, + <348, 342 >, + <240, 414 >, + <361, 444 >, + <306, 379 >, + <344, 367 >, + <440, 426 >, + <452, 438 >, + <440, 420 >, + <281, 348 >, + <261, 456 >, + <189, 288 >, + <240, 407 >, + <277, 481 >, + <385, 426 >, + <385, 420 >, + <407, 474 >, + <302, 367 >, + <288, 456 >, + <253, 336 >, + <350, 462 >, + <325, 397 >, + <297, 316 >, + <273, 414 >, + <166, 397 >, + <407, 468 >, + <288, 391 >, + <261, 391 >, + <189, 261 >, + <312, 361 >, + <422, 432 >, + <323, 438 >, + <332, 450 >, + <323, 342 >, + <166, 354 >, + <281, 426 >, + <219, 456 >, + <277, 294 >, + <281, 420 >, + <247, 288 >, + <177, 462 >, + <281, 310 >, + <325, 354 >, + <302, 468 >, + <281, 323 >, + <434, 468 >, + <434, 474 >, + <323, 414 >, + <363, 474 >, + <416, 468 >, + <464, 450 >, + <294, 316 >, + <336, 481 >, + <348, 407 >, + <257, 444 >, + <363, 468 >, + <297, 336 >, + <452, 414 >, + <253, 316 >, + <338, 397 >, + <393, 450 >, + <416, 474 >, + <354, 432 >, + <348, 414 >, + <240, 438 >, + <240, 342 >, + <265, 294 >, + <195, 361 >, + <381, 420 >, + <189, 444 >, + <381, 426 >, + <219, 265 >, + <189, 281 >, + <323, 407 >, + <247, 444 >, + <391, 444 >, + <379, 432 >, + <273, 342 >, + <273, 438 >, + <265, 481 >, + <316, 385 >, + <166, 247 >, + <203, 379 >, + <195, 302 >, + <350, 414 >, + <247, 361 >, + <344, 450 >, + <332, 367 >, + <375, 444 >, + <306, 348 >, + <257, 302 >, + <277, 397 >, + <189, 302 >, + <281, 379 >, + <332, 474 >, + <316, 432 >, + <325, 481 >, + <407, 450 >, + <273, 462 >, + <166, 481 >, + <177, 407 >, + <470, 432 >, + <354, 385 >, + <284, 450 >, + <438, 481 >, + <240, 462 >, + <189, 361 >, + <203, 323 >, + <387, 420 >, + <387, 426 >, + <357, 444 >, + <277, 354 >, + <166, 294 >, + <342, 336 >, + <195, 444 >, + <235, 336 >, + <399, 456 >, + <257, 361 >, + <290, 316 >, + <379, 385 >, + <332, 468 >, + <247, 302 >, + <350, 407 >, + <195, 281 >, + <306, 420 >, + <177, 414 >, + <306, 426 >, + <302, 450 >, + <312, 444 >, + <323, 462 >, + <219, 240 >, + <306, 323 >, + <203, 273 >, + <336, 397 >, + <290, 336 >, + <336, 354 >, + <316, 373 >, + <393, 474 >, + <416, 450 >, + <338, 481 >, + <393, 468 >, + <363, 450 >, + <452, 462 >, + <316, 329 >, + <361, 361 >, + <434, 450 >, + <348, 462 >, + <203, 348 >, + <195, 288 >, + <474, 481 >, + <464, 474 >, + <310, 456 >, + <329, 456 >, + <203, 420 >, + <203, 426 >, + <203, 310 >, + <235, 316 >, + <195, 261 >, + <464, 468 >, + <310, 391 >, + <329, 391 >, + <265, 397 >, + <350, 438 >, + <177, 342 >, + <265, 354 >, + <177, 438 >, + <219, 261 >, + <428, 474 >, + <257, 456 >, + <235, 468 >, + <410, 450 >, + <195, 240 >, + <177, 397 >, + <273, 294 >, + <391, 391 >, + <306, 385 >, + <247, 391 >, + <277, 407 >, + <367, 468 >, + <240, 481 >, + <354, 426 >, + <354, 420 >, + <261, 288 >, + <458, 474 >, + <381, 432 >, + <203, 235 >, + <189, 456 >, + <281, 373 >, + <277, 414 >, + <319, 450 >, + <342, 468 >, + <294, 450 >, + <288, 288 >, + <336, 438 >, + <336, 342 >, + <166, 462 >, + <257, 391 >, + <325, 462 >, + <342, 474 >, + <273, 481 >, + <240, 294 >, + <177, 354 >, + <281, 329 >, + <235, 474 >, + <367, 367 >, + <265, 438 >, + <350, 397 >, + <265, 342 >, + <369, 450 >, + <458, 468 >, + <428, 468 >, + <189, 391 >, + <379, 426 >, + <379, 420 >, + <219, 288 >, + <397, 450 >, + <367, 474 >, + <235, 367 >, + <354, 348 >, + <247, 456 >, + <391, 456 >, + <342, 367 >, + <253, 450 >, + <452, 481 >, + <385, 432 >, + <373, 468 >, + <261, 444 >, + <373, 474 >, + <336, 407 >, + <323, 481 >, + <1441, 1475 >, + <336, 414 >, + <446, 468 >, + <219, 281 >, + <361, 456 >, + <189, 265 >, + <203, 385 >, + <297, 450 >, + <288, 444 >, + <338, 462 >, + <329, 361 >, + <348, 481 >, + <310, 361 >, + <290, 474 >, + <177, 247 >, + <265, 407 >, + <290, 468 >, + <281, 432 >, + <219, 444 >, + <361, 391 >, + <316, 379 >, + <422, 420 >, + <446, 474 >, + <265, 414 >, + <422, 426 >, + <440, 432 >, + <277, 438 >, + <290, 367 >, + <277, 342 >, + <428, 450 >, + <219, 361 >, + <195, 265 >, + <235, 450 >, + <288, 361 >, + <284, 336 >, + <273, 354 >, + <319, 474 >, + <294, 474 >, + <385, 385 >, + <354, 379 >, + <325, 414 >, + <306, 329 >, + <273, 397 >, + <302, 336 >, + <166, 414 >, + <312, 456 >, + <350, 481 >, + <458, 450 >, + <369, 468 >, + <369, 474 >, + <288, 302 >, + <294, 468 >, + <306, 373 >, + <177, 294 >, + <240, 354 >, + <319, 468 >, + <342, 450 >, + <261, 302 >, + <253, 367 >, + <312, 391 >, + <277, 462 >, + <253, 474 >, + <261, 361 >, + <379, 379 >, + <367, 450 >, + <310, 444 >, + <329, 444 >, + <240, 397 >, + <397, 474 >, + <281, 385 >, + <410, 474 >, + <338, 438 >, + <219, 302 >, + <316, 323 >, + <410, 468 >, + <319, 367 >, + <166, 407 >, + <294, 367 >, + <253, 468 >, + <177, 481 >, + <203, 432 >, + <397, 468 >, + <325, 407 >, + <323, 397 >, + <189, 240 >, + <203, 373 >, + <338, 414 >, + <373, 450 >, + <297, 468 >, + <336, 462 >, + <265, 462 >, + <316, 348 >, + <297, 367 >, + <470, 426 >, + <470, 420 >, + <203, 329 >, + <203, 253 >, + <302, 316 >, + <348, 397 >, + <375, 456 >, + <284, 316 >, + <316, 310 >, + <316, 420 >, + <446, 450 >, + <348, 354 >, + <316, 426 >, + <387, 432 >, + <357, 456 >, + <338, 407 >, + <195, 456 >, + <399, 444 >, + <306, 432 >, + <297, 474 >, + <357, 391 >, + <195, 391 >, + <323, 354 >, + <166, 438 >, + <166, 342 >, + <325, 438 >, + <325, 342 >, + <290, 450 >, + <375, 391 >, + <325, 444 >, + <336, 361 >, + <166, 444 >, + <342, 385 >, + <253, 329 >, + <265, 302 >, + <354, 336 >, + <387, 468 >, + <294, 329 >, + <195, 294 >, + <265, 361 >, + <177, 265 >, + <235, 385 >, + <294, 373 >, + <319, 373 >, + <306, 468 >, + <387, 474 >, + <367, 385 >, + <281, 450 >, + <1495, 1534 >, + <332, 426 >, + <332, 420 >, + <189, 247 >, + <1206, 1225 >, + <440, 450 >, + <357, 481 >, + <302, 379 >, + <195, 481 >, + <361, 397 >, + <310, 414 >, + <329, 414 >, + <306, 474 >, + <297, 432 >, + <375, 481 >, + <344, 379 >, + <306, 367 >, + <253, 373 >, + <399, 438 >, + <284, 379 >, + <385, 450 >, + <166, 281 >, + <310, 407 >, + <329, 407 >, + <363, 379 >, + <166, 288 >, + <177, 456 >, + <219, 462 >, + <397, 432 >, + <203, 474 >, + <189, 354 >, + <277, 361 >, + <177, 391 >, + <166, 261 >, + <277, 302 >, + <297, 373 >, + <369, 432 >, + <247, 354 >, + <350, 456 >, + <312, 481 >, + <373, 385 >, + <391, 397 >, + <410, 432 >, + <247, 397 >, + <203, 468 >, + <253, 432 >, + <464, 420 >, + <464, 426 >, + <399, 414 >, + <257, 354 >, + <288, 462 >, + <399, 407 >, + <290, 385 >, + <338, 444 >, + <381, 450 >, + <257, 397 >, + <350, 391 >, + <1453, 1475 >, + <393, 420 >, + <261, 462 >, + <393, 426 >, + <310, 438 >, + <294, 432 >, + <329, 438 >, + <203, 367 >, + <319, 432 >, + <329, 342 >, + <310, 342 >, + <189, 397 >, + <297, 329 >, + <253, 385 >, + <302, 310 >, + <235, 373 >, + <440, 474 >, + <290, 432 >, + <348, 391 >, + <281, 367 >, + <385, 468 >, + <369, 385 >, + <357, 397 >, + <329, 462 >, + <310, 462 >, + <446, 432 >, + <261, 438 >, + <261, 342 >, + <195, 397 >, + <440, 468 >, + <361, 481 >, + <235, 329 >, + <177, 240 >, + <281, 474 >, + <288, 342 >, + <332, 379 >, + <288, 438 >, + <338, 361 >, + <387, 450 >, + <284, 348 >, + <265, 288 >, + <281, 468 >, + <302, 426 >, + <302, 420 >, + <342, 373 >, + <306, 450 >, + <323, 456 >, + <344, 420 >, + <344, 426 >, + <284, 426 >, + <284, 420 >, + <284, 310 >, + <316, 316 >, + <219, 342 >, + <219, 438 >, + <407, 426 >, + <367, 373 >, + <407, 420 >, + <385, 474 >, + <294, 385 >, + <319, 385 >, + <375, 397 >, + <348, 456 >, + <323, 391 >, + <195, 354 >, + <452, 456 >, + <302, 348 >, + <373, 432 >, + <277, 444 >, + <240, 391 >, + <265, 444 >, + <166, 361 >, + <325, 361 >, + <277, 288 >, + <235, 432 >, + <261, 407 >, + <302, 323 >, + <367, 432 >, + <399, 462 >, + <288, 407 >, + <290, 373 >, + <240, 456 >, + <373, 373 >, + <290, 329 >, + <219, 414 >, + <312, 354 >, + <257, 294 >, + <391, 481 >, + <247, 481 >, + <297, 385 >, + <336, 444 >, + <203, 450 >, + <189, 294 >, + <166, 302 >, + <195, 247 >, + <416, 420 >, + <416, 426 >, + <458, 432 >, + <273, 456 >, + <381, 474 >, + <428, 432 >, + <312, 397 >, + <219, 407 >, + <257, 481 >, + <284, 323 >, + <316, 336 >, + <434, 420 >, + <247, 294 >, + <434, 426 >, + <273, 391 >, + <381, 468 >, + <288, 414 >, + <363, 426 >, + <363, 420 >, + <342, 432 >, + <261, 414 >, + <189, 481 >, + <235, 379 >, + <281, 336 >, + <177, 444 >, + <357, 414 >, + <361, 462 >, + <195, 414 >, + <302, 385 >, + <375, 407 >, + <393, 432 >, + <369, 420 >, + <338, 391 >, + <369, 426 >, + <350, 444 >, + <342, 379 >, + <253, 420 >, + <253, 426 >, + <297, 323 >, + <312, 342 >, + <312, 438 >, + <253, 310 >, + <464, 432 >, + <166, 265 >, + <397, 420 >, + <323, 361 >, + <397, 426 >, + <367, 379 >, + <379, 450 >, + <410, 420 >, + <294, 348 >, + <319, 348 >, + <410, 426 >, + <329, 481 >, + <177, 281 >, + <310, 481 >, + <348, 361 >, + <319, 426 >, + <294, 426 >, + <375, 414 >, + <319, 420 >, + <294, 420 >, + <219, 247 >, + <294, 310 >, + <203, 257 >, + <338, 456 >, + <195, 407 >, + <332, 373 >, + <284, 385 >, + <354, 450 >, + <432, 481 >, + <344, 385 >, + <253, 348 >, + <357, 407 >, + <325, 456 >, + <426, 481 >, + <273, 361 >, + <297, 310 >, + <375, 438 >, + <325, 391 >, + <288, 397 >, + <281, 316 >, + <357, 438 >, + <219, 354 >, + <166, 456 >, + <316, 367 >, + <312, 414 >, + <297, 348 >, + <312, 407 >, + <219, 397 >, + <332, 432 >, + <391, 462 >, + <247, 462 >, + <177, 288 >, + <316, 474 >, + <422, 450 >, + <297, 420 >, + <297, 426 >, + <253, 323 >, + <240, 302 >, + <261, 354 >, + <288, 354 >, + <177, 261 >, + <316, 468 >, + <399, 481 >, + <257, 462 >, + <166, 391 >, + <189, 462 >, + <240, 361 >, + <195, 342 >, + <195, 438 >, + <470, 474 >, + <261, 397 >, + <290, 379 >, + <294, 323 >, + <373, 379 >, + <273, 302 >, + <363, 385 >, + <470, 468 >, + <265, 391 >, + <265, 456 >, + <336, 456 >, + <203, 277 >, + <342, 348 >, + <284, 329 >, + <354, 474 >, + <416, 432 >, + <363, 432 >, + <354, 468 >, + <253, 379 >, + <344, 373 >, + <284, 373 >, + <189, 342 >, + <189, 438 >, + <195, 462 >, + <379, 474 >, + <240, 444 >, + <361, 414 >, + <332, 385 >, + <357, 462 >, + <329, 397 >, + <310, 397 >, + <367, 426 >, + <336, 391 >, + <367, 420 >, + <257, 438 >, + <257, 342 >, + <354, 367 >, + <379, 468 >, + <235, 348 >, + <319, 379 >, + <294, 379 >, + <428, 420 >, + <428, 426 >, + <273, 444 >, + <458, 426 >, + <458, 420 >, + <247, 342 >, + <247, 438 >, + <391, 438 >, + <306, 336 >, + <302, 329 >, + <235, 310 >, + <203, 316 >, + <369, 379 >, + <342, 426 >, + <342, 420 >, + <302, 373 >, + <166, 240 >, + <235, 426 >, + <375, 462 >, + <235, 420 >, + <361, 407 >, + <310, 354 >, + <329, 354 >, + <434, 432 >, + <290, 323 >, + <257, 407 >, + <240, 288 >, + <288, 294 >, + <350, 361 >, + <363, 373 >, + <177, 361 >, + <273, 288 >, + <257, 414 >, + <288, 481 >, + <348, 444 >, + <446, 426 >, + <316, 450 >, + <446, 420 >, + <422, 474 >, + <219, 481 >, + <297, 379 >, + <422, 468 >, + <247, 414 >, + <391, 414 >, + <302, 432 >, + <261, 294 >, + <177, 302 >, + <470, 450 >, + <203, 336 >, + <235, 323 >, + <407, 432 >, + <277, 391 >, + <312, 462 >, + <284, 432 >, + <323, 444 >, + <247, 407 >, + <290, 310 >, + <391, 407 >, + <444, 481 >, + <344, 432 >, + <290, 426 >, + <290, 420 >, + <373, 426 >, + <452, 444 >, + <219, 294 >, + <373, 420 >, + <277, 456 >, + <361, 438 >, + <456, 481 >, + <189, 414 >, + <261, 481 >, + <290, 348 >, + <189, 407 >, + <277, 379 >, + <195, 367 >, + <166, 273 >, + <306, 481 >, + <261, 316 >, + <470, 438 >, + <219, 277 >, + <297, 456 >, + <325, 348 >, + <195, 468 >, + <387, 481 >, + <350, 373 >, + <219, 316 >, + <354, 462 >, + <166, 310 >, + <375, 474 >, + <325, 426 >, + <325, 420 >, + <281, 354 >, + <166, 420 >, + <166, 426 >, + <177, 329 >, + <332, 444 >, + <281, 397 >, + <240, 385 >, + <357, 474 >, + <297, 391 >, + <288, 316 >, + <379, 462 >, + <195, 474 >, + <361, 450 >, + <166, 348 >, + <357, 468 >, + <316, 342 >, + <316, 438 >, + <375, 468 >, + <177, 373 >, + <273, 385 >, + <177, 253 >, + <385, 397 >, + <312, 468 >, + <348, 385 >, + <253, 456 >, + <410, 456 >, + <265, 379 >, + <336, 379 >, + <261, 336 >, + <253, 391 >, + <288, 336 >, + <312, 474 >, + <166, 323 >, + <323, 385 >, + <350, 432 >, + <470, 414 >, + <294, 391 >, + <319, 391 >, + <219, 336 >, + <247, 450 >, + <391, 450 >, + <422, 462 >, + <284, 361 >, + <464, 444 >, + <397, 456 >, + <344, 361 >, + <302, 302 >, + <203, 481 >, + <177, 432 >, + <312, 367 >, + <316, 414 >, + <319, 456 >, + <462, 481 >, + <294, 456 >, + <316, 407 >, + <393, 444 >, + <257, 450 >, + <381, 397 >, + <203, 294 >, + <189, 450 >, + <338, 420 >, + <338, 426 >, + <369, 391 >, + <369, 456 >, + <302, 361 >, + <354, 407 >, + <281, 481 >, + <379, 414 >, + <361, 474 >, + <379, 407 >, + <219, 257 >, + <265, 323 >, + <195, 450 >, + <361, 468 >, + <440, 481 >, + <357, 450 >, + <446, 456 >, + <387, 397 >, + <177, 385 >, + <273, 373 >, + <273, 329 >, + <306, 397 >, + <277, 348 >, + <302, 444 >, + <361, 367 >, + <407, 444 >, + <284, 444 >, + <373, 391 >, + <344, 444 >, + <323, 432 >, + <422, 438 >, + <306, 354 >, + <240, 373 >, + <290, 456 >, + <373, 456 >, + <281, 294 >, + <277, 420 >, + <452, 432 >, + <277, 426 >, + <350, 385 >, + <240, 329 >, + <277, 310 >, + <385, 481 >, + <290, 391 >, + <375, 450 >, + <348, 432 >, + <325, 379 >, + <354, 414 >, + <166, 379 >, + <203, 247 >, + <310, 336 >, + <329, 336 >, + <316, 462 >, + <203, 397 >, + <189, 367 >, + <323, 329 >, + <312, 450 >, + <336, 348 >, + <434, 444 >, + <323, 373 >, + <240, 432 >, + <367, 456 >, + <247, 474 >, + <391, 474 >, + <257, 367 >, + <348, 373 >, + <265, 420 >, + <265, 426 >, + <422, 414 >, + <336, 426 >, + <302, 288 >, + <336, 420 >, + <367, 391 >, + <391, 468 >, + <247, 468 >, + <265, 310 >, + <470, 462 >, + <277, 323 >, + <203, 354 >, + <428, 456 >, + <458, 456 >, + <273, 432 >, + <189, 474 >, + <379, 438 >, + <338, 379 >, + <257, 474 >, + <414, 481 >, + <332, 361 >, + <310, 316 >, + <342, 391 >, + <416, 444 >, + <1191, 1225 >, + <235, 391 >, + <363, 444 >, + <257, 468 >, + <342, 456 >, + <381, 481 >, + <177, 235 >, + <235, 456 >, + <247, 367 >, + <265, 348 >, + <189, 468 >, + <354, 438 >, + <354, 342 >, + <369, 444 >, + <306, 407 >, + <177, 273 >, + <397, 444 >, + <440, 462 >, + <354, 354 >, + <166, 373 >, + <385, 462 >, + <387, 407 >, + <393, 456 >, + <247, 316 >, + <329, 367 >, + <203, 438 >, + <310, 367 >, + <203, 342 >, + <277, 385 >, + <350, 426 >, + <350, 420 >, + <306, 414 >, + <177, 420 >, + <177, 426 >, + <166, 329 >, + <189, 316 >, + <410, 444 >, + <177, 310 >, + <281, 462 >, + <290, 361 >, + <257, 316 >, + <379, 397 >, + <253, 444 >, + <329, 474 >, + <240, 379 >, + <464, 456 >, + <310, 474 >, + <329, 468 >, + <310, 468 >, + <387, 414 >, + <325, 373 >, + <166, 253 >, + <338, 432 >, + <189, 277 >, + <319, 444 >, + <294, 444 >, + <354, 397 >, + <273, 379 >, + <177, 348 >, + <247, 336 >, + <253, 288 >, + <203, 414 >, + <235, 361 >, + <189, 336 >, + <381, 462 >, + <387, 438 >, + <261, 450 >, + <306, 342 >, + <306, 438 >, + <399, 474 >, + <219, 450 >, + <332, 456 >, + <338, 373 >, + <177, 323 >, + <342, 361 >, + <332, 391 >, + <257, 336 >, + <297, 444 >, + <336, 385 >, + <316, 481 >, + <265, 385 >, + <399, 468 >, + <323, 379 >, + <288, 450 >, + <294, 288 >, + <235, 302 >, + <367, 361 >, + <325, 432 >, + <348, 379 >, + <203, 407 >, + <166, 432 >, + <195, 257 >, + <470, 481 >, + <381, 438 >, + <387, 462 >, + <306, 462 >, + <265, 432 >, + <379, 481 >, + <189, 257 >, + <336, 432 >, + <240, 348 >, + <363, 391 >, + <416, 456 >, + <273, 310 >, + <323, 323 >, + <166, 385 >, + <297, 361 >, + <420, 481 >, + <310, 450 >, + <329, 450 >, + <273, 348 >, + <177, 379 >, + <367, 444 >, + <440, 414 >, + <240, 310 >, + <385, 407 >, + <240, 426 >, + <240, 420 >, + <277, 329 >, + <281, 414 >, + <277, 373 >, + <273, 426 >, + <458, 444 >, + <273, 420 >, + <281, 407 >, + <428, 444 >, + <325, 385 >, + <434, 456 >, + <385, 414 >, + <363, 456 >, + <354, 481 >, + <235, 444 >, + <195, 336 >, + <350, 379 >, + <342, 444 >, + <323, 348 >, + <312, 336 >, + <284, 456 >, + <235, 288 >, + <344, 391 >, + <446, 444 >, + <468, 481 >, + <219, 474 >, + <399, 450 >, + <385, 438 >, + <381, 407 >, + <195, 277 >, + <166, 235 >, + <302, 391 >, + <316, 397 >, + <219, 468 >, + <422, 481 >, + <302, 456 >, + <288, 367 >, + <253, 302 >, + <261, 367 >, + <203, 462 >, + <240, 323 >, + <348, 348 >, + <323, 426 >, + <323, 420 >, + <344, 456 >, + <338, 385 >, + <290, 444 >, + <253, 361 >, + <261, 474 >, + <281, 342 >, + <281, 438 >, + <407, 456 >, + <265, 329 >, + <195, 316 >, + <288, 474 >, + <348, 420 >, + <348, 426 >, + <316, 354 >, + <265, 373 >, + <219, 367 >, + <288, 468 >, + <381, 414 >, + <336, 373 >, + <452, 426 >, + <284, 391 >, + <452, 420 >, + <277, 432 >, + <440, 438 >, + <373, 444 >, + <273, 323 >, + <261, 468 >, + <294, 302 >, + <319, 361 >, + <294, 361 >, + <481, 481 >, + <235, 407 >, + <342, 407 >, + <288, 432 >, + <385, 444 >, + <277, 474 >, + <344, 481 >, + <458, 414 >, + <253, 462 >, + <367, 414 >, + <373, 438 >, + <440, 444 >, + <361, 420 >, + <247, 323 >, + <361, 426 >, + <367, 407 >, + <277, 468 >, + <302, 294 >, + <261, 432 >, + <342, 414 >, + <446, 438 >, + <235, 414 >, + <294, 462 >, + <319, 462 >, + <166, 450 >, + <177, 336 >, + <407, 481 >, + <325, 450 >, + <203, 361 >, + <189, 323 >, + <284, 481 >, + <257, 323 >, + <369, 462 >, + <428, 414 >, + <410, 462 >, + <329, 385 >, + <310, 385 >, + <281, 444 >, + <219, 432 >, + <332, 397 >, + <357, 379 >, + <397, 462 >, + <290, 438 >, + <277, 367 >, + <422, 456 >, + <290, 342 >, + <203, 302 >, + <302, 481 >, + <195, 379 >, + <261, 329 >, + <379, 391 >, + <297, 462 >, + <177, 316 >, + <434, 481 >, + <354, 391 >, + <306, 361 >, + <416, 481 >, + <428, 438 >, + <261, 373 >, + <336, 474 >, + <373, 407 >, + <354, 456 >, + <247, 348 >, + <189, 310 >, + <363, 481 >, + <338, 450 >, + <189, 273 >, + <381, 444 >, + <189, 420 >, + <189, 426 >, + <312, 379 >, + <373, 414 >, + <367, 438 >, + <219, 373 >, + <265, 367 >, + <257, 420 >, + <257, 426 >, + <257, 310 >, + <219, 253 >, + <336, 367 >, + <290, 414 >, + <219, 329 >, + <379, 456 >, + <458, 438 >, + <257, 348 >, + <177, 277 >, + <247, 310 >, + <189, 348 >, + <281, 288 >, + <290, 407 >, + <265, 468 >, + <391, 420 >, + <247, 426 >, + <391, 426 >, + <247, 420 >, + <336, 468 >, + <288, 373 >, + <342, 342 >, + <288, 329 >, + <342, 438 >, + <235, 342 >, + <265, 474 >, + <446, 414 >, + <235, 438 >, + <195, 348 >, + <166, 474 >, + <325, 367 >, + <367, 462 >, + <316, 456 >, + <195, 273 >, + <332, 481 >, + <369, 407 >, + <253, 414 >, + <397, 414 >, + <325, 468 >, + <302, 354 >, + <397, 407 >, + <253, 407 >, + <166, 468 >, + <458, 462 >, + <344, 397 >, + <428, 462 >, + <369, 414 >, + <284, 397 >, + <310, 373 >, + <470, 456 >, + <329, 373 >, + <310, 329 >, + <1507, 1534 >, + <329, 329 >, + <297, 438 >, + <297, 342 >, + <450, 481 >, + <312, 323 >, + <319, 414 >, + <375, 420 >, + <294, 414 >, + <375, 426 >, + <235, 462 >, + <325, 474 >, + <342, 462 >, + <240, 336 >, + <277, 450 >, + <219, 235 >, + <319, 407 >, + <294, 407 >, + <166, 367 >, + <357, 420 >, + <203, 261 >, + <387, 444 >, + <357, 426 >, + <195, 310 >, + <195, 426 >, + <195, 420 >, + <399, 432 >, + <203, 288 >, + <306, 444 >, + <410, 414 >, + <284, 354 >, + <302, 397 >, + <316, 391 >, + <273, 336 >, + <361, 379 >, + <261, 385 >, + <240, 316 >, + <281, 361 >, + <363, 397 >, + <297, 407 >, + <177, 257 >, + <373, 462 >, + <348, 336 >, + <219, 385 >, + <247, 379 >, + <273, 316 >, + <281, 302 >, + <397, 438 >, + <338, 474 >, + <410, 438 >, + <257, 379 >, + <338, 468 >, + <393, 481 >, + <446, 462 >, + <294, 342 >, + <319, 438 >, + <294, 438 >, + <329, 432 >, + <310, 432 >, + <319, 342 >, + <203, 281 >, + <297, 414 >, + <312, 348 >, + <323, 336 >, + <338, 367 >, + <464, 481 >, + <369, 438 >, + <203, 444 >, + <336, 450 >, + <195, 323 >, + <265, 450 >, + <290, 462 >, + <288, 385 >, + <253, 438 >, + <253, 342 >, + <312, 426 >, + <189, 379 >, + <312, 420 >, + <434, 438 >, + <273, 367 >, + <219, 323 >, + <363, 438 >, + <257, 432 >, + <316, 361 >, + <235, 481 >, + <350, 450 >, + <302, 407 >, + <344, 414 >, + <325, 336 >, + <240, 468 >, + <294, 354 >, + <367, 481 >, + <319, 354 >, + <354, 444 >, + <294, 397 >, + <319, 397 >, + <189, 432 >, + <273, 474 >, + <342, 481 >, + <381, 456 >, + <381, 391 >, + <240, 367 >, + <253, 354 >, + <407, 414 >, + <273, 468 >, + <375, 385 >, + <261, 323 >, + <369, 397 >, + <284, 414 >, + <428, 481 >, + <361, 373 >, + <458, 481 >, + <288, 323 >, + <416, 438 >, + <195, 385 >, + <284, 407 >, + <332, 462 >, + <357, 385 >, + <391, 432 >, + <247, 432 >, + <344, 407 >, + <253, 397 >, + <379, 444 >, + <177, 450 >, + <302, 414 >, + <166, 336 >, + <235, 294 >, + <329, 379 >, + <397, 397 >, + <240, 474 >, + <310, 379 >, + <363, 407 >, + <297, 354 >, + <247, 329 >, + <385, 391 >, + <290, 481 >, + <416, 414 >, + <344, 438 >, + <219, 310 >, + <281, 391 >, + <446, 481 >, + <257, 329 >, + <361, 432 >, + <297, 397 >, + <189, 329 >, + <203, 240 >, + <323, 367 >, + <440, 456 >, + <166, 316 >, + <323, 474 >, + <452, 468 >, + <261, 310 >, + <363, 414 >, + <261, 426 >, + <219, 348 >, + <261, 420 >, + <393, 462 >, + <312, 385 >, + <373, 481 >, + <288, 310 >, + <348, 468 >, + <247, 373 >, + <434, 414 >, + <385, 456 >, + <288, 420 >, + <288, 426 >, + <348, 474 >, + <302, 342 >, + <302, 438 >, + <195, 235 >, + <323, 468 >, + <166, 277 >, + <452, 474 >, + <189, 253 >, + <464, 462 >, + <288, 348 >, + <422, 444 >, + <284, 438 >, + <284, 342 >, + <189, 373 >, + <261, 348 >, + <281, 456 >, + <219, 426 >, + <407, 438 >, + <219, 420 >, + <257, 373 >, + <348, 367 >, + <219, 273 >, + <350, 468 >, + <357, 373 >, + <344, 462 >, + <265, 316 >, + <195, 329 >, + <342, 397 >, + <203, 391 >, + <310, 310 >, + <393, 438 >, + <410, 481 >, + <302, 462 >, + <397, 481 >, + <310, 348 >, + <329, 348 >, + <407, 462 >, + <273, 450 >, + <369, 481 >, + <464, 438 >, + <195, 253 >, + <195, 373 >, + <332, 407 >, + <312, 432 >, + <284, 462 >, + <319, 481 >, + <253, 294 >, + <294, 481 >, + <177, 367 >, + <350, 474 >, + <235, 397 >, + <342, 354 >, + <277, 336 >, + <240, 450 >, + <235, 354 >, + <177, 474 >, + <367, 397 >, + <350, 367 >, + <329, 420 >, + <310, 426 >, + <329, 426 >, + <310, 420 >, + <189, 235 >, + <203, 456 >, + <332, 414 >, + <361, 385 >, + <294, 294 >, + <253, 481 >, + <177, 468 >, + <203, 265 >, + <393, 414 >, + <375, 432 >, + <373, 397 >, + <434, 462 >, + <348, 450 >, + <354, 361 >, + <277, 316 >, + <464, 414 >, + <312, 329 >, + <265, 336 >, + <416, 462 >, + <189, 385 >, + <323, 450 >, + <288, 379 >, + <332, 438 >, + <452, 450 >, + <363, 462 >, + <387, 456 >, + <357, 432 >, + <306, 391 >, + <247, 385 >, + <399, 426 >, + <399, 420 >, + <195, 432 >, + <312, 373 >, + <166, 257 >, + <219, 379 >, + <297, 481 >, + <393, 407 >, + <316, 444 >, + <306, 456 >, + <290, 354 >, + <310, 323 >, + <329, 323 >, + <257, 385 >, + <470, 444 >, + <336, 336 >, + <290, 397 >, + <261, 379 >}; + dontNestGroupsINT + = {<{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, {310, 316, 284, 290, 297} >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, {265, 277, 281, 302, 235, 273, 240, 257, 247, 261, 288, 294, 253} >, + <{474, 438, 391, 426, 432, 462, 414, 385, 420, 407, 450, 397, 444, 456, 481, 468}, {375, 385} >, + <{481}, {474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468} >, + <{336, + 474, + 438, + 348, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, {336, 325, 348, 354, 342, 319} >, + <{474, + 438, + 391, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, {357, 363, 373} >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, {323, 312, 329, 306} >, + <{474, 438, 391, 426, 432, 462, 414, 420, 407, 450, 397, 444, 456, 481, 468}, {391, 381} >, + <{1534}, {1507, 1495} >, + <{474, + 438, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 367, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, {338, 350, 361, 344, 332, 367} >, + <{1225}, {1191, 1206} >, + <{1475}, {1441, 1453} >, + <{474, 438, 426, 432, 462, 414, 420, 407, 450, 397, 444, 456, 481, 468}, {387, 397} >, + <{474, 438, 426, 432, 462, 414, 420, 407, 450, 444, 456, 481, 468}, {399, 393} >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, {452, 440, 470, 422, 407, 464, 446, 410, 434, 416, 458, 428} >, + <{474, 438, 391, 426, 432, 462, 414, 385, 407, 450, 397, 444, 456, 481, 468, 379, 420}, {379, 369} >, + <{277, + 474, + 438, + 361, + 426, + 462, + 414, + 316, + 450, + 373, + 336, + 265, + 323, + 348, + 273, + 240, + 257, + 391, + 247, + 432, + 329, + 310, + 444, + 261, + 456, + 288, + 385, + 281, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 235, + 342, + 367, + 294, + 397, + 253}, {166, 177, 195, 189, 219, 203} >}; + + set[rel[set[int] children, int parent]] allGINT + = {{<{474, + 438, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 367, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, 338 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 273 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 316 >, + <{474, 438, 391, 426, 432, 462, 414, 385, 407, 450, 397, 444, 456, 481, 468, 379, 420}, 369 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 458 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 294 >, + <{1225}, 1206 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 290 >, + <{481}, 432 >, + <{1475}, 1453 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 323 >, + <{474, 438, 426, 432, 462, 414, 420, 407, 450, 444, 456, 481, 468}, 393 >, + <{336, + 474, + 438, + 348, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 354 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 422 >, + <{277, + 474, + 438, + 361, + 426, + 462, + 414, + 316, + 450, + 373, + 336, + 265, + 323, + 348, + 273, + 240, + 257, + 391, + 247, + 432, + 329, + 310, + 444, + 261, + 456, + 288, + 385, + 281, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 235, + 342, + 367, + 294, + 397, + 253}, 166 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 470 >, + <{474, + 438, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 367, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, 361 >, + <{474, 438, 391, 426, 432, 462, 414, 420, 407, 450, 397, 444, 456, 481, 468}, 381 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 329 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 277 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 428 >, + <{481}, 426 >, + <{481}, 420 >, + <{1534}, 1495 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 446 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 257 >, + <{277, + 474, + 438, + 361, + 426, + 462, + 414, + 316, + 450, + 373, + 336, + 265, + 323, + 348, + 273, + 240, + 257, + 391, + 247, + 432, + 329, + 310, + 444, + 261, + 456, + 288, + 385, + 281, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 235, + 342, + 367, + 294, + 397, + 253}, 189 >, + <{336, + 474, + 438, + 348, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 342 >, + <{474, 438, 391, 426, 432, 462, 414, 385, 420, 407, 450, 397, 444, 456, 481, 468}, 375 >, + <{474, + 438, + 391, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, 363 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 310 >, + <{474, 438, 426, 432, 462, 414, 420, 407, 450, 397, 444, 456, 481, 468}, 387 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 297 >, + <{474, + 438, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 367, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, 332 >, + <{277, + 474, + 438, + 361, + 426, + 462, + 414, + 316, + 450, + 373, + 336, + 265, + 323, + 348, + 273, + 240, + 257, + 391, + 247, + 432, + 329, + 310, + 444, + 261, + 456, + 288, + 385, + 281, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 235, + 342, + 367, + 294, + 397, + 253}, 177 >, + <{481}, 450 >, + <{474, 438, 426, 432, 462, 414, 420, 407, 450, 444, 456, 481, 468}, 399 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 253 >, + <{1475}, 1441 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 284 >, + <{277, + 474, + 438, + 361, + 426, + 462, + 414, + 316, + 450, + 373, + 336, + 265, + 323, + 348, + 273, + 240, + 257, + 391, + 247, + 432, + 329, + 310, + 444, + 261, + 456, + 288, + 385, + 281, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 235, + 342, + 367, + 294, + 397, + 253}, 219 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 464 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 235 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 416 >, + <{481}, 474 >, + <{481}, 468 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 288 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 261 >, + <{474, + 438, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 367, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, 350 >, + <{336, + 474, + 438, + 348, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 348 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 281 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 312 >, + <{474, 438, 426, 432, 462, 414, 420, 407, 450, 397, 444, 456, 481, 468}, 397 >, + <{481}, 462 >, + <{474, 438, 391, 426, 432, 462, 414, 420, 407, 450, 397, 444, 456, 481, 468}, 391 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 302 >, + <{481}, 438 >, + <{1534}, 1507 >, + <{277, + 474, + 438, + 361, + 426, + 462, + 414, + 316, + 450, + 373, + 336, + 265, + 323, + 348, + 273, + 240, + 257, + 391, + 247, + 432, + 329, + 310, + 444, + 261, + 456, + 288, + 385, + 281, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 235, + 342, + 367, + 294, + 397, + 253}, 203 >, + <{474, + 438, + 391, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, 357 >, + <{277, + 474, + 438, + 361, + 426, + 462, + 414, + 316, + 450, + 373, + 336, + 265, + 323, + 348, + 273, + 240, + 257, + 391, + 247, + 432, + 329, + 310, + 444, + 261, + 456, + 288, + 385, + 281, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 235, + 342, + 367, + 294, + 397, + 253}, 195 >, + <{474, + 438, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 367, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, 367 >, + <{481}, 414 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 410 >, + <{1225}, 1191 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 265 >, + <{474, + 438, + 391, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, 373 >, + <{474, 438, 391, 426, 432, 462, 414, 385, 407, 450, 397, 444, 456, 481, 468, 379, 420}, 379 >, + <{336, + 474, + 438, + 348, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 319 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 240 >, + <{481}, 481 >, + <{481}, 456 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 316, + 450, + 373, + 323, + 348, + 432, + 329, + 310, + 444, + 456, + 288, + 481, + 468, + 379, + 354, + 420, + 302, + 407, + 342, + 367, + 294, + 397}, 247 >, + <{336, + 474, + 438, + 348, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 325 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 440 >, + <{474, 438, 391, 426, 432, 462, 414, 385, 420, 407, 450, 397, 444, 456, 481, 468}, 385 >, + <{336, + 474, + 438, + 391, + 361, + 426, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 323, + 348, + 432, + 329, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 306 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 452 >, + <{336, + 474, + 438, + 348, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 397, + 444, + 456, + 481, + 468, + 379, + 354, + 420, + 342, + 367}, 336 >, + <{481}, 444 >, + <{474, + 438, + 391, + 361, + 426, + 432, + 462, + 414, + 385, + 407, + 450, + 373, + 367, + 397, + 444, + 456, + 481, + 468, + 379, + 420}, 344 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 407 >, + <{474, 438, 426, 432, 462, 414, 420, 450, 444, 456, 481, 468}, 434 >}}; + rel[set[int] children, int parent] allG + = {| int p <- dontNestINT.parent}; + + println("{allG }== allGINT: <{allG} == allGINT>"); + + rel[set[int] children, set[int] parents] dontNestGroups + = { + | rel[set[int] children, int parent] g := + {| p <- dontNestINT.parent}, c <- g.children + }; + + rel[set[int] children, set[int] parents] dontNestGroups1 + = {| rel[set[int] children, int parent] g := allG, c <- g.children}; + + rel[set[int] children, set[int] parents] dontNestGroups2 + = {| c <- allG.children}; + + println("dontNestGroups: "); + println("dontNestGroups1: "); + println("dontNestGroups2: "); + + println( + "allG[{...}] = " + ); + + println( + "dontNestGroups == dontNestGroupsINT: " + ); + println( + "dontNestGroups1 == dontNestGroupsINT: " + ); + println( + "dontNestGroups2 == dontNestGroupsINT: " + ); + + return dontNestGroupsINT == dontNestGroups; +} + +value compare4() { + r = {<{1, 2}, "a" >, + <{10, 20}, "b" >}; + return r[{10, 20}]; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/LayoutTests.rsc| +module lang::rascal::grammar::tests::LayoutTests + +import Grammar; +import lang::rascal::grammar::definition::Layout; + +import lang::rascal::grammar::tests::TestGrammars; + +test bool intermix1() + = intermix([lit("a")], layouts("$default$"), {}) == [lit("a")]; + +test bool intermix2() + = intermix([lit("a"), lit("b")], layouts("$default$"), {}) == [lit("a"), layouts("$default$"), lit("b")]; + +test bool intermix3() + = intermix([lit("a"), lit("b"), lit("c")], layouts("$default$"), {}) == [ + lit("a"), + layouts("$default$"), + lit("b"), + layouts("$default$"), + lit("c") + ]; + +test bool intermix4() + = intermix( + [lit("a"), \iter(sort("Exp")), lit("c")], layouts("$default$"), {} + ) == [ + lit("a"), + layouts("$default$"), + \iter-seps ( + sort("Exp"), + [layouts("$default$")] + ), + layouts("$default$"), + lit("c") + ]; + +test bool intermix5() + = intermix( + [lit("a"), \iter-star(sort("Exp")), lit("c")], layouts("$default$"), {} + ) == [ + lit("a"), + layouts("$default$"), + \iter-star-seps ( + sort("Exp"), + [layouts("$default$")] + ), + layouts("$default$"), + lit("c") + ]; + +test bool intermix6() + = intermix( + [lit("a"), \iter-seps ( + sort("Exp"), + [lit("b")] + ), lit("c")], + layouts("$default$"), + {} + ) == [ + lit("a"), + layouts("$default$"), + \iter-seps ( + sort("Exp"), + [layouts("$default$"), lit("b"), layouts("$default$")] + ), + layouts("$default$"), + lit("c") + ]; + +test bool layouts1() + = layouts(GEXP, layouts("$default$"), {}) == grammar( + {sort("E")}, + ( + lit("+") : choice(lit("+"), {prod(lit("+"), [\char-class([range(43, 43)])], {})}), + lit("*") : choice(lit("*"), {prod(lit("*"), [\char-class([range(42, 42)])], {})}), + sort("B") : choice( + sort("B"), + {prod(sort("B"), [lit("0")], {}), prod(sort("B"), [lit("1")], {})} + ), + lit("0") : choice(lit("0"), {prod(lit("0"), [\char-class([range(48, 48)])], {})}), + sort("E") : choice( + sort("E"), + {prod(sort("E"), [sort("B")], {}), + prod( + sort("E"), + [ + sort("E"), + layouts("$default$"), + lit("+"), + layouts("$default$"), + sort("B") + ], + {} + ), + prod( + sort("E"), + [ + sort("E"), + layouts("$default$"), + lit("*"), + layouts("$default$"), + sort("B") + ], + {} + )} + ), + lit("1") : choice(lit("1"), {prod(lit("1"), [\char-class([range(49, 49)])], {})}) + ) + ); +value main() = layouts(GEXPPRIO, layouts("$default$"), {}); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/LiteralsTests.rsc| +module lang::rascal::grammar::tests::LiteralsTests + +import lang::rascal::\syntax::Rascal; +import lang::rascal::grammar::definition::Literals; +import ParseTree; + +test bool tstLiteral1() = literal("") == prod(lit(""), [], {}); +test bool tstLiteral2() + = literal("a") == prod(lit("a"), [\char-class([range(97, 97)])], {}); +test bool tstLiteral3() + = literal("ab") == prod( + lit("ab"), + [\char-class([range(97, 97)]), \char-class([range(98, 98)])], + {} + ); + +test bool tstCiLiteral1() = ciliteral("") == prod(cilit(""), [], {}); +test bool tstCiLiteral2() + = ciliteral("a") == prod(cilit("a"), [\char-class([range(97, 97), range(65, 65)])], {}); +test bool tstCiLiteral3() + = ciliteral("ab") == prod( + cilit("ab"), + [ + \char-class([range(97, 97), range(65, 65)]), + \char-class([range(98, 98), range(66, 66)]) + ], + {} + ); + +test bool tstStr2Syms1() = str2syms("") == []; +test bool tstStr2Syms2() = str2syms("a") == [\char-class([range(97, 97)])]; +test bool tstStr2Syms3() + = str2syms("ab") == [\char-class([range(97, 97)]), \char-class([range(98, 98)])]; + +test bool tsCistr2syms1() = cistr2syms("") == []; +test bool tsCistr2syms2() + = cistr2syms("a") == [\char-class([range(97, 97), range(65, 65)])]; +test bool tsCistr2syms3() + = cistr2syms("A") == [\char-class([range(65, 65), range(97, 97)])]; + +test bool tstUnescapeSC1() + = unescapeLiteral((StringConstant) `"a"`) == "a"; +test bool tstUnescapeSC2() + = unescapeLiteral((StringConstant) `"\\t"`) == "\t"; +test bool tstUnescapeSC3() + = unescapeLiteral((StringConstant) `"a\\tb"`) == "a\tb"; +test bool tstUnescapeSC4() + = unescapeLiteral((StringConstant) `"\\'"`) == "\'"; +test bool tstUnescapeSC5() + = unescapeLiteral((StringConstant) `"a\\tb\\'c"`) == "a\tb\'c"; + +test bool tstUnescapeCI1() + = unescapeLiteral((CaseInsensitiveStringConstant) `'a'`) == "a"; +test bool tstUnescapeCI2() + = unescapeLiteral((CaseInsensitiveStringConstant) `'\\t'`) == "\t"; +test bool tstUnescapeCI3() + = unescapeLiteral((CaseInsensitiveStringConstant) `'a\\tb'`) == "a\tb"; +test bool tstUnescapeCI4() + = unescapeLiteral((CaseInsensitiveStringConstant) `'\\''`) == "\'"; +test bool tstUnescapeCI5() + = unescapeLiteral((CaseInsensitiveStringConstant) `'a\\tb\\'c'`) == "a\tb\'c"; + +test bool tstUnescape1() = unescapeLiteral("a") == "a"; +test bool tstUnescape2() = unescapeLiteral("\\t") == "\t"; +test bool tstUnescape3() = unescapeLiteral("a\\tb") == "a\tb"; +test bool tstUnescape4() = unescapeLiteral("\\\'") == "\'"; +test bool tstUnescape5() = unescapeLiteral("a\\tb\\\'c") == "a\tb\'c"; + +test bool tstCharacter1() = character((StringCharacter) `a`) == "a"; +test bool tstCharacter2() = character((StringCharacter) `\\t`) == "\t"; +test bool tstCharacter3() = character((StringCharacter) `\\\<`) == "\<"; +test bool tstCharacter4() = character((StringCharacter) `\\'`) == "\'"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/LookaheadTests.rsc| +module lang::rascal::grammar::tests::LookaheadTests + +import Grammar; +import lang::rascal::grammar::definition::Regular; +import lang::rascal::grammar::definition::Characters; +import lang::rascal::grammar::definition::Productions; +import ParseTree; +extend lang::rascal::grammar::Lookahead; + +// -------- Examples and tests ------------------- +public Grammar G0 = simple({sort("S")}, {}) ; + +test bool testEmpty1() = first(G0) == (); + +test bool testEmpty2() = firstAndFollow(G0) == <(), (sort("S") : {eoi()})>; + +private Production pr(Symbol rhs, list[Symbol] lhs) { + return prod(rhs, lhs, {}); +} + +public Grammar Lit1 + = simple( + {}, + {pr ( + lit("*"), + [\char-class([range(42, 42)])] + ), + pr ( + lit("+"), + [\char-class([range(43, 43)])] + ), + pr ( + lit("0"), + [\char-class([range(48, 48)])] + ), + pr ( + lit("1"), + [\char-class([range(49, 49)])] + )} + ) ; + +public Grammar G1 + = simple( + {sort("E")}, + {pr ( + sort("E"), + [sort("E"), lit("*"), sort("B")] + ), + pr ( + sort("E"), + [sort("E"), lit("+"), sort("B")] + ), + pr ( + sort("E"), + [sort("B")] + ), + pr ( + sort("B"), + [lit("0")] + ), + pr ( + sort("B"), + [lit("1")] + )} + + Lit1.productions + ) ; + +test bool testUsed1() + = usedSymbols(G1) >= {lit("0"), lit("1"), sort("E"), sort("B"), lit("*"), lit("+")}; + +test bool testDefined1() + = definedSymbols(G1) == {sort("E"), sort("B"), lit("+"), lit("*"), lit("0"), lit("1")}; + +test bool testStartsDefined() = G1.starts < definedSymbols(G1); + +public SymbolUse firstLit1 + = ( + lit("0") : {\char-class([range(48, 48)])}, + lit("1") : {\char-class([range(49, 49)])}, + lit("*") : {\char-class([range(42, 42)])}, + lit("+") : {\char-class([range(43, 43)])} + ) ; + +test bool first1() + = SymbolUse F := first(G1) + && F[sort("E")] == {\char-class([range(49, 49)]), \char-class([range(48, 48)])} + && F[sort("B")] == {\char-class([range(49, 49)]), \char-class([range(48, 48)])}; + +public Grammar G2 + = simple( + {sort("E")}, + {pr ( + sort("E"), + [sort("E"), lit("*"), sort("B")] + ), + pr ( + sort("E"), + [sort("E"), lit("+"), sort("B")] + ), + pr ( + sort("E"), + [sort("B")] + ), + pr ( + sort("B"), + [lit("0")] + ), + pr ( + sort("B"), + [lit("1")] + )} + + Lit1.productions + ) ; + +test bool first2() + = SymbolUse F := first(G2) + && F[sort("E")] == {\char-class([range(48, 48)]), \char-class([range(49, 49)])} + && F[sort("B")] == {\char-class([range(48, 48)]), \char-class([range(49, 49)])}; + +public Grammar G3 + = simple( + {sort("E")}, + {pr ( + sort("E"), + [sort("T"), sort("E1")] + ), + pr ( + sort("E1"), + [lit("+"), sort("T"), sort("E1")] + ), + pr ( + lit("+"), + [\char-class([range(43, 43)])] + ), + pr ( + sort("E1"), + [] + ), + pr ( + sort("T"), + [sort("F"), sort("T1")] + ), + pr ( + sort("T1"), + [lit("*"), sort("F"), sort("T1")] + ), + pr ( + lit("*"), + [\char-class([range(42, 42)])] + ), + pr ( + sort("T1"), + [] + ), + pr ( + sort("F"), + [lit("("), sort("E"), lit(")")] + ), + pr ( + lit("("), + [\char-class([range(40, 40)])] + ), + pr ( + lit(")"), + [\char-class([range(41, 41)])] + ), + pr ( + sort("F"), + [lit("id")] + ), + pr ( + lit("id"), + [\char-class([range(105, 105)]), \char-class([range(100, 100)])] + )} + ) ; + +private SymbolUse F3 = first(G3) ; + +test bool tF31() + = F3[sort("F")] == {\char-class([range(105, 105)]), \char-class([range(40, 40)])}; +test bool tF32() = F3[sort("T")] == F3[sort("F")]; +test bool tF33() = F3[sort("E")] == F3[sort("T")]; +test bool tF34() = F3[lit("*")] == {\char-class([range(42, 42)])}; +test bool tF35() = F3[lit("+")] == {\char-class([range(43, 43)])}; +test bool tF36() = F3[lit("id")] == {\char-class([range(105, 105)])}; +test bool tF37() = F3[sort("E1")] == {Symbol::empty()} + F3[lit("+")]; +test bool tF38() = F3[sort("T1")] == {Symbol::empty()} + F3[lit("*")]; +test bool tF39() = F3[lit("(")] == {\char-class([range(40, 40)])}; +test bool tF310() = F3[lit(")")] == {\char-class([range(41, 41)])}; + +public SymbolUse Fol3() = follow(G3, first(G3)); + +test bool tFol31() + = Fol3()[sort("E")] == {\char-class([range(41, 41)]), eoi()}; +test bool tFol32() + = Fol3()[sort("E1")] == {\char-class([range(41, 41)]), eoi()}; +test bool tFol33() + = Fol3()[sort("T")] == {\char-class([range(43, 43)]), \char-class([range(41, 41)]), eoi()}; +test bool tFol34() + = Fol3()[sort("T1")] == {\char-class([range(43, 43)]), \char-class([range(41, 41)]), eoi()}; +test bool tFol35() + = Fol3()[sort("F")] == {\char-class([range(43, 43)]), + \char-class([range(42, 42)]), + \char-class([range(41, 41)]), + eoi()}; + +public Grammar Session + = simple( + {sort("Session")}, + {pr ( + sort("Session"), + [sort("Facts"), sort("Question")] + ), + pr ( + sort("Session"), + [lit("("), sort("Session"), lit(")"), sort("Session")] + ), + pr ( + lit("("), + [\char-class([range(40, 40)])] + ), + pr ( + lit(")"), + [\char-class([range(41, 41)])] + ), + pr ( + sort("Facts"), + [sort("Fact"), sort("Facts")] + ), + pr ( + sort("Facts"), + [] + ), + pr ( + sort("Fact"), + [lit("!"), sort("STRING")] + ), + pr ( + lit("!"), + [\char-class([range(33, 33)])] + ), + pr ( + sort("Question"), + [lit("?"), sort("STRING")] + ), + pr ( + lit("?"), + [\char-class([range(63, 63)])] + ), + pr ( + sort("STRING"), + [lit("a")] + ), + pr ( + lit("a"), + [\char-class([range(97, 97)])] + )} + ) ; + +private SymbolUse SF = first(Session) ; + +test bool tSF1() = SF[sort("Question")] == {\char-class([range(63, 63)])}; +test bool tSF2() + = SF[sort("Session")] == {\char-class([range(33, 33)]), + \char-class([range(40, 40)]), + \char-class([range(63, 63)])}; +test bool tSF3() + = SF[sort("Facts")] == {\char-class([range(33, 33)]), Symbol::empty()}; +test bool tSF4() = SF[lit("a")] == {\char-class([range(97, 97)])}; +test bool tSF5() = SF[lit("!")] == {\char-class([range(33, 33)])}; +test bool tSF6() = SF[lit("?")] == {\char-class([range(63, 63)])}; +test bool tSF7() = SF[lit("(")] == {\char-class([range(40, 40)])}; +test bool tSF8() = SF[lit(")")] == {\char-class([range(41, 41)])}; +test bool tSF9() = SF[sort("STRING")] == {\char-class([range(97, 97)])}; +test bool tSF10() = SF[sort("Fact")] == {\char-class([range(33, 33)])}; + +test bool testFollow() + = follow(Session, first(Session)) >= ( + lit(")") : {\char-class([range(63, 63)]), + \char-class([range(40, 40)]), + \char-class([range(33, 33)])}, + lit("(") : {\char-class([range(63, 63)]), + \char-class([range(40, 40)]), + \char-class([range(33, 33)])}, + sort("STRING") : {\char-class([range(63, 63)]), + eoi(), + \char-class([range(41, 41)]), + \char-class([range(33, 33)])}, + sort("Session") : {eoi(), \char-class([range(41, 41)])}, + lit("?") : {\char-class([range(97, 97)])}, + sort("Fact") : {\char-class([range(63, 63)]), \char-class([range(33, 33)])}, + sort("Facts") : {\char-class([range(63, 63)])}, + sort("Question") : {eoi(), \char-class([range(41, 41)])}, + lit("!") : {\char-class([range(97, 97)])}, + lit("a") : {\char-class([range(63, 63)]), + eoi(), + \char-class([range(41, 41)]), + \char-class([range(33, 33)])} + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/ParserGeneratorTests.rsc| +module lang::rascal::grammar::tests::ParserGeneratorTests + +import lang::rascal::grammar::ParserGenerator; +import Grammar; +import lang::rascal::grammar::definition::Parameters; +import lang::rascal::grammar::definition::Literals; +import IO; +import ParseTree; +import lang::rascal::grammar::tests::TestGrammars; +import String; + +// -------- Examples and tests ------------------- +test bool tstEsc1() = esc(sort("S")) == "sort(\\\"S\\\")"; +test bool tstEsc2() = esc(lit(":")) == "lit(\\\":\\\")"; +test bool tstEsc3() = esc(lit("\"")) == "lit(\\\"\\\\\\\"\\\")"; + +test bool tstEsc4() = "" == "sort(\\\"S\\\")"; +test bool tstEsc5() = "" == "lit(\\\"\\\\\\\"\\\")"; + +test bool tstEsc6() = "" == "<"sort(\\\"S\\\")">"; +test bool tstEsc7() = "" == "<"lit(\\\":\\\")">"; +test bool tstEsc8() = "" == "<"lit(\\\"\\\\\\\"\\\")">"; + +test bool tstEsc9() + = "<for (s <- [sort("S"), lit("\"")]) {><}>" == "sort(\\\"S\\\")lit(\\\"\\\\\\\"\\\")"; +test bool tstEsc10() + = "\"<for (s <- [sort("S"), lit("\"")]) {>\"\"<}>\"" == "\"\"sort(\\\"S\\\")\"\"lit(\\\"\\\\\\\"\\\")\"\""; + +test bool tstExpandParameterizedSymbols1() + = expandParameterizedSymbols(GEMPTY) == GEMPTY; +test bool tstExpandParameterizedSymbols2() + = expandParameterizedSymbols(G0) == G0; +test bool tstExpandParameterizedSymbols3() + = expandParameterizedSymbols(GEXP) == GEXP; +test bool tstExpandParameterizedSymbols4() + = expandParameterizedSymbols(GEXPPRIO) == GEXPPRIO; + +test bool tstLiterals1() = literals(GEMPTY) == GEMPTY; +test bool tstLiterals2() = literals(G0) == G0; +test bool tstLiterals3() = literals(GEXP) == GEXP; +test bool tstLiterals4() = literals(GEXPPRIO) == GEXPPRIO; + +test bool tstUnique0() = makeUnique(GEMPTY) == grammar({sort("S")}, ()); + +test bool tstUnique1() + = makeUnique(G0) == grammar( + {sort("S")}, + ( + sort("S") : choice(sort("S", id = 2), {prod(sort("S", id = 3), [lit("0", id = 4)], {})}), + lit("0") : choice( + lit("0", id = 5), + {prod(lit("0", id = 6), [\char-class([ range(48, 48) ], id = 7 )], {})} + ) + ) + ); + +value main() { + g = grammar({sort("S")}, ()); + el = compose(g, grammar({}, {literal(s)| /lit(s) <- g})); + println("el:"); + iprintln(el); + println("g"); + iprintln(g); + println("g == el: "); + return 0; +} + +test bool tstUnique2() + = makeUnique(GEXP) == grammar( + {sort("E")}, + ( + lit("+") : choice( + lit("+", id = 2), + {prod(lit("+", id = 3), [\char-class([ range(43, 43) ], id = 4 )], {})} + ), + lit("*") : choice( + lit("*", id = 5), + {prod(lit("*", id = 6), [\char-class([ range(42, 42) ], id = 7 )], {})} + ), + sort("B") : choice( + sort("B", id = 8), + {prod(sort("B", id = 11), [lit("1", id = 12)], {}), + prod(sort("B", id = 9), [lit("0", id = 10)], {})} + ), + lit("0") : choice( + lit("0", id = 13), + {prod(lit("0", id = 14), [\char-class([ range(48, 48) ], id = 15 )], {})} + ), + sort("E") : choice( + sort("E", id = 16), + {prod( + sort("E", id = 23), [sort("E", id = 24), lit("*", id = 25), sort("B", id = 26)], {} + ), + prod(sort("E", id = 17), [sort("B", id = 18)], {}), + prod( + sort("E", id = 19), [sort("E", id = 20), lit("+", id = 21), sort("B", id = 22)], {} + )} + ), + lit("1") : choice( + lit("1", id = 27), + {prod(lit("1", id = 28), [\char-class([ range(49, 49) ], id = 29 )], {})} + ) + ) + ); + +test bool tstGenerateNewItems1() + = generateNewItems(makeUnique(GEMPTY)) == (); + +test bool tstGenerateNewItems2() + = generateNewItems(makeUnique(G0)) == ( + sort("S") : ( + item(prod(sort("S"), [lit("0")], {}), 0) : <"new LiteralStackNode\(4, 0, cHJvZChsaXQoIjAiKSxbXGNoYXItY2xhc3MoW3JhbmdlKDQ4LDQ4KV0pXSx7fSk00, new int[] {48}, null, null)", + 4> + ), + lit("0") : ( + item(prod(lit("0"), [\char-class([range(48, 48)])], {}), 0) : <"new CharStackNode\(7, 0, new int[][]{{48,48}}, null, null)", + 7> + ) + ); + +test bool tstGenerateNewItems3() + = generateNewItems(makeUnique(GEXP)) == ( + lit("+") : ( + item(prod(lit("+"), [\char-class([range(43, 43)])], {}), 0) : <"new CharStackNode\(4, 0, new int[][]{{43,43}}, null, null)", + 4> + ), + lit("*") : ( + item(prod(lit("*"), [\char-class([range(42, 42)])], {}), 0) : <"new CharStackNode\(7, 0, new int[][]{{42,42}}, null, null)", + 7> + ), + sort("B") : ( + item(prod(sort("B"), [lit("0")], {}), 0) : <"new LiteralStackNode\(10, 0, cHJvZChsaXQoIjAiKSxbXGNoYXItY2xhc3MoW3JhbmdlKDQ4LDQ4KV0pXSx7fSk00, new int[] {48}, null, null)", + 10>, + item(prod(sort("B"), [lit("1")], {}), 0) : <"new LiteralStackNode\(12, 0, cHJvZChsaXQoIjEiKSxbXGNoYXItY2xhc3MoW3JhbmdlKDQ5LDQ5KV0pXSx7fSk00, new int[] {49}, null, null)", + 12> + ), + lit("0") : ( + item(prod(lit("0"), [\char-class([range(48, 48)])], {}), 0) : <"new CharStackNode\(15, 0, new int[][]{{48,48}}, null, null)", + 15> + ), + sort("E") : ( + item(prod(sort("E"), [sort("B")], {}), 0) : <"new NonTerminalStackNode\(18, 0, \"B\", null, null)", + 18>, + item(prod(sort("E"), [sort("E"), lit("*"), sort("B")], {}), 1) : <"new LiteralStackNode\(25, 1, cHJvZChsaXQoIioiKSxbXGNoYXItY2xhc3MoW3JhbmdlKDQyLDQyKV0pXSx7fSk00, new int[] {42}, null, null)", + 25>, + item(prod(sort("E"), [sort("E"), lit("+"), sort("B")], {}), 1) : <"new LiteralStackNode\(21, 1, cHJvZChsaXQoIisiKSxbXGNoYXItY2xhc3MoW3JhbmdlKDQzLDQzKV0pXSx7fSk00, new int[] {43}, null, null)", + 21>, + item(prod(sort("E"), [sort("E"), lit("+"), sort("B")], {}), 0) : <"new NonTerminalStackNode\(20, 0, \"E\", null, null)", + 20>, + item(prod(sort("E"), [sort("E"), lit("*"), sort("B")], {}), 0) : <"new NonTerminalStackNode\(24, 0, \"E\", null, null)", + 24>, + item(prod(sort("E"), [sort("E"), lit("*"), sort("B")], {}), 2) : <"new NonTerminalStackNode\(26, 2, \"B\", null, null)", + 26>, + item(prod(sort("E"), [sort("E"), lit("+"), sort("B")], {}), 2) : <"new NonTerminalStackNode\(22, 2, \"B\", null, null)", + 22> + ), + lit("1") : ( + item(prod(lit("1"), [\char-class([range(49, 49)])], {}), 0) : <"new CharStackNode\(29, 0, new int[][]{{49,49}}, null, null)", + 29> + ) + ); + +test bool tstComputeDontNests1() + = computeDontNests( + generateNewItems(makeUnique(GEMPTY)), GEMPTY, makeUnique(GEMPTY) + ) == {}; +test bool tstComputeDontNests2() + = computeDontNests(generateNewItems(makeUnique(G0)), G0, makeUnique(G0)) == {}; +test bool tstComputeDontNests3() + = computeDontNests( + generateNewItems(makeUnique(GEXP)), GEXP, makeUnique(GEXP) + ) == {}; +test bool tstComputeDontNests4() + = computeDontNests( + generateNewItems(makeUnique(GEXPPRIO)), GEXPPRIO, makeUnique(GEXPPRIO) + ) == {}; + +test bool tstExpandParameterizedSymbols5() + = expandParameterizedSymbols(G0) == grammar( + {sort("S")}, + ( + sort("S") : choice(sort("S"), {prod(sort("S"), [lit("0")], {})}), + lit("0") : choice(lit("0"), {prod(lit("0"), [\char-class([range(48, 48)])], {})}) + ) + ); + +loc ParserBaseLoc + = |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/generated_parsers/| +; + +void generateParsers() { + writeFile( + ParserBaseLoc + "GEMPTYParser.java.gz", + newGenerate( + "org.rascalmpl.library.lang.rascal.grammar.tests.generated_parsers", + "GEMPTYParser", + GEMPTY + ) + ); + writeFile( + ParserBaseLoc + "G0Parser.java.gz", + newGenerate( + "org.rascalmpl.library.lang.rascal.grammar.tests.generated_parsers", + "G0Parser", + G0 + ) + ); + writeFile( + ParserBaseLoc + "GEXPParser.java.gz", + newGenerate( + "org.rascalmpl.library.lang.rascal.grammar.tests.generated_parsers", + "GEXPParser", + GEXP + ) + ); + writeFile( + ParserBaseLoc + "GEXPPRIOParser.java.gz", + newGenerate( + "org.rascalmpl.library.lang.rascal.grammar.tests.generated_parsers", + "GEXPPRIOParser", + GEXPPRIO + ) + ); +} + +private +list[str] removeEmptyLines(str s) + = [line | line <- split("\n", s), /^[ \t]*$/ !:= line]; + +bool sameLines(str s1, str s2) + = size(removeEmptyLines(s1) - removeEmptyLines(s2)) == 0; + +test bool tstNewGenerateGEMPTY() + = sameLines( + newGenerate( + "org.rascalmpl.library.lang.rascal.grammar.tests.generated_parsers", + "GEMPTYParser", + GEMPTY + ), + readFile( + |std:///lang/rascal/grammar/tests/generated_parsers/GEMPTYParser.java.gz| + ) + ); + +test bool tstNewGenerateG0() + = sameLines( + newGenerate( + "org.rascalmpl.library.lang.rascal.grammar.tests.generated_parsers", + "G0Parser", + G0 + ), + readFile( + |std:///lang/rascal/grammar/tests/generated_parsers/G0Parser.java.gz| + ) + ); + +test bool tstNewGenerateGEXP() + = sameLines( + newGenerate( + "org.rascalmpl.library.lang.rascal.grammar.tests.generated_parsers", + "GEXPParser", + GEXP + ), + readFile( + |std:///lang/rascal/grammar/tests/generated_parsers/GEXPParser.java.gz| + ) + ); + +test bool tstNewGenerateGEXPPRIO() + = sameLines( + newGenerate( + "org.rascalmpl.library.lang.rascal.grammar.tests.generated_parsers", + "GEXPPRIOParser", + GEXPPRIO + ), + readFile( + |std:///lang/rascal/grammar/tests/generated_parsers/GEXPPRIOParser.java.gz| + ) + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/PicoGrammar.rsc| +module lang::rascal::grammar::tests::PicoGrammar + +import IO; +import Grammar; +import ParseTree; +import List; +import lang::rascal::grammar::ParserGenerator; +import lang::rascal::grammar::Lookahead; +import util::Benchmark; +import util::Reflective; +import lang::rascal::grammar::tests::ParserGeneratorTests; + +public Grammar Pico + = grammar( + {sort("Program")}, + ( + empty() : choice(empty(), {prod(empty(), [], {})}), + lex("Id") : choice( + lex("Id"), + {prod( + lex("Id"), + [ + \char-class([range(97, 122)]), + conditional( + \iter-star(\char-class([range(48, 57), range(97, 122)])), + {\not-follow(\char-class([range(48, 57), range(97, 122)]))} + ) + ], + {} + )} + ), + \start(sort("Program")) : choice( + \start(sort("Program")), + {prod( + \start(sort("Program")), + [layouts("Layout"), label("top", sort("Program")), layouts("Layout")], + {} + )} + ), + layouts("Layout") : choice( + layouts("Layout"), + {prod( + layouts("Layout"), + [ + conditional( + \iter-star(lex("WhitespaceAndComment")), + {\not-follow( + \char-class([range(9, 10), range(13, 13), range(32, 32), range(37, 37)]) + )} + ) + ], + {} + )} + ), + lex("WhitespaceAndComment") : choice( + lex("WhitespaceAndComment"), + {prod( + label("ws2", lex("WhitespaceAndComment")), + [ + lit("%"), + iter(\char-class([range(1, 36), range(38, 16777215)])), + lit("%") + ], + {\tag("category"("Comment"))} + ), + prod( + label("ws3", lex("WhitespaceAndComment")), + [ + lit("%%"), + conditional( + \iter-star(\char-class([range(1, 9), range(11, 16777215)])), + {\end-of-line()} + ) + ], + {\tag("category"("Comment"))} + ), + prod( + lex("WhitespaceAndComment"), + [\char-class([range(9, 10), range(13, 13), range(32, 32)])], + {} + )} + ), + sort("Statement") : choice( + sort("Statement"), + {prod( + label("ifElseStat", sort("Statement")), + [ + lit("if"), + layouts("Layout"), + label("cond", sort("Expression")), + layouts("Layout"), + lit("then"), + layouts("Layout"), + label( + "thenPart", + \iter-star-seps ( + sort("Statement"), + [layouts("Layout"), lit(";"), layouts("Layout")] + ) + ), + layouts("Layout"), + lit("else"), + layouts("Layout"), + label( + "elsePart", + \iter-star-seps ( + sort("Statement"), + [layouts("Layout"), lit(";"), layouts("Layout")] + ) + ), + layouts("Layout"), + lit("fi") + ], + {} + ), + prod( + label("asgStat", sort("Statement")), + [ + label("var", lex("Id")), + layouts("Layout"), + lit(":="), + layouts("Layout"), + label("val", sort("Expression")) + ], + {} + ), + prod( + label("whileStat", sort("Statement")), + [ + lit("while"), + layouts("Layout"), + label("cond", sort("Expression")), + layouts("Layout"), + lit("do"), + layouts("Layout"), + label( + "body", + \iter-star-seps ( + sort("Statement"), + [layouts("Layout"), lit(";"), layouts("Layout")] + ) + ), + layouts("Layout"), + lit("od") + ], + {} + )} + ), + lex("Natural") : choice( + lex("Natural"), + {prod(lex("Natural"), [iter(\char-class([range(48, 57)]))], {})} + ), + sort("Program") : choice( + sort("Program"), + {prod( + label("program", sort("Program")), + [ + lit("begin"), + layouts("Layout"), + label("decls", sort("Declarations")), + layouts("Layout"), + label( + "body", + \iter-star-seps ( + sort("Statement"), + [layouts("Layout"), lit(";"), layouts("Layout")] + ) + ), + layouts("Layout"), + lit("end") + ], + {} + )} + ), + sort("Declarations") : choice( + sort("Declarations"), + {prod( + sort("Declarations"), + [ + lit("declare"), + layouts("Layout"), + label( + "decls", + \iter-star-seps ( + sort("Declaration"), + [layouts("Layout"), lit(","), layouts("Layout")] + ) + ), + layouts("Layout"), + lit(";") + ], + {} + )} + ), + lex("String") : choice( + lex("String"), + {prod( + lex("String"), + [ + lit("\""), + \iter-star(\char-class([range(1, 33), range(35, 16777215)])), + lit("\"") + ], + {} + )} + ), + sort("Expression") : choice( + sort("Expression"), + {priority ( + sort("Expression"), + [ + choice( + sort("Expression"), + {prod( + label("strCon", sort("Expression")), [label("string", lex("String"))], {} + ), + prod(label("id", sort("Expression")), [label("name", lex("Id"))], {}), + prod( + label("natCon", sort("Expression")), + [label("natcon", lex("Natural"))], + {} + ), + prod( + sort("Expression"), + [ + lit("("), + layouts("Layout"), + label("e", sort("Expression")), + layouts("Layout"), + lit(")") + ], + {\bracket()} + )} + ), + prod( + label("conc", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("Layout"), + lit("||"), + layouts("Layout"), + label("rhs", sort("Expression")) + ], + {} + ), + associativity( + sort("Expression"), + left(), + {prod( + label("add", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("Layout"), + lit("+"), + layouts("Layout"), + label("rhs", sort("Expression")) + ], + {} + ), + prod( + label("sub", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("Layout"), + lit("-"), + layouts("Layout"), + label("rhs", sort("Expression")) + ], + {\assoc(\left())} + )} + ) + ] + )} + ), + layouts("$default$") : choice(layouts("$default$"), {prod(layouts("$default$"), [], {})}), + sort("Type") : choice( + sort("Type"), + {prod(label("natural", sort("Type")), [lit("natural")], {}), + prod(label("string", sort("Type")), [lit("string")], {})} + ), + sort("Declaration") : choice( + sort("Declaration"), + {prod( + label("decl", sort("Declaration")), + [ + label("id", lex("Id")), + layouts("Layout"), + lit(":"), + layouts("Layout"), + label("tp", sort("Type")) + ], + {} + )} + ) + ) + ) ; + +loc PicoParserLoc + = |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/| + + "generated_parsers/PicoParser.java.gz" ; + +str generatePicoParser() + = newGenerate( + "org.rascalmpl.library.lang.rascal.grammar.tests.generated_parsers", + "PicoParser", + Pico + ); + +void generateAndWritePicoParser() { + writeFile(PicoParserLoc, generatePicoParser()); +} + +void warmup() { + for (_ <- [0..10]) { + generatePicoParser(); + } +} +int generateAndTimePicoParser() { + warmup(); + t = cpuTime(); + generatePicoParser(); + used = (cpuTime() - t) / 1000000; + println("GenerateAndTimePicoParser: ms"); + return used; +} + +value main() = generateAndTimePicoParser(); + +test bool tstgeneratePicoParser() + = sameLines(generatePicoParser(), readFile(PicoParserLoc)); + +test bool cntChoice1() { + cnt = 0; + visit(Pico) { + case choice(_, _): + cnt += 1; + } + ; + return cnt == 15; +} +test bool cntChoice2() = size([x | /x: choice(_, _) := Pico]) == 15; + +test bool cntLex1() { + cnt = 0; + visit(Pico) { + case lex(_): + cnt += 1; + } + ; + return cnt == 20; +} +test bool cntLex2() = size([x | /x: lex(_) := Pico]) == 20; + +test bool cntEmpty1() { + cnt = 0; + visit(Pico) { + case empty(): + cnt += 1; + } + ; + return cnt == 3; +} +test bool cntEmpty2() = size([x | /x: empty() := Pico]) == 3; + +test bool cntSort1() { + cnt = 0; + visit(Pico) { + case sort(_): + cnt += 1; + } + ; + return cnt == 52; +} +test bool cntSort2() = size([x | /x: sort(_) := Pico]) == 52; + +test bool cntLit1() { + cnt = 0; + visit(Pico) { + case lit(_): + cnt += 1; + } + ; + return cnt == 30; +} +test bool cntLit2() = size([x | /x: lit(_) := Pico]) == 30; + +test bool cntLabel1() { + cnt = 0; + visit(Pico) { + case label(_, _): + cnt += 1; + } + ; + return cnt == 38; +} +test bool cntLabel2() = size([x | /x: label(_, _) := Pico]) == 38; + +test bool cntCharClass1() { + cnt = 0; + visit(Pico) { + case \char-class(_): + cnt += 1; + } + ; + return cnt == 9; +} +test bool cntCharClass2() = size([x | /x: \char-class(_) := Pico]) == 9; + +test bool cntProd1() { + cnt = 0; + visit(Pico) { + case \prod(_, _, _): + cnt += 1; + } + ; + return cnt == 25; +} +test bool cntProd2() = size([x | /x: \prod(_, _, _) := Pico]) == 25; + +test bool cntEmptyList1() { + cnt = 0; + visit(Pico) { + case []: + cnt += 1; + } + ; + return cnt == 2; +} +test bool cntEmptyList2() = size([x | /x: [] := Pico]) == 2; + +test bool cntList1() { + cnt = 0; + visit(Pico) { + case [*value _]: + cnt += 1; + } + ; + return cnt == 40; +} +test bool cntList2() = size([x | /x: [*value _] := Pico]) == 40; + +test bool cntEmptySet1() { + cnt = 0; + visit(Pico) { + case {}: + cnt += 1; + } + ; + return cnt == 21; +} +test bool cntEmptySet2() = size([x | /x: {} := Pico]) == 21; + +test bool cntSet1() { + cnt = 0; + visit(Pico) { + case {*value _}: + cnt += 1; + } + ; + return cnt == 45; +} +test bool cntSet2() = size([x | /x: {*value _} := Pico]) == 45; +@ignoreInterpreter{gives wrong answer 1186} +test bool cntStr1() { + cnt = 0; + visit(Pico) { + case str _: + cnt += 1; + } + ; + return cnt == 187; +} +test bool cntStr2() = size([x | /x: str _ := Pico]) == 187; + +test bool cntInt1() { + cnt = 0; + visit(Pico) { + case int _: + cnt += 1; + } + ; + return cnt == 38; +} +test bool cntInt2() = size([x | /x: int _ := Pico]) == 38; + +test bool cntIter1() { + cnt = 0; + visit(Pico) { + case \iter(_): + cnt += 1; + } + ; + return cnt == 2; +} +test bool cntIter2() = size([x | /x: \iter(_) := Pico]) == 2; + +test bool cntIterStar1() { + cnt = 0; + visit(Pico) { + case \iter-star(_): + cnt += 1; + } + ; + return cnt == 4; +} +test bool cntIterStar2() = size([x | /x: \iter-star(_) := Pico]) == 4; + +test bool cntIterSeps1() { + cnt = 0; + visit(Pico) { + case \iter-seps(_, _): + cnt += 1; + } + ; + return cnt == 0; +} +test bool cntIterSeps2() = size([x | /x: \iter-seps(_, _) := Pico]) == 0; + +test bool cntIterStarSeps1() { + cnt = 0; + visit(Pico) { + case \iter-star-seps(_, _): + cnt += 1; + } + ; + return cnt == 5; +} +test bool cntIterStarSeps2() + = size([x | /x: \iter-star-seps(_, _) := Pico]) == 5; + +test bool cntConditional1() { + cnt = 0; + visit(Pico) { + case \conditional(_, _): + cnt += 1; + } + ; + return cnt == 3; +} +test bool cntConditional2() = size([x | /x: \conditional(_, _) := Pico]) == 3; + +test bool cntRange1() { + cnt = 0; + visit(Pico) { + case \range(_, _): + cnt += 1; + } + ; + return cnt == 19; +} +test bool cntRange2() = size([x | /x: \range(_, _) := Pico]) == 19; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/RascalGrammar.rsc| +module lang::rascal::grammar::tests::RascalGrammar + +import Grammar; +import ParseTree; +import IO; +import List; +import lang::rascal::grammar::ParserGenerator; +import lang::rascal::grammar::Lookahead; +import lang::rascal::grammar::tests::ParserGeneratorTests; +import util::Benchmark; +import util::Reflective; + +public Grammar Rascal + = grammar( + {sort("Module")}, + ( + lex("RealLiteral") : choice( + lex("RealLiteral"), + {prod( + lex("RealLiteral"), + [ + conditional(lit("."), {\not-precede(\char-class([range(46, 46)]))}), + iter(\char-class([range(48, 57)])), + \char-class([range(69, 69), range(101, 101)]), + opt(\char-class([range(43, 43), range(45, 45)])), + iter(\char-class([range(48, 57)])), + opt( + \char-class([ + range(68, 68), range(70, 70), range(100, 100), range(102, 102) + ]) + ) + ], + {} + ), + prod( + lex("RealLiteral"), + [ + iter(\char-class([range(48, 57)])), + conditional(lit("."), {\not-follow(lit("."))}), + \iter-star(\char-class([range(48, 57)])), + opt( + \char-class([ + range(68, 68), range(70, 70), range(100, 100), range(102, 102) + ]) + ) + ], + {} + ), + prod( + lex("RealLiteral"), + [ + conditional(lit("."), {\not-precede(\char-class([range(46, 46)]))}), + iter(\char-class([range(48, 57)])), + opt( + \char-class([ + range(68, 68), range(70, 70), range(100, 100), range(102, 102) + ]) + ) + ], + {} + ), + prod( + lex("RealLiteral"), + [ + iter(\char-class([range(48, 57)])), + \char-class([range(69, 69), range(101, 101)]), + opt(\char-class([range(43, 43), range(45, 45)])), + iter(\char-class([range(48, 57)])), + opt( + \char-class([ + range(68, 68), range(70, 70), range(100, 100), range(102, 102) + ]) + ) + ], + {} + ), + prod( + lex("RealLiteral"), + [ + iter(\char-class([range(48, 57)])), + \char-class([ + range(68, 68), range(70, 70), range(100, 100), range(102, 102) + ]) + ], + {} + ), + prod( + lex("RealLiteral"), + [ + iter(\char-class([range(48, 57)])), + lit("."), + \iter-star(\char-class([range(48, 57)])), + \char-class([range(69, 69), range(101, 101)]), + opt(\char-class([range(43, 43), range(45, 45)])), + iter(\char-class([range(48, 57)])), + opt( + \char-class([ + range(68, 68), range(70, 70), range(100, 100), range(102, 102) + ]) + ) + ], + {} + )} + ), + sort("Tags") : choice( + sort("Tags"), + {prod( + label("default", sort("Tags")), + [label("tags", \iter-star-seps ( + sort("Tag"), + [layouts("LAYOUTLIST")] + ))], + {} + )} + ), + \start(sort("EvalCommand")) : choice( + \start(sort("EvalCommand")), + {prod( + \start(sort("EvalCommand")), + [ + layouts("LAYOUTLIST"), + label("top", sort("EvalCommand")), + layouts("LAYOUTLIST") + ], + {} + )} + ), + sort("ModuleActuals") : choice( + sort("ModuleActuals"), + {prod( + label("default", sort("ModuleActuals")), + [ + lit("["), + layouts("LAYOUTLIST"), + label( + "types", + \iter-seps ( + sort("Type"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + )} + ), + sort("Renamings") : choice( + sort("Renamings"), + {prod( + label("default", sort("Renamings")), + [ + lit("renaming"), + layouts("LAYOUTLIST"), + label( + "renamings", + \iter-seps ( + sort("Renaming"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ) + ], + {} + )} + ), + sort("KeywordFormal") : choice( + sort("KeywordFormal"), + {prod( + label("default", sort("KeywordFormal")), + [ + label("type", sort("Type")), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")) + ], + {} + )} + ), + lex("MidPathChars") : choice( + lex("MidPathChars"), + {prod(lex("MidPathChars"), [lit("\>"), lex("URLChars"), lit("\<")], {})} + ), + sort("OptionalExpression") : choice( + sort("OptionalExpression"), + {prod(label("noExpression", sort("OptionalExpression")), [], {}), + prod( + label("expression", sort("OptionalExpression")), + [label("expression", sort("Expression"))], + {} + )} + ), + sort("Expression") : choice( + sort("Expression"), + {priority ( + sort("Expression"), + [ + choice( + sort("Expression"), + {prod( + label("it", sort("Expression")), + [ + conditional( + lit("it"), + {\not-precede(\char-class([range(65, 90), range(95, 95), range(97, 122)])), + \not-follow(\char-class([range(65, 90), range(95, 95), range(97, 122)]))} + ) + ], + {} + ), + prod( + label("tuple", sort("Expression")), + [ + lit("\<"), + layouts("LAYOUTLIST"), + label( + "elements", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("\>") + ], + {} + ), + prod( + label("literal", sort("Expression")), + [label("literal", sort("Literal"))], + {} + ), + prod( + label("visit", sort("Expression")), + [ + label("label", sort("Label")), + layouts("LAYOUTLIST"), + label("visit", sort("Visit")) + ], + {} + ), + prod( + label("closure", sort("Expression")), + [ + label("type", sort("Type")), + layouts("LAYOUTLIST"), + label("parameters", sort("Parameters")), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label( + "statements", \iter-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("reducer", sort("Expression")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("init", sort("Expression")), + layouts("LAYOUTLIST"), + lit("|"), + layouts("LAYOUTLIST"), + label("result", sort("Expression")), + layouts("LAYOUTLIST"), + lit("|"), + layouts("LAYOUTLIST"), + label( + "generators", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("callOrTree", sort("Expression")), + [ + label( + "expression", + conditional( + sort("Expression"), + {except("isDefined"), + except("transitiveClosure"), + except("transitiveReflexiveClosure")} + ) + ), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "arguments", + \iter-star-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label( + "keywordArguments", + \parameterized-sort ( + "KeywordArguments", + [sort("Expression")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("nonEmptyBlock", sort("Expression")), + [ + lit("{"), + layouts("LAYOUTLIST"), + label( + "statements", \iter-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("set", sort("Expression")), + [ + lit("{"), + layouts("LAYOUTLIST"), + label( + "elements0", + \iter-star-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("reifiedType", sort("Expression")), + [ + lit("type"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("symbol", sort("Expression")), + layouts("LAYOUTLIST"), + lit(","), + layouts("LAYOUTLIST"), + label("definitions", sort("Expression")), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("all", sort("Expression")), + [ + lit("all"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "generators", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("has", sort("Expression")), + [ + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit("has"), + layouts("LAYOUTLIST"), + label("name", lex("Name")) + ], + {} + ), + prod( + label("sliceStep", sort("Expression")), + [ + conditional( + label("expression", sort("Expression")), + {except("isDefined"), + except("transitiveClosure"), + except("transitiveReflexiveClosure")} + ), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label("optFirst", sort("OptionalExpression")), + layouts("LAYOUTLIST"), + lit(","), + layouts("LAYOUTLIST"), + label("second", sort("Expression")), + layouts("LAYOUTLIST"), + lit(".."), + layouts("LAYOUTLIST"), + label("optLast", sort("OptionalExpression")), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("getAnnotation", sort("Expression")), + [ + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit("@"), + layouts("LAYOUTLIST"), + label("name", lex("Name")) + ], + {} + ), + prod( + label("is", sort("Expression")), + [ + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit("is"), + layouts("LAYOUTLIST"), + label("name", lex("Name")) + ], + {} + ), + prod( + label("voidClosure", sort("Expression")), + [ + label("parameters", sort("Parameters")), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label( + "statements0", + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("slice", sort("Expression")), + [ + conditional( + label("expression", sort("Expression")), + {except("isDefined"), + except("transitiveClosure"), + except("transitiveReflexiveClosure")} + ), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label("optFirst", sort("OptionalExpression")), + layouts("LAYOUTLIST"), + lit(".."), + layouts("LAYOUTLIST"), + label("optLast", sort("OptionalExpression")), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("list", sort("Expression")), + [ + lit("["), + layouts("LAYOUTLIST"), + label( + "elements0", + \iter-star-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("comprehension", sort("Expression")), + [label("comprehension", sort("Comprehension"))], + {} + ), + prod( + label("stepRange", sort("Expression")), + [ + lit("["), + layouts("LAYOUTLIST"), + label("first", sort("Expression")), + layouts("LAYOUTLIST"), + lit(","), + layouts("LAYOUTLIST"), + label("second", sort("Expression")), + layouts("LAYOUTLIST"), + lit(".."), + layouts("LAYOUTLIST"), + label("last", sort("Expression")), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("fieldProject", sort("Expression")), + [ + conditional( + label("expression", sort("Expression")), + {except("isDefined"), + except("transitiveClosure"), + except("transitiveReflexiveClosure")} + ), + layouts("LAYOUTLIST"), + lit("\<"), + layouts("LAYOUTLIST"), + label( + "fields", + \iter-seps ( + sort("Field"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("\>") + ], + {} + ), + prod( + label("fieldAccess", sort("Expression")), + [ + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit("."), + layouts("LAYOUTLIST"), + label("field", lex("Name")) + ], + {} + ), + prod( + label("setAnnotation", sort("Expression")), + [ + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + lit("@"), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("value", sort("Expression")), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("transitiveReflexiveClosure", sort("Expression")), + [ + label("argument", sort("Expression")), + layouts("LAYOUTLIST"), + conditional(lit("*"), {\not-follow(lit("="))}) + ], + {} + ), + prod( + label("qualifiedName", sort("Expression")), + [label("qualifiedName", sort("QualifiedName"))], + {} + ), + prod( + label("map", sort("Expression")), + [ + lit("("), + layouts("LAYOUTLIST"), + label( + "mappings", + \iter-star-seps ( + \parameterized-sort ( + "Mapping", + [sort("Expression")] + ), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("transitiveClosure", sort("Expression")), + [ + label("argument", sort("Expression")), + layouts("LAYOUTLIST"), + conditional(lit("+"), {\not-follow(lit("="))}) + ], + {} + ), + prod( + label("subscript", sort("Expression")), + [ + conditional( + label("expression", sort("Expression")), + {except("isDefined"), + except("transitiveClosure"), + except("transitiveReflexiveClosure")} + ), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label( + "subscripts", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("any", sort("Expression")), + [ + lit("any"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "generators", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("bracket", sort("Expression")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit(")") + ], + {\bracket()} + ), + prod( + label("fieldUpdate", sort("Expression")), + [ + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label("key", lex("Name")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("replacement", sort("Expression")), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("reifyType", sort("Expression")), + [ + lit("#"), + layouts("LAYOUTLIST"), + conditional( + label("type", sort("Type")), {except("selector"), \not-follow(lit("["))} + ) + ], + {} + ), + prod( + label("range", sort("Expression")), + [ + lit("["), + layouts("LAYOUTLIST"), + label("first", sort("Expression")), + layouts("LAYOUTLIST"), + lit(".."), + layouts("LAYOUTLIST"), + label("last", sort("Expression")), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + )} + ), + prod( + label("isDefined", sort("Expression")), + [label("argument", sort("Expression")), layouts("LAYOUTLIST"), lit("?")], + {} + ), + choice( + sort("Expression"), + {prod( + label("asType", sort("Expression")), + [ + lit("["), + layouts("LAYOUTLIST"), + label("type", sort("Type")), + layouts("LAYOUTLIST"), + lit("]"), + layouts("LAYOUTLIST"), + label( + "argument", + conditional(sort("Expression"), {except("match"), except("noMatch")}) + ) + ], + {} + ), + prod( + label("splice", sort("Expression")), + [lit("*"), layouts("LAYOUTLIST"), label("argument", sort("Expression"))], + {\assoc(\non-assoc())} + ), + prod( + label("negative", sort("Expression")), + [lit("-"), layouts("LAYOUTLIST"), label("argument", sort("Expression"))], + {} + ), + prod( + label("negation", sort("Expression")), + [ + lit("!"), + layouts("LAYOUTLIST"), + label( + "argument", + conditional(sort("Expression"), {except("match"), except("noMatch")}) + ) + ], + {} + )} + ), + prod( + label("composition", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("o"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {} + ), + associativity( + sort("Expression"), + \left(), + {prod( + label("division", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("/"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {} + ), + prod( + label("join", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("join"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {} + ), + prod( + label("product", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("*"), + layouts("LAYOUTLIST"), + conditional(empty(), {\not-follow(lit("*"))}), + layouts("LAYOUTLIST"), + label( + "rhs", + conditional(sort("Expression"), {except("match"), except("noMatch")}) + ) + ], + {} + ), + prod( + label("remainder", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("%"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {} + )} + ), + prod( + label("intersection", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + conditional(lit("&"), {\not-follow(lit("&"))}), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {} + ), + associativity( + sort("Expression"), + \left(), + {prod( + label("insertBefore", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("\>\>"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {} + ), + prod( + label("addition", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("+"), + layouts("LAYOUTLIST"), + label( + "rhs", + conditional(sort("Expression"), {except("match"), except("noMatch")}) + ) + ], + {} + ), + prod( + label("subtraction", sort("Expression")), + [ + label( + "lhs", + conditional( + sort("Expression"), + {except("transitiveClosure"), except("transitiveReflexiveClosure")} + ) + ), + layouts("LAYOUTLIST"), + lit("-"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {} + ), + prod( + label("appendAfter", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + conditional(lit("\<\<"), {\not-follow(lit("="))}), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {} + )} + ), + prod( + label("modulo", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("mod"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {} + ), + associativity( + sort("Expression"), + \non-assoc(), + {prod( + label("in", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("in"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {\assoc(\non-assoc())} + ), + prod( + label("notIn", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("notin"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {\assoc(\non-assoc())} + )} + ), + associativity( + sort("Expression"), + \non-assoc(), + {prod( + label("lessThan", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + conditional(lit("\<"), {\not-follow(lit("-"))}), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {\assoc(\non-assoc())} + ), + prod( + label("greaterThanOrEq", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("\>="), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {\assoc(\non-assoc())} + ), + prod( + label("greaterThan", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("\>"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {\assoc(\non-assoc())} + ), + prod( + label("lessThanOrEq", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("\<="), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {\assoc(\non-assoc())} + )} + ), + associativity( + sort("Expression"), + \non-assoc(), + {prod( + label("nonEquals", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("!="), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {\assoc(\non-assoc())} + ), + prod( + label("equals", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("=="), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {\assoc(\non-assoc())} + )} + ), + prod( + label("ifDefinedOtherwise", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("?"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {\assoc(\non-assoc())} + ), + associativity( + sort("Expression"), + \non-assoc(), + {prod( + label("noMatch", sort("Expression")), + [ + label("pattern", sort("Pattern")), + layouts("LAYOUTLIST"), + lit("!:="), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")) + ], + {\assoc(\non-assoc())} + ), + prod( + label("enumerator", sort("Expression")), + [ + label("pattern", sort("Pattern")), + layouts("LAYOUTLIST"), + lit("\<-"), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")) + ], + {\assoc(\non-assoc())} + ), + prod( + label("match", sort("Expression")), + [ + label("pattern", sort("Pattern")), + layouts("LAYOUTLIST"), + lit(":="), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")) + ], + {\assoc(\non-assoc())} + )} + ), + associativity( + sort("Expression"), + \non-assoc(), + {prod( + label("implication", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("==\>"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {\assoc(\non-assoc())} + ), + prod( + label("equivalence", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("\<==\>"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {\assoc(\non-assoc())} + )} + ), + prod( + label("and", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("&&"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {} + ), + prod( + label("or", sort("Expression")), + [ + label("lhs", sort("Expression")), + layouts("LAYOUTLIST"), + lit("||"), + layouts("LAYOUTLIST"), + label("rhs", sort("Expression")) + ], + {} + ), + prod( + label("ifThenElse", sort("Expression")), + [ + label("condition", sort("Expression")), + layouts("LAYOUTLIST"), + lit("?"), + layouts("LAYOUTLIST"), + label("thenExp", sort("Expression")), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + label("elseExp", sort("Expression")) + ], + {} + ) + ] + ), + prod( + label("concrete", sort("Expression")), + [label("concrete", lex("Concrete"))], + {} + )} + ), + sort("Class") : choice( + sort("Class"), + {priority ( + sort("Class"), + [ + choice( + sort("Class"), + {prod( + label("simpleCharclass", sort("Class")), + [ + lit("["), + layouts("LAYOUTLIST"), + label( + "ranges", \iter-star-seps ( + sort("Range"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("complement", sort("Class")), + [lit("!"), layouts("LAYOUTLIST"), label("charClass", sort("Class"))], + {} + )} + ), + prod( + label("difference", sort("Class")), + [ + label("lhs", sort("Class")), + layouts("LAYOUTLIST"), + lit("-"), + layouts("LAYOUTLIST"), + label("rhs", sort("Class")) + ], + {} + ), + prod( + label("intersection", sort("Class")), + [ + label("lhs", sort("Class")), + layouts("LAYOUTLIST"), + lit("&&"), + layouts("LAYOUTLIST"), + label("rhs", sort("Class")) + ], + {} + ), + choice( + sort("Class"), + {prod( + label("union", sort("Class")), + [ + label("lhs", sort("Class")), + layouts("LAYOUTLIST"), + lit("||"), + layouts("LAYOUTLIST"), + label("rhs", sort("Class")) + ], + {} + ), + prod( + label("bracket", sort("Class")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("charclass", sort("Class")), + layouts("LAYOUTLIST"), + lit(")") + ], + {\bracket()} + )} + ) + ] + )} + ), + layouts("$default$") : choice(layouts("$default$"), {prod(layouts("$default$"), [], {})}), + sort("PathPart") : choice( + sort("PathPart"), + {prod( + label("interpolated", sort("PathPart")), + [ + label("pre", lex("PrePathChars")), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + label("tail", sort("PathTail")) + ], + {} + ), + prod( + label("nonInterpolated", sort("PathPart")), + [label("pathChars", lex("PathChars"))], + {} + )} + ), + sort("Signature") : choice( + sort("Signature"), + {prod( + label("withThrows", sort("Signature")), + [ + label("modifiers", sort("FunctionModifiers")), + layouts("LAYOUTLIST"), + label("type", sort("Type")), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + label("parameters", sort("Parameters")), + layouts("LAYOUTLIST"), + lit("throws"), + layouts("LAYOUTLIST"), + label( + "exceptions", + \iter-seps ( + sort("Type"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ) + ], + {} + ), + prod( + label("noThrows", sort("Signature")), + [ + label("modifiers", sort("FunctionModifiers")), + layouts("LAYOUTLIST"), + label("type", sort("Type")), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + label("parameters", sort("Parameters")) + ], + {} + )} + ), + sort("ShellCommand") : choice( + sort("ShellCommand"), + {prod( + label("unimport", sort("ShellCommand")), + [ + lit("unimport"), + layouts("LAYOUTLIST"), + label("name", sort("QualifiedName")) + ], + {} + ), + prod( + label("listDeclarations", sort("ShellCommand")), + [lit("declarations")], + {} + ), + prod(label("help", sort("ShellCommand")), [lit("help")], {}), + prod( + label("undeclare", sort("ShellCommand")), + [ + lit("undeclare"), + layouts("LAYOUTLIST"), + label("name", sort("QualifiedName")) + ], + {} + ), + prod(label("quit", sort("ShellCommand")), [lit("quit")], {}), + prod(label("clear", sort("ShellCommand")), [lit("clear")], {}), + prod( + label("setOption", sort("ShellCommand")), + [ + lit("set"), + layouts("LAYOUTLIST"), + label("name", sort("QualifiedName")), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")) + ], + {} + ), + prod(label("listModules", sort("ShellCommand")), [lit("modules")], {}), + prod(label("test", sort("ShellCommand")), [lit("test")], {}), + prod(label("history", sort("ShellCommand")), [lit("history")], {}), + prod( + label("edit", sort("ShellCommand")), + [ + lit("edit"), + layouts("LAYOUTLIST"), + label("name", sort("QualifiedName")) + ], + {} + )} + ), + sort("Catch") : choice( + sort("Catch"), + {prod( + label("binding", sort("Catch")), + [ + lit("catch"), + layouts("LAYOUTLIST"), + label("pattern", sort("Pattern")), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + label("body", sort("Statement")) + ], + {} + ), + prod( + label("default", sort("Catch")), + [ + lit("catch"), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + label("body", sort("Statement")) + ], + {} + )} + ), + sort("DataTarget") : choice( + sort("DataTarget"), + {prod( + label("labeled", sort("DataTarget")), + [label("label", lex("Name")), layouts("LAYOUTLIST"), lit(":")], + {} + ), + prod(label("empty", sort("DataTarget")), [], {})} + ), + empty() : choice(empty(), {prod(empty(), [], {})}), + sort("KeywordFormals") : choice( + sort("KeywordFormals"), + {prod( + label("default", sort("KeywordFormals")), + [ + label("optionalComma", lex("OptionalComma")), + layouts("LAYOUTLIST"), + conditional( + label( + "keywordFormalList", + \iter-seps ( + sort("KeywordFormal"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + {precede( + \char-class([range(9, 10), range(32, 32), range(40, 40), range(44, 44)]) + )} + ) + ], + {} + ), + prod(label("none", sort("KeywordFormals")), [], {})} + ), + sort("Renaming") : choice( + sort("Renaming"), + {prod( + label("default", sort("Renaming")), + [ + label("from", lex("Name")), + layouts("LAYOUTLIST"), + lit("=\>"), + layouts("LAYOUTLIST"), + label("to", lex("Name")) + ], + {} + )} + ), + sort("Tag") : choice( + sort("Tag"), + {prod( + label("empty", sort("Tag")), + [lit("@"), layouts("LAYOUTLIST"), label("name", lex("Name"))], + {\tag("Folded"()), \tag("category"("Comment"))} + ), + prod( + label("default", sort("Tag")), + [ + lit("@"), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + label("contents", lex("TagString")) + ], + {\tag("Folded"()), \tag("category"("Comment"))} + ), + prod( + label("expression", sort("Tag")), + [ + lit("@"), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")) + ], + {\tag("Folded"()), \tag("category"("Comment"))} + )} + ), + sort("Type") : choice( + sort("Type"), + {prod( + label("structured", sort("Type")), + [label("structured", sort("StructuredType"))], + {} + ), + prod(label("user", sort("Type")), [label("user", sort("UserType"))], {}), + prod( + label("variable", sort("Type")), [label("typeVar", sort("TypeVar"))], {} + ), + prod( + label("basic", sort("Type")), [label("basic", sort("BasicType"))], {} + ), + prod( + label("bracket", sort("Type")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("type", sort("Type")), + layouts("LAYOUTLIST"), + lit(")") + ], + {\bracket()} + ), + prod( + label("function", sort("Type")), + [label("function", sort("FunctionType"))], + {} + ), + prod( + label("symbol", sort("Type")), + [ + label( + "symbol", + conditional( + sort("Sym"), + {except("labeled"), + except("parametrized"), + except("nonterminal"), + except("parameter")} + ) + ) + ], + {} + ), + prod( + label("selector", sort("Type")), + [label("selector", sort("DataTypeSelector"))], + {} + )} + ), + sort("LocationLiteral") : choice( + sort("LocationLiteral"), + {prod( + label("default", sort("LocationLiteral")), + [ + label("protocolPart", sort("ProtocolPart")), + layouts("LAYOUTLIST"), + label("pathPart", sort("PathPart")) + ], + {} + )} + ), + sort("Declaration") : choice( + sort("Declaration"), + {prod( + label("data", sort("Declaration")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + label("visibility", sort("Visibility")), + layouts("LAYOUTLIST"), + lit("data"), + layouts("LAYOUTLIST"), + label("user", sort("UserType")), + layouts("LAYOUTLIST"), + label("commonKeywordParameters", sort("CommonKeywordParameters")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label( + "variants", + \iter-seps ( + sort("Variant"), + [layouts("LAYOUTLIST"), lit("|"), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("Foldable"())} + ), + prod( + label("annotation", sort("Declaration")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + label("visibility", sort("Visibility")), + layouts("LAYOUTLIST"), + lit("anno"), + layouts("LAYOUTLIST"), + label("annoType", sort("Type")), + layouts("LAYOUTLIST"), + label("onType", sort("Type")), + layouts("LAYOUTLIST"), + lit("@"), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + label("alias", sort("Declaration")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + label("visibility", sort("Visibility")), + layouts("LAYOUTLIST"), + lit("alias"), + layouts("LAYOUTLIST"), + label("user", sort("UserType")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("base", sort("Type")), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + label("tag", sort("Declaration")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + label("visibility", sort("Visibility")), + layouts("LAYOUTLIST"), + lit("tag"), + layouts("LAYOUTLIST"), + label("kind", sort("Kind")), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("on"), + layouts("LAYOUTLIST"), + label( + "types", + \iter-seps ( + sort("Type"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + label("function", sort("Declaration")), + [label("functionDeclaration", sort("FunctionDeclaration"))], + {} + ), + prod( + label("variable", sort("Declaration")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + label("visibility", sort("Visibility")), + layouts("LAYOUTLIST"), + label("type", sort("Type")), + layouts("LAYOUTLIST"), + label( + "variables", + \iter-seps ( + sort("Variable"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + label("dataAbstract", sort("Declaration")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + label("visibility", sort("Visibility")), + layouts("LAYOUTLIST"), + lit("data"), + layouts("LAYOUTLIST"), + label("user", sort("UserType")), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + )} + ), + sort("FunctionBody") : choice( + sort("FunctionBody"), + {prod( + label("default", sort("FunctionBody")), + [ + lit("{"), + layouts("LAYOUTLIST"), + label( + "statements", + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + )} + ), + \start(sort("Module")) : choice( + \start(sort("Module")), + {prod( + \start(sort("Module")), + [ + layouts("LAYOUTLIST"), + label("top", sort("Module")), + layouts("LAYOUTLIST") + ], + {} + )} + ), + sort("Field") : choice( + sort("Field"), + {prod(label("name", sort("Field")), [label("fieldName", lex("Name"))], {}), + prod( + label("index", sort("Field")), + [label("fieldIndex", sort("IntegerLiteral"))], + {} + )} + ), + sort("Range") : choice( + sort("Range"), + {prod( + label("character", sort("Range")), [label("character", lex("Char"))], {} + ), + prod( + label("fromTo", sort("Range")), + [ + label("start", lex("Char")), + layouts("LAYOUTLIST"), + lit("-"), + layouts("LAYOUTLIST"), + label("end", lex("Char")) + ], + {} + )} + ), + lex("OptionalComma") : choice( + lex("OptionalComma"), + {prod(label("default", lex("OptionalComma")), [opt(lit(","))], {})} + ), + \start(sort("Command")) : choice( + \start(sort("Command")), + {prod( + \start(sort("Command")), + [ + layouts("LAYOUTLIST"), + label("top", sort("Command")), + layouts("LAYOUTLIST") + ], + {} + )} + ), + lex("PrePathChars") : choice( + lex("PrePathChars"), + {prod(lex("PrePathChars"), [lex("URLChars"), lit("\<")], {})} + ), + lex("StringConstant") : choice( + lex("StringConstant"), + {prod( + lex("StringConstant"), + [ + lit("\""), + label("chars", \iter-star(lex("StringCharacter"))), + lit("\"") + ], + {\tag("category"("Constant"))} + )} + ), + sort("Variable") : choice( + sort("Variable"), + {prod( + label("unInitialized", sort("Variable")), + [label("name", lex("Name"))], + {} + ), + prod( + label("initialized", sort("Variable")), + [ + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("initial", sort("Expression")) + ], + {} + )} + ), + sort("TypeArg") : choice( + sort("TypeArg"), + {prod( + label("default", sort("TypeArg")), [label("type", sort("Type"))], {} + ), + prod( + label("named", sort("TypeArg")), + [ + label("type", sort("Type")), + layouts("LAYOUTLIST"), + label("name", lex("Name")) + ], + {} + )} + ), + lex("DateAndTime") : choice( + lex("DateAndTime"), + {prod( + lex("DateAndTime"), + [ + lit("$"), + lex("DatePart"), + lit("T"), + lex("TimePartNoTZ"), + lex("TimeZonePart"), + lit("$") + ], + {} + ), + prod( + lex("DateAndTime"), + [ + lit("$"), + lex("DatePart"), + lit("T"), + conditional( + lex("TimePartNoTZ"), + {\not-follow(\char-class([range(43, 43), range(45, 45)]))} + ), + lit("$") + ], + {} + )} + ), + sort("StringLiteral") : choice( + sort("StringLiteral"), + {prod( + label("nonInterpolated", sort("StringLiteral")), + [label("constant", lex("StringConstant"))], + {} + ), + prod( + label("interpolated", sort("StringLiteral")), + [ + label("pre", lex("PreStringChars")), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + label("tail", sort("StringTail")) + ], + {} + ), + prod( + label("template", sort("StringLiteral")), + [ + label("pre", lex("PreStringChars")), + layouts("LAYOUTLIST"), + label("template", sort("StringTemplate")), + layouts("LAYOUTLIST"), + label("tail", sort("StringTail")) + ], + {} + )} + ), + lex("Backslash") : choice( + lex("Backslash"), + {prod( + lex("Backslash"), + [ + conditional( + \char-class([range(92, 92)]), + {\not-follow( + \char-class([range(47, 47), range(60, 60), range(62, 62), range(92, 92)]) + )} + ) + ], + {} + )} + ), + sort("Bound") : choice( + sort("Bound"), + {prod( + label("default", sort("Bound")), + [ + lit(";"), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")) + ], + {} + ), + prod(label("empty", sort("Bound")), [], {})} + ), + lex("Char") : choice( + lex("Char"), + {prod( + lex("Char"), + [ + \char-class([ + range(1, 31), + range(33, 33), + range(35, 38), + range(40, 44), + range(46, 59), + range(61, 61), + range(63, 90), + range(94, 16777215) + ]) + ], + {\tag("category"("Constant"))} + ), + prod( + lex("Char"), + [ + lit("\\"), + \char-class([ + range(32, 32), + range(34, 34), + range(39, 39), + range(45, 45), + range(60, 60), + range(62, 62), + range(91, 93), + range(98, 98), + range(102, 102), + range(110, 110), + range(114, 114), + range(116, 116) + ]) + ], + {\tag("category"("Constant"))} + ), + prod( + lex("Char"), [lex("UnicodeEscape")], {\tag("category"("Constant"))} + )} + ), + \start(sort("Commands")) : choice( + \start(sort("Commands")), + {prod( + \start(sort("Commands")), + [ + layouts("LAYOUTLIST"), + label("top", sort("Commands")), + layouts("LAYOUTLIST") + ], + {} + )} + ), + lex("CaseInsensitiveStringConstant") : choice( + lex("CaseInsensitiveStringConstant"), + {prod( + lex("CaseInsensitiveStringConstant"), + [ + lit("\'"), + label("chars", \iter-star(lex("StringCharacter"))), + lit("\'") + ], + {\tag("category"("Constant"))} + )} + ), + lex("JustTime") : choice( + lex("JustTime"), + {prod( + lex("JustTime"), + [lit("$T"), lex("TimePartNoTZ"), lex("TimeZonePart"), lit("$")], + {} + ), + prod( + lex("JustTime"), + [ + lit("$T"), + conditional( + lex("TimePartNoTZ"), + {\not-follow(\char-class([range(43, 43), range(45, 45)]))} + ), + lit("$") + ], + {} + )} + ), + sort("Declarator") : choice( + sort("Declarator"), + {prod( + label("default", sort("Declarator")), + [ + label("type", sort("Type")), + layouts("LAYOUTLIST"), + label( + "variables", + \iter-seps ( + sort("Variable"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ) + ], + {} + )} + ), + sort("Target") : choice( + sort("Target"), + {prod(label("empty", sort("Target")), [], {}), + prod(label("labeled", sort("Target")), [label("name", lex("Name"))], {})} + ), + sort("IntegerLiteral") : choice( + sort("IntegerLiteral"), + {prod( + label("octalIntegerLiteral", sort("IntegerLiteral")), + [label("octal", lex("OctalIntegerLiteral"))], + {} + ), + prod( + label("decimalIntegerLiteral", sort("IntegerLiteral")), + [label("decimal", lex("DecimalIntegerLiteral"))], + {} + ), + prod( + label("hexIntegerLiteral", sort("IntegerLiteral")), + [label("hex", lex("HexIntegerLiteral"))], + {} + )} + ), + sort("Pattern") : choice( + sort("Pattern"), + {priority ( + sort("Pattern"), + [ + choice( + sort("Pattern"), + {prod( + label("list", sort("Pattern")), + [ + lit("["), + layouts("LAYOUTLIST"), + label( + "elements0", + \iter-star-seps ( + sort("Pattern"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("callOrTree", sort("Pattern")), + [ + label("expression", sort("Pattern")), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "arguments", + \iter-star-seps ( + sort("Pattern"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label( + "keywordArguments", + \parameterized-sort ( + "KeywordArguments", + [sort("Pattern")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("tuple", sort("Pattern")), + [ + lit("\<"), + layouts("LAYOUTLIST"), + label( + "elements", + \iter-seps ( + sort("Pattern"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("\>") + ], + {} + ), + prod( + label("literal", sort("Pattern")), + [label("literal", sort("Literal"))], + {} + ), + prod( + label("set", sort("Pattern")), + [ + lit("{"), + layouts("LAYOUTLIST"), + label( + "elements0", + \iter-star-seps ( + sort("Pattern"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("splice", sort("Pattern")), + [lit("*"), layouts("LAYOUTLIST"), label("argument", sort("Pattern"))], + {} + ), + prod( + label("reifiedType", sort("Pattern")), + [ + lit("type"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("symbol", sort("Pattern")), + layouts("LAYOUTLIST"), + lit(","), + layouts("LAYOUTLIST"), + label("definitions", sort("Pattern")), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("typedVariable", sort("Pattern")), + [ + label("type", sort("Type")), + layouts("LAYOUTLIST"), + label("name", lex("Name")) + ], + {} + ), + prod( + label("multiVariable", sort("Pattern")), + [ + label("qualifiedName", sort("QualifiedName")), + layouts("LAYOUTLIST"), + lit("*") + ], + {} + ), + prod( + label("negative", sort("Pattern")), + [lit("-"), layouts("LAYOUTLIST"), label("argument", sort("Pattern"))], + {} + ), + prod( + label("map", sort("Pattern")), + [ + lit("("), + layouts("LAYOUTLIST"), + label( + "mappings", + \iter-star-seps ( + \parameterized-sort ( + "Mapping", + [sort("Pattern")] + ), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("qualifiedName", sort("Pattern")), + [label("qualifiedName", sort("QualifiedName"))], + {} + ), + prod( + label("splicePlus", sort("Pattern")), + [lit("+"), layouts("LAYOUTLIST"), label("argument", sort("Pattern"))], + {} + )} + ), + choice( + sort("Pattern"), + {prod( + label("descendant", sort("Pattern")), + [lit("/"), layouts("LAYOUTLIST"), label("pattern", sort("Pattern"))], + {} + ), + prod( + label("asType", sort("Pattern")), + [ + lit("["), + layouts("LAYOUTLIST"), + label("type", sort("Type")), + layouts("LAYOUTLIST"), + lit("]"), + layouts("LAYOUTLIST"), + label("argument", sort("Pattern")) + ], + {} + ), + prod( + label("variableBecomes", sort("Pattern")), + [ + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + label("pattern", sort("Pattern")) + ], + {} + ), + prod( + label("typedVariableBecomes", sort("Pattern")), + [ + label("type", sort("Type")), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + label("pattern", sort("Pattern")) + ], + {} + ), + prod( + label("anti", sort("Pattern")), + [lit("!"), layouts("LAYOUTLIST"), label("pattern", sort("Pattern"))], + {} + )} + ) + ] + ), + prod( + label("concrete", sort("Pattern")), + [label("concrete", lex("Concrete"))], + {} + )} + ), + sort("Case") : choice( + sort("Case"), + {prod( + label("patternWithAction", sort("Case")), + [ + lit("case"), + layouts("LAYOUTLIST"), + label("patternWithAction", sort("PatternWithAction")) + ], + {\tag("Foldable"())} + ), + prod( + label("default", sort("Case")), + [ + lit("default"), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + label("statement", sort("Statement")) + ], + {\tag("Foldable"())} + )} + ), + lex("PostPathChars") : choice( + lex("PostPathChars"), + {prod(lex("PostPathChars"), [lit("\>"), lex("URLChars"), lit("|")], {})} + ), + lex("StringCharacter") : choice( + lex("StringCharacter"), + {prod( + lex("StringCharacter"), + [ + \char-class([range(10, 10)]), + \iter-star( + \char-class([ + range(9, 9), + range(32, 32), + range(160, 160), + range(5760, 5760), + range(8192, 8202), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \char-class([range(39, 39)]) + ], + {} + ), + prod( + lex("StringCharacter"), + [ + lit("\\"), + \char-class([ + range(34, 34), + range(39, 39), + range(60, 60), + range(62, 62), + range(92, 92), + range(98, 98), + range(102, 102), + range(110, 110), + range(114, 114), + range(116, 116) + ]) + ], + {} + ), + prod(lex("StringCharacter"), [lex("UnicodeEscape")], {}), + prod( + lex("StringCharacter"), + [ + \char-class([ + range(1, 33), + range(35, 38), + range(40, 59), + range(61, 61), + range(63, 91), + range(93, 16777215) + ]) + ], + {} + )} + ), + lex("ConcretePart") : choice( + lex("ConcretePart"), + {prod( + label("gt", lex("ConcretePart")), + [lit("\\\>")], + {\tag("category"("MetaSkipped"))} + ), + prod( + label("hole", lex("ConcretePart")), + [label("hole", sort("ConcreteHole"))], + {\tag("category"("MetaVariable"))} + ), + prod( + label("bs", lex("ConcretePart")), + [lit("\\\\")], + {\tag("category"("MetaSkipped"))} + ), + prod( + label("lt", lex("ConcretePart")), + [lit("\\\<")], + {\tag("category"("MetaSkipped"))} + ), + prod( + label("bq", lex("ConcretePart")), + [lit("\\`")], + {\tag("category"("MetaSkipped"))} + ), + prod( + label("text", lex("ConcretePart")), + [ + conditional( + iter( + \char-class([ + range(1, 9), + range(11, 59), + range(61, 61), + range(63, 91), + range(93, 95), + range(97, 16777215) + ]) + ), + {\not-follow( + \char-class([ + range(1, 9), + range(11, 59), + range(61, 61), + range(63, 91), + range(93, 95), + range(97, 16777215) + ]) + )} + ) + ], + {\tag("category"("MetaSkipped"))} + ), + prod( + label("newline", lex("ConcretePart")), + [ + lit("\n"), + \iter-star( + \char-class([ + range(9, 9), + range(32, 32), + range(160, 160), + range(5760, 5760), + range(8192, 8202), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + lit("\'") + ], + {} + )} + ), + sort("ImportedModule") : choice( + sort("ImportedModule"), + {prod( + label("actuals", sort("ImportedModule")), + [ + label("name", sort("QualifiedName")), + layouts("LAYOUTLIST"), + label("actuals", sort("ModuleActuals")) + ], + {} + ), + prod( + label("default", sort("ImportedModule")), + [label("name", sort("QualifiedName"))], + {} + ), + prod( + label("renamings", sort("ImportedModule")), + [ + label("name", sort("QualifiedName")), + layouts("LAYOUTLIST"), + label("renamings", sort("Renamings")) + ], + {} + ), + prod( + label("actualsRenaming", sort("ImportedModule")), + [ + label("name", sort("QualifiedName")), + layouts("LAYOUTLIST"), + label("actuals", sort("ModuleActuals")), + layouts("LAYOUTLIST"), + label("renamings", sort("Renamings")) + ], + {} + )} + ), + lex("PathChars") : choice( + lex("PathChars"), + {prod( + lex("PathChars"), [lex("URLChars"), \char-class([range(124, 124)])], {} + )} + ), + sort("Strategy") : choice( + sort("Strategy"), + {prod(label("innermost", sort("Strategy")), [lit("innermost")], {}), + prod(label("topDown", sort("Strategy")), [lit("top-down")], {}), + prod( + label("bottomUpBreak", sort("Strategy")), [lit("bottom-up-break")], {} + ), + prod( + label("topDownBreak", sort("Strategy")), [lit("top-down-break")], {} + ), + prod(label("outermost", sort("Strategy")), [lit("outermost")], {}), + prod(label("bottomUp", sort("Strategy")), [lit("bottom-up")], {})} + ), + sort("LocalVariableDeclaration") : choice( + sort("LocalVariableDeclaration"), + {prod( + label("default", sort("LocalVariableDeclaration")), + [label("declarator", sort("Declarator"))], + {} + ), + prod( + label("dynamic", sort("LocalVariableDeclaration")), + [ + lit("dynamic"), + layouts("LAYOUTLIST"), + label("declarator", sort("Declarator")) + ], + {} + )} + ), + sort("FunctionType") : choice( + sort("FunctionType"), + {prod( + label("typeArguments", sort("FunctionType")), + [ + label("type", sort("Type")), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "arguments", + \iter-star-seps ( + sort("TypeArg"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + )} + ), + sort("Visibility") : choice( + sort("Visibility"), + {prod(label("private", sort("Visibility")), [lit("private")], {}), + prod(label("public", sort("Visibility")), [lit("public")], {}), + prod(label("default", sort("Visibility")), [], {})} + ), + sort("Replacement") : choice( + sort("Replacement"), + {prod( + label("conditional", sort("Replacement")), + [ + label("replacementExpression", sort("Expression")), + layouts("LAYOUTLIST"), + lit("when"), + layouts("LAYOUTLIST"), + label( + "conditions", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ) + ], + {} + ), + prod( + label("unconditional", sort("Replacement")), + [label("replacementExpression", sort("Expression"))], + {} + )} + ), + lex("JustDate") : choice( + lex("JustDate"), + {prod(lex("JustDate"), [lit("$"), lex("DatePart"), lit("$")], {})} + ), + lex("NamedBackslash") : choice( + lex("NamedBackslash"), + {prod( + lex("NamedBackslash"), + [ + conditional( + \char-class([range(92, 92)]), + {\not-follow(\char-class([range(60, 60), range(62, 62), range(92, 92)]))} + ) + ], + {} + )} + ), + sort("Assoc") : choice( + sort("Assoc"), + {prod(label("left", sort("Assoc")), [lit("left")], {}), + prod(label("associative", sort("Assoc")), [lit("assoc")], {}), + prod(label("right", sort("Assoc")), [lit("right")], {}), + prod(label("nonAssociative", sort("Assoc")), [lit("non-assoc")], {})} + ), + sort("PathTail") : choice( + sort("PathTail"), + {prod( + label("mid", sort("PathTail")), + [ + label("mid", lex("MidPathChars")), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + label("tail", sort("PathTail")) + ], + {} + ), + prod( + label("post", sort("PathTail")), + [label("post", lex("PostPathChars"))], + {} + )} + ), + lex("MidProtocolChars") : choice( + lex("MidProtocolChars"), + {prod( + lex("MidProtocolChars"), [lit("\>"), lex("URLChars"), lit("\<")], {} + )} + ), + sort("Kind") : choice( + sort("Kind"), + {prod(label("all", sort("Kind")), [lit("all")], {}), + prod(label("variable", sort("Kind")), [lit("variable")], {}), + prod(label("alias", sort("Kind")), [lit("alias")], {}), + prod(label("module", sort("Kind")), [lit("module")], {}), + prod(label("anno", sort("Kind")), [lit("anno")], {}), + prod(label("data", sort("Kind")), [lit("data")], {}), + prod(label("view", sort("Kind")), [lit("view")], {}), + prod(label("tag", sort("Kind")), [lit("tag")], {}), + prod(label("function", sort("Kind")), [lit("function")], {})} + ), + keywords("RascalKeywords") : choice( + keywords("RascalKeywords"), + {prod(keywords("RascalKeywords"), [lit("break")], {}), + prod(keywords("RascalKeywords"), [lit("for")], {}), + prod(keywords("RascalKeywords"), [lit("str")], {}), + prod(keywords("RascalKeywords"), [lit("node")], {}), + prod(keywords("RascalKeywords"), [lit("tuple")], {}), + prod(keywords("RascalKeywords"), [lit("solve")], {}), + prod(keywords("RascalKeywords"), [lit("rat")], {}), + prod(keywords("RascalKeywords"), [lit("dynamic")], {}), + prod(keywords("RascalKeywords"), [lit("assoc")], {}), + prod(keywords("RascalKeywords"), [lit("bag")], {}), + prod(keywords("RascalKeywords"), [lit("set")], {}), + prod(keywords("RascalKeywords"), [lit("o")], {}), + prod(keywords("RascalKeywords"), [lit("start")], {}), + prod(keywords("RascalKeywords"), [sort("BasicType")], {}), + prod(keywords("RascalKeywords"), [lit("lrel")], {}), + prod(keywords("RascalKeywords"), [lit("continue")], {}), + prod(keywords("RascalKeywords"), [lit("bracket")], {}), + prod(keywords("RascalKeywords"), [lit("rel")], {}), + prod(keywords("RascalKeywords"), [lit("list")], {}), + prod(keywords("RascalKeywords"), [lit("test")], {}), + prod(keywords("RascalKeywords"), [lit("return")], {}), + prod(keywords("RascalKeywords"), [lit("false")], {}), + prod(keywords("RascalKeywords"), [lit("join")], {}), + prod(keywords("RascalKeywords"), [lit("else")], {}), + prod(keywords("RascalKeywords"), [lit("it")], {}), + prod(keywords("RascalKeywords"), [lit("in")], {}), + prod(keywords("RascalKeywords"), [lit("if")], {}), + prod(keywords("RascalKeywords"), [lit("non-assoc")], {}), + prod(keywords("RascalKeywords"), [lit("lexical")], {}), + prod(keywords("RascalKeywords"), [lit("value")], {}), + prod(keywords("RascalKeywords"), [lit("map")], {}), + prod(keywords("RascalKeywords"), [lit("visit")], {}), + prod(keywords("RascalKeywords"), [lit("all")], {}), + prod(keywords("RascalKeywords"), [lit("try")], {}), + prod(keywords("RascalKeywords"), [lit("private")], {}), + prod(keywords("RascalKeywords"), [lit("true")], {}), + prod(keywords("RascalKeywords"), [lit("finally")], {}), + prod(keywords("RascalKeywords"), [lit("real")], {}), + prod(keywords("RascalKeywords"), [lit("void")], {}), + prod(keywords("RascalKeywords"), [lit("keyword")], {}), + prod(keywords("RascalKeywords"), [lit("any")], {}), + prod(keywords("RascalKeywords"), [lit("one")], {}), + prod(keywords("RascalKeywords"), [lit("module")], {}), + prod(keywords("RascalKeywords"), [lit("public")], {}), + prod(keywords("RascalKeywords"), [lit("throws")], {}), + prod(keywords("RascalKeywords"), [lit("alias")], {}), + prod(keywords("RascalKeywords"), [lit("default")], {}), + prod(keywords("RascalKeywords"), [lit("catch")], {}), + prod(keywords("RascalKeywords"), [lit("insert")], {}), + prod(keywords("RascalKeywords"), [lit("anno")], {}), + prod(keywords("RascalKeywords"), [lit("throw")], {}), + prod(keywords("RascalKeywords"), [lit("bool")], {}), + prod(keywords("RascalKeywords"), [lit("switch")], {}), + prod(keywords("RascalKeywords"), [lit("type")], {}), + prod(keywords("RascalKeywords"), [lit("while")], {}), + prod(keywords("RascalKeywords"), [lit("notin")], {}), + prod(keywords("RascalKeywords"), [lit("case")], {}), + prod(keywords("RascalKeywords"), [lit("layout")], {}), + prod(keywords("RascalKeywords"), [lit("mod")], {}), + prod(keywords("RascalKeywords"), [lit("extend")], {}), + prod(keywords("RascalKeywords"), [lit("append")], {}), + prod(keywords("RascalKeywords"), [lit("fail")], {}), + prod(keywords("RascalKeywords"), [lit("datetime")], {}), + prod(keywords("RascalKeywords"), [lit("filter")], {}), + prod(keywords("RascalKeywords"), [lit("loc")], {}), + prod(keywords("RascalKeywords"), [lit("assert")], {}), + prod(keywords("RascalKeywords"), [lit("data")], {}), + prod(keywords("RascalKeywords"), [lit("import")], {}), + prod(keywords("RascalKeywords"), [lit("num")], {}), + prod(keywords("RascalKeywords"), [lit("tag")], {}), + prod(keywords("RascalKeywords"), [lit("syntax")], {}), + prod(keywords("RascalKeywords"), [lit("int")], {})} + ), + sort("Label") : choice( + sort("Label"), + {prod( + label("default", sort("Label")), + [label("name", lex("Name")), layouts("LAYOUTLIST"), lit(":")], + {} + ), + prod(label("empty", sort("Label")), [], {})} + ), + lex("OctalIntegerLiteral") : choice( + lex("OctalIntegerLiteral"), + {prod( + lex("OctalIntegerLiteral"), + [ + \char-class([range(48, 48)]), + conditional( + iter(\char-class([range(48, 55)])), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ], + {} + )} + ), + sort("DateTimeLiteral") : choice( + sort("DateTimeLiteral"), + {prod( + label("dateLiteral", sort("DateTimeLiteral")), + [label("date", lex("JustDate"))], + {} + ), + prod( + label("timeLiteral", sort("DateTimeLiteral")), + [label("time", lex("JustTime"))], + {} + ), + prod( + label("dateAndTimeLiteral", sort("DateTimeLiteral")), + [label("dateAndTime", lex("DateAndTime"))], + {} + )} + ), + sort("Prod") : choice( + sort("Prod"), + {priority ( + sort("Prod"), + [ + choice( + sort("Prod"), + {prod( + label("associativityGroup", sort("Prod")), + [ + label("associativity", sort("Assoc")), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("group", sort("Prod")), + layouts("LAYOUTLIST"), + lit(")") + ], + {\tag("Foldable"())} + ), + prod( + label("reference", sort("Prod")), + [lit(":"), layouts("LAYOUTLIST"), label("referenced", lex("Name"))], + {} + ), + prod( + label("unlabeled", sort("Prod")), + [ + label( + "modifiers", + \iter-star-seps ( + sort("ProdModifier"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("syms", \iter-star-seps ( + sort("Sym"), + [layouts("LAYOUTLIST")] + )) + ], + {} + ), + prod(label("others", sort("Prod")), [lit("...")], {}), + prod( + label("labeled", sort("Prod")), + [ + label( + "modifiers", + \iter-star-seps ( + sort("ProdModifier"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + label("syms", \iter-star-seps ( + sort("Sym"), + [layouts("LAYOUTLIST")] + )) + ], + {} + )} + ), + prod( + label("all", sort("Prod")), + [ + label("lhs", sort("Prod")), + layouts("LAYOUTLIST"), + lit("|"), + layouts("LAYOUTLIST"), + label("rhs", sort("Prod")) + ], + {} + ), + prod( + label("first", sort("Prod")), + [ + label("lhs", sort("Prod")), + layouts("LAYOUTLIST"), + conditional(lit("\>"), {\not-follow(lit("\>"))}), + layouts("LAYOUTLIST"), + label("rhs", sort("Prod")) + ], + {} + ) + ] + )} + ), + layouts("LAYOUTLIST") : choice( + layouts("LAYOUTLIST"), + {prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + )} + ), + lex("Comment") : choice( + lex("Comment"), + {prod( + lex("Comment"), + [ + lit("//"), + conditional( + \iter-star(\char-class([range(1, 9), range(11, 16777215)])), + {\not-follow( + \char-class([ + range(9, 9), + range(13, 13), + range(32, 32), + range(160, 160), + range(5760, 5760), + range(8192, 8202), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \end-of-line()} + ) + ], + {\tag("category"("Comment"))} + ), + prod( + lex("Comment"), + [ + lit("/*"), + \iter-star( + alt( + {conditional( + \char-class([range(42, 42)]), + {\not-follow(\char-class([range(47, 47)]))} + ), + \char-class([range(1, 41), range(43, 16777215)])} + ) + ), + lit("*/") + ], + {\tag("category"("Comment"))} + )} + ), + lex("UnicodeEscape") : choice( + lex("UnicodeEscape"), + {prod( + label("utf32", lex("UnicodeEscape")), + [ + lit("\\"), + \char-class([range(85, 85)]), + alt( + {lit("10"), + seq([ + lit("0"), \char-class([range(48, 57), range(65, 70), range(97, 102)]) + ])} + ), + \char-class([range(48, 57), range(65, 70), range(97, 102)]), + \char-class([range(48, 57), range(65, 70), range(97, 102)]), + \char-class([range(48, 57), range(65, 70), range(97, 102)]), + \char-class([range(48, 57), range(65, 70), range(97, 102)]) + ], + {} + ), + prod( + label("utf16", lex("UnicodeEscape")), + [ + lit("\\"), + \char-class([range(117, 117)]), + \char-class([range(48, 57), range(65, 70), range(97, 102)]), + \char-class([range(48, 57), range(65, 70), range(97, 102)]), + \char-class([range(48, 57), range(65, 70), range(97, 102)]), + \char-class([range(48, 57), range(65, 70), range(97, 102)]) + ], + {} + ), + prod( + label("ascii", lex("UnicodeEscape")), + [ + lit("\\"), + \char-class([range(97, 97)]), + \char-class([range(48, 55)]), + \char-class([range(48, 57), range(65, 70), range(97, 102)]) + ], + {} + )} + ), + sort("SyntaxDefinition") : choice( + sort("SyntaxDefinition"), + {prod( + label("keyword", sort("SyntaxDefinition")), + [ + lit("keyword"), + layouts("LAYOUTLIST"), + label("defined", sort("Sym")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("production", sort("Prod")), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("Foldable"())} + ), + prod( + label("language", sort("SyntaxDefinition")), + [ + label("start", sort("Start")), + layouts("LAYOUTLIST"), + lit("syntax"), + layouts("LAYOUTLIST"), + label("defined", sort("Sym")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("production", sort("Prod")), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("Foldable"())} + ), + prod( + label("lexical", sort("SyntaxDefinition")), + [ + lit("lexical"), + layouts("LAYOUTLIST"), + label("defined", sort("Sym")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("production", sort("Prod")), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("Foldable"())} + ), + prod( + label("layout", sort("SyntaxDefinition")), + [ + label("vis", sort("Visibility")), + layouts("LAYOUTLIST"), + lit("layout"), + layouts("LAYOUTLIST"), + label("defined", sort("Sym")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("production", sort("Prod")), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("Foldable"())} + )} + ), + sort("ModuleParameters") : choice( + sort("ModuleParameters"), + {prod( + label("default", sort("ModuleParameters")), + [ + lit("["), + layouts("LAYOUTLIST"), + label( + "parameters", + \iter-seps ( + sort("TypeVar"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + )} + ), + lex("DatePart") : choice( + lex("DatePart"), + {prod( + lex("DatePart"), + [ + \char-class([range(48, 57)]), + \char-class([range(48, 57)]), + \char-class([range(48, 57)]), + \char-class([range(48, 57)]), + \char-class([range(48, 49)]), + \char-class([range(48, 57)]), + \char-class([range(48, 51)]), + \char-class([range(48, 57)]) + ], + {} + ), + prod( + lex("DatePart"), + [ + \char-class([range(48, 57)]), + \char-class([range(48, 57)]), + \char-class([range(48, 57)]), + \char-class([range(48, 57)]), + lit("-"), + \char-class([range(48, 49)]), + \char-class([range(48, 57)]), + lit("-"), + \char-class([range(48, 51)]), + \char-class([range(48, 57)]) + ], + {} + )} + ), + sort("Assignable") : choice( + sort("Assignable"), + {prod( + label("constructor", sort("Assignable")), + [ + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "arguments", + \iter-seps ( + sort("Assignable"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("subscript", sort("Assignable")), + [ + label("receiver", sort("Assignable")), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label("subscript", sort("Expression")), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("tuple", sort("Assignable")), + [ + lit("\<"), + layouts("LAYOUTLIST"), + label( + "elements", + \iter-seps ( + sort("Assignable"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("\>") + ], + {} + ), + prod( + label("variable", sort("Assignable")), + [label("qualifiedName", sort("QualifiedName"))], + {} + ), + prod( + label("bracket", sort("Assignable")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("arg", sort("Assignable")), + layouts("LAYOUTLIST"), + lit(")") + ], + {\bracket()} + ), + prod( + label("sliceStep", sort("Assignable")), + [ + label("receiver", sort("Assignable")), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label("optFirst", sort("OptionalExpression")), + layouts("LAYOUTLIST"), + lit(","), + layouts("LAYOUTLIST"), + label("second", sort("Expression")), + layouts("LAYOUTLIST"), + lit(".."), + layouts("LAYOUTLIST"), + label("optLast", sort("OptionalExpression")), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("fieldAccess", sort("Assignable")), + [ + label("receiver", sort("Assignable")), + layouts("LAYOUTLIST"), + lit("."), + layouts("LAYOUTLIST"), + label("field", lex("Name")) + ], + {} + ), + prod( + label("slice", sort("Assignable")), + [ + label("receiver", sort("Assignable")), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label("optFirst", sort("OptionalExpression")), + layouts("LAYOUTLIST"), + lit(".."), + layouts("LAYOUTLIST"), + label("optLast", sort("OptionalExpression")), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("annotation", sort("Assignable")), + [ + label("receiver", sort("Assignable")), + layouts("LAYOUTLIST"), + lit("@"), + layouts("LAYOUTLIST"), + label("annotation", lex("Name")) + ], + {} + ), + prod( + label("ifDefinedOrDefault", sort("Assignable")), + [ + label("receiver", sort("Assignable")), + layouts("LAYOUTLIST"), + lit("?"), + layouts("LAYOUTLIST"), + label("defaultExpression", sort("Expression")) + ], + {} + )} + ), + lex("RationalLiteral") : choice( + lex("RationalLiteral"), + {prod( + lex("RationalLiteral"), + [ + \char-class([range(49, 57)]), + \iter-star(\char-class([range(48, 57)])), + \char-class([range(114, 114)]), + \char-class([range(48, 57)]), + conditional( + \iter-star(\char-class([range(48, 57)])), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ], + {} + ), + prod( + lex("RationalLiteral"), + [ + \char-class([range(48, 57)]), + \iter-star(\char-class([range(48, 57)])), + \char-class([range(114, 114)]) + ], + {} + )} + ), + sort("ProtocolTail") : choice( + sort("ProtocolTail"), + {prod( + label("mid", sort("ProtocolTail")), + [ + label("mid", lex("MidProtocolChars")), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + label("tail", sort("ProtocolTail")) + ], + {} + ), + prod( + label("post", sort("ProtocolTail")), + [label("post", lex("PostProtocolChars"))], + {} + )} + ), + \parameterized-sort ( + "KeywordArguments", + [parameter("T", adt ( + "Tree", + [] + ))] + ) : choice( + \parameterized-sort ( + "KeywordArguments", + [parameter("T", adt ( + "Tree", + [] + ))] + ), + {prod( + label( + "none", + \parameterized-sort ( + "KeywordArguments", + [parameter("T", adt ( + "Tree", + [] + ))] + ) + ), + [], + {} + ), + prod( + label( + "default", + \parameterized-sort ( + "KeywordArguments", + [parameter("T", adt ( + "Tree", + [] + ))] + ) + ), + [ + label("optionalComma", lex("OptionalComma")), + layouts("LAYOUTLIST"), + conditional( + label( + "keywordArgumentList", + \iter-seps ( + \parameterized-sort ( + "KeywordArgument", + [parameter("T", adt ( + "Tree", + [] + ))] + ), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + {precede( + \char-class([range(9, 10), range(32, 32), range(40, 40), range(44, 44)]) + )} + ) + ], + {} + )} + ), + sort("Commands") : choice( + sort("Commands"), + {prod( + label("commandlist", sort("Commands")), + [ + label( + "commands", \iter-seps ( + sort("EvalCommand"), + [layouts("LAYOUTLIST")] + ) + ) + ], + {} + )} + ), + sort("BasicType") : choice( + sort("BasicType"), + {prod(label("int", sort("BasicType")), [lit("int")], {}), + prod(label("listRelation", sort("BasicType")), [lit("lrel")], {}), + prod(label("bool", sort("BasicType")), [lit("bool")], {}), + prod(label("type", sort("BasicType")), [lit("type")], {}), + prod(label("set", sort("BasicType")), [lit("set")], {}), + prod(label("bag", sort("BasicType")), [lit("bag")], {}), + prod(label("rational", sort("BasicType")), [lit("rat")], {}), + prod(label("real", sort("BasicType")), [lit("real")], {}), + prod(label("node", sort("BasicType")), [lit("node")], {}), + prod(label("tuple", sort("BasicType")), [lit("tuple")], {}), + prod(label("map", sort("BasicType")), [lit("map")], {}), + prod(label("loc", sort("BasicType")), [lit("loc")], {}), + prod(label("num", sort("BasicType")), [lit("num")], {}), + prod(label("list", sort("BasicType")), [lit("list")], {}), + prod(label("value", sort("BasicType")), [lit("value")], {}), + prod(label("void", sort("BasicType")), [lit("void")], {}), + prod(label("relation", sort("BasicType")), [lit("rel")], {}), + prod(label("dateTime", sort("BasicType")), [lit("datetime")], {}), + prod(label("string", sort("BasicType")), [lit("str")], {})} + ), + lex("RegExpModifier") : choice( + lex("RegExpModifier"), + {prod( + lex("RegExpModifier"), + [ + \iter-star( + \char-class([ + range(100, 100), range(105, 105), range(109, 109), range(115, 115) + ]) + ) + ], + {} + )} + ), + sort("CommonKeywordParameters") : choice( + sort("CommonKeywordParameters"), + {prod( + label("present", sort("CommonKeywordParameters")), + [ + lit("("), + layouts("LAYOUTLIST"), + label( + "keywordFormalList", + \iter-seps ( + sort("KeywordFormal"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod(label("absent", sort("CommonKeywordParameters")), [], {})} + ), + \parameterized-sort ( + "Mapping", + [parameter("T", adt ( + "Tree", + [] + ))] + ) : choice( + \parameterized-sort ( + "Mapping", + [parameter("T", adt ( + "Tree", + [] + ))] + ), + {prod( + label( + "default", + \parameterized-sort ( + "Mapping", + [parameter("T", adt ( + "Tree", + [] + ))] + ) + ), + [ + label( + "from", + conditional( + parameter("T", adt ( + "Tree", + [] + )), {except("ifDefinedOtherwise")} + ) + ), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + label("to", parameter("T", adt ( + "Tree", + [] + ))) + ], + {} + )} + ), + sort("Command") : choice( + sort("Command"), + {prod( + label("import", sort("Command")), [label("imported", sort("Import"))], {} + ), + prod( + label("expression", sort("Command")), + [ + label( + "expression", + conditional(sort("Expression"), {except("nonEmptyBlock")}) + ) + ], + {} + ), + prod( + label("statement", sort("Command")), + [ + label( + "statement", + conditional( + sort("Statement"), + {except("variableDeclaration"), + except("visit"), + except("functionDeclaration")} + ) + ) + ], + {} + ), + prod( + label("shell", sort("Command")), + [ + lit(":"), + layouts("LAYOUTLIST"), + label("command", sort("ShellCommand")) + ], + {} + ), + prod( + label("declaration", sort("Command")), + [label("declaration", sort("Declaration"))], + {} + )} + ), + sort("StringTemplate") : choice( + sort("StringTemplate"), + {prod( + label("ifThen", sort("StringTemplate")), + [ + lit("if"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "conditions", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label( + "preStats", \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("body", sort("StringMiddle")), + layouts("LAYOUTLIST"), + label( + "postStats", + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("while", sort("StringTemplate")), + [ + lit("while"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("condition", sort("Expression")), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label( + "preStats", \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("body", sort("StringMiddle")), + layouts("LAYOUTLIST"), + label( + "postStats", + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("ifThenElse", sort("StringTemplate")), + [ + lit("if"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "conditions", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label( + "preStatsThen", + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("thenString", sort("StringMiddle")), + layouts("LAYOUTLIST"), + label( + "postStatsThen", + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}"), + layouts("LAYOUTLIST"), + lit("else"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label( + "preStatsElse", + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("elseString", sort("StringMiddle")), + layouts("LAYOUTLIST"), + label( + "postStatsElse", + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("doWhile", sort("StringTemplate")), + [ + lit("do"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label( + "preStats", \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("body", sort("StringMiddle")), + layouts("LAYOUTLIST"), + label( + "postStats", + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}"), + layouts("LAYOUTLIST"), + lit("while"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("condition", sort("Expression")), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("for", sort("StringTemplate")), + [ + lit("for"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "generators", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label( + "preStats", \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("body", sort("StringMiddle")), + layouts("LAYOUTLIST"), + label( + "postStats", + \iter-star-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + )} + ), + sort("ProtocolPart") : choice( + sort("ProtocolPart"), + {prod( + label("interpolated", sort("ProtocolPart")), + [ + label("pre", lex("PreProtocolChars")), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + label("tail", sort("ProtocolTail")) + ], + {} + ), + prod( + label("nonInterpolated", sort("ProtocolPart")), + [label("protocolChars", lex("ProtocolChars"))], + {} + )} + ), + sort("Visit") : choice( + sort("Visit"), + {prod( + label("defaultStrategy", sort("Visit")), + [ + lit("visit"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("subject", sort("Expression")), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label("cases", \iter-seps ( + sort("Case"), + [layouts("LAYOUTLIST")] + )), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("givenStrategy", sort("Visit")), + [ + label("strategy", sort("Strategy")), + layouts("LAYOUTLIST"), + lit("visit"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("subject", sort("Expression")), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label("cases", \iter-seps ( + sort("Case"), + [layouts("LAYOUTLIST")] + )), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + )} + ), + sort("StructuredType") : choice( + sort("StructuredType"), + {prod( + label("default", sort("StructuredType")), + [ + label("basicType", sort("BasicType")), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label( + "arguments", + \iter-seps ( + sort("TypeArg"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + )} + ), + lex("NamedRegExp") : choice( + lex("NamedRegExp"), + {prod(lex("NamedRegExp"), [lex("NamedBackslash")], {}), + prod( + lex("NamedRegExp"), + [ + \char-class([ + range(1, 46), range(48, 59), range(61, 61), range(63, 91), range(93, 16777215) + ]) + ], + {} + ), + prod( + lex("NamedRegExp"), + [ + \char-class([range(92, 92)]), + \char-class([range(47, 47), range(60, 60), range(62, 62), range(92, 92)]) + ], + {} + ), + prod(lex("NamedRegExp"), [lit("\<"), lex("Name"), lit("\>")], {})} + ), + sort("Parameters") : choice( + sort("Parameters"), + {prod( + label("default", sort("Parameters")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("formals", sort("Formals")), + layouts("LAYOUTLIST"), + label("keywordFormals", sort("KeywordFormals")), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("varArgs", sort("Parameters")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("formals", sort("Formals")), + layouts("LAYOUTLIST"), + lit("..."), + layouts("LAYOUTLIST"), + label("keywordFormals", sort("KeywordFormals")), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + )} + ), + lex("DecimalIntegerLiteral") : choice( + lex("DecimalIntegerLiteral"), + {prod( + lex("DecimalIntegerLiteral"), + [ + \char-class([range(49, 57)]), + conditional( + \iter-star(\char-class([range(48, 57)])), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ], + {} + ), + prod( + lex("DecimalIntegerLiteral"), + [ + conditional( + lit("0"), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ], + {} + )} + ), + lex("PreProtocolChars") : choice( + lex("PreProtocolChars"), + {prod(lex("PreProtocolChars"), [lit("|"), lex("URLChars"), lit("\<")], {})} + ), + sort("Header") : choice( + sort("Header"), + {prod( + label("parameters", sort("Header")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + lit("module"), + layouts("LAYOUTLIST"), + label("name", sort("QualifiedName")), + layouts("LAYOUTLIST"), + label("params", sort("ModuleParameters")), + layouts("LAYOUTLIST"), + label( + "imports", \iter-star-seps ( + sort("Import"), + [layouts("LAYOUTLIST")] + ) + ) + ], + {} + ), + prod( + label("default", sort("Header")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + lit("module"), + layouts("LAYOUTLIST"), + label("name", sort("QualifiedName")), + layouts("LAYOUTLIST"), + label( + "imports", \iter-star-seps ( + sort("Import"), + [layouts("LAYOUTLIST")] + ) + ) + ], + {} + )} + ), + sort("TypeVar") : choice( + sort("TypeVar"), + {prod( + label("bounded", sort("TypeVar")), + [ + lit("&"), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("\<:"), + layouts("LAYOUTLIST"), + label("bound", sort("Type")) + ], + {} + ), + prod( + label("free", sort("TypeVar")), + [lit("&"), layouts("LAYOUTLIST"), label("name", lex("Name"))], + {} + )} + ), + lex("PostProtocolChars") : choice( + lex("PostProtocolChars"), + {prod( + lex("PostProtocolChars"), [lit("\>"), lex("URLChars"), lit("://")], {} + )} + ), + lex("Concrete") : choice( + lex("Concrete"), + {prod( + label("typed", lex("Concrete")), + [ + lit("("), + label("l1", layouts("LAYOUTLIST")), + label("symbol", sort("Sym")), + label("l2", layouts("LAYOUTLIST")), + lit(")"), + label("l3", layouts("LAYOUTLIST")), + lit("`"), + label("parts", \iter-star(lex("ConcretePart"))), + lit("`") + ], + {} + )} + ), + lex("ProtocolChars") : choice( + lex("ProtocolChars"), + {prod( + lex("ProtocolChars"), + [ + \char-class([range(124, 124)]), + lex("URLChars"), + conditional( + lit("://"), + {\not-follow( + \char-class([ + range(9, 10), + range(13, 13), + range(32, 32), + range(160, 160), + range(5760, 5760), + range(8192, 8202), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + )} + ) + ], + {} + )} + ), + sort("Assignment") : choice( + sort("Assignment"), + {prod(label("product", sort("Assignment")), [lit("*=")], {}), + prod(label("division", sort("Assignment")), [lit("/=")], {}), + prod(label("default", sort("Assignment")), [lit("=")], {}), + prod(label("intersection", sort("Assignment")), [lit("&=")], {}), + prod(label("addition", sort("Assignment")), [lit("+=")], {}), + prod(label("ifDefined", sort("Assignment")), [lit("?=")], {}), + prod(label("append", sort("Assignment")), [lit("\<\<=")], {}), + prod(label("subtraction", sort("Assignment")), [lit("-=")], {})} + ), + sort("PatternWithAction") : choice( + sort("PatternWithAction"), + {prod( + label("arbitrary", sort("PatternWithAction")), + [ + label("pattern", sort("Pattern")), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + label("statement", sort("Statement")) + ], + {} + ), + prod( + label("replacing", sort("PatternWithAction")), + [ + label("pattern", sort("Pattern")), + layouts("LAYOUTLIST"), + lit("=\>"), + layouts("LAYOUTLIST"), + label("replacement", sort("Replacement")) + ], + {} + )} + ), + sort("Module") : choice( + sort("Module"), + {prod( + label("default", sort("Module")), + [ + label("header", sort("Header")), + layouts("LAYOUTLIST"), + label("body", sort("Body")) + ], + {} + )} + ), + lex("MidStringChars") : choice( + lex("MidStringChars"), + {prod( + lex("MidStringChars"), + [ + \char-class([range(62, 62)]), + \iter-star(lex("StringCharacter")), + \char-class([range(60, 60)]) + ], + {\tag("category"("Constant"))} + )} + ), + sort("Toplevel") : choice( + sort("Toplevel"), + {prod( + label("givenVisibility", sort("Toplevel")), + [label("declaration", sort("Declaration"))], + {} + )} + ), + lex("TimePartNoTZ") : choice( + lex("TimePartNoTZ"), + {prod( + lex("TimePartNoTZ"), + [ + \char-class([range(48, 50)]), + \char-class([range(48, 57)]), + lit(":"), + \char-class([range(48, 53)]), + \char-class([range(48, 57)]), + lit(":"), + \char-class([range(48, 53)]), + \char-class([range(48, 57)]), + opt( + seq([ + \char-class([range(44, 44), range(46, 46)]), + \char-class([range(48, 57)]), + opt( + seq([\char-class([range(48, 57)]), opt(\char-class([range(48, 57)]))]) + ) + ]) + ) + ], + {} + ), + prod( + lex("TimePartNoTZ"), + [ + \char-class([range(48, 50)]), + \char-class([range(48, 57)]), + \char-class([range(48, 53)]), + \char-class([range(48, 57)]), + \char-class([range(48, 53)]), + \char-class([range(48, 57)]), + opt( + seq([ + \char-class([range(44, 44), range(46, 46)]), + \char-class([range(48, 57)]), + opt( + seq([\char-class([range(48, 57)]), opt(\char-class([range(48, 57)]))]) + ) + ]) + ) + ], + {} + )} + ), + lex("BooleanLiteral") : choice( + lex("BooleanLiteral"), + {prod(lex("BooleanLiteral"), [lit("false")], {}), + prod(lex("BooleanLiteral"), [lit("true")], {})} + ), + sort("Statement") : choice( + sort("Statement"), + {prod( + label("tryFinally", sort("Statement")), + [ + lit("try"), + layouts("LAYOUTLIST"), + label("body", sort("Statement")), + layouts("LAYOUTLIST"), + label("handlers", \iter-seps ( + sort("Catch"), + [layouts("LAYOUTLIST")] + )), + layouts("LAYOUTLIST"), + lit("finally"), + layouts("LAYOUTLIST"), + label( + "finallyBody", + conditional( + sort("Statement"), + {except("variableDeclaration"), except("functionDeclaration")} + ) + ) + ], + {\tag("breakable"())} + ), + prod( + label("expression", sort("Statement")), + [ + label( + "expression", + conditional( + sort("Expression"), {except("nonEmptyBlock"), except("visit")} + ) + ), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("breakable"())} + ), + prod( + label("continue", sort("Statement")), + [ + lit("continue"), + layouts("LAYOUTLIST"), + label("target", sort("Target")), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("breakable"())} + ), + prod( + label("functionDeclaration", sort("Statement")), + [label("functionDeclaration", sort("FunctionDeclaration"))], + {\tag("breakable"())} + ), + prod( + label("assignment", sort("Statement")), + [ + label("assignable", sort("Assignable")), + layouts("LAYOUTLIST"), + label("operator", sort("Assignment")), + layouts("LAYOUTLIST"), + label( + "statement", + conditional( + sort("Statement"), + {except("variableDeclaration"), except("functionDeclaration")} + ) + ) + ], + {\tag("breakable"())} + ), + prod( + label("try", sort("Statement")), + [ + lit("try"), + layouts("LAYOUTLIST"), + label("body", sort("Statement")), + layouts("LAYOUTLIST"), + label("handlers", \iter-seps ( + sort("Catch"), + [layouts("LAYOUTLIST")] + )) + ], + {\assoc(\non-assoc()), \tag("breakable"())} + ), + prod( + label("variableDeclaration", sort("Statement")), + [ + label("declaration", sort("LocalVariableDeclaration")), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("breakable"())} + ), + prod( + label("ifThen", sort("Statement")), + [ + label("label", sort("Label")), + layouts("LAYOUTLIST"), + lit("if"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "conditions", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + label( + "thenStatement", + conditional( + sort("Statement"), + {except("variableDeclaration"), except("functionDeclaration")} + ) + ), + layouts("LAYOUTLIST"), + conditional(empty(), {\not-follow(lit("else"))}) + ], + {\tag("breakable"())} + ), + prod( + label("solve", sort("Statement")), + [ + lit("solve"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "variables", + \iter-seps ( + sort("QualifiedName"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("bound", sort("Bound")), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + label( + "body", + conditional( + sort("Statement"), + {except("variableDeclaration"), except("functionDeclaration")} + ) + ) + ], + {\tag("breakable"())} + ), + prod( + label("filter", sort("Statement")), + [lit("filter"), layouts("LAYOUTLIST"), lit(";")], + {\tag("breakable"())} + ), + prod( + label("switch", sort("Statement")), + [ + label("label", sort("Label")), + layouts("LAYOUTLIST"), + lit("switch"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label("cases", \iter-seps ( + sort("Case"), + [layouts("LAYOUTLIST")] + )), + layouts("LAYOUTLIST"), + lit("}") + ], + {\tag("breakable"())} + ), + prod( + label("fail", sort("Statement")), + [ + lit("fail"), + layouts("LAYOUTLIST"), + label("target", sort("Target")), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("breakable"())} + ), + prod( + label("assert", sort("Statement")), + [ + lit("assert"), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("breakable"())} + ), + prod( + label("nonEmptyBlock", sort("Statement")), + [ + label("label", sort("Label")), + layouts("LAYOUTLIST"), + lit("{"), + layouts("LAYOUTLIST"), + label( + "statements", \iter-seps ( + sort("Statement"), + [layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {} + ), + prod( + label("while", sort("Statement")), + [ + label("label", sort("Label")), + layouts("LAYOUTLIST"), + lit("while"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "conditions", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + label( + "body", + conditional( + sort("Statement"), + {except("variableDeclaration"), except("functionDeclaration")} + ) + ) + ], + {\tag("breakable"())} + ), + prod( + label("doWhile", sort("Statement")), + [ + label("label", sort("Label")), + layouts("LAYOUTLIST"), + lit("do"), + layouts("LAYOUTLIST"), + label("body", sort("Statement")), + layouts("LAYOUTLIST"), + lit("while"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label("condition", sort("Expression")), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("breakable"())} + ), + associativity( + sort("Statement"), + \non-assoc(), + {prod( + label("throw", sort("Statement")), + [ + lit("throw"), + layouts("LAYOUTLIST"), + label( + "statement", + conditional( + sort("Statement"), + {except("variableDeclaration"), except("functionDeclaration")} + ) + ) + ], + {\assoc(\non-assoc()), \tag("breakable"())} + ), + prod( + label("append", sort("Statement")), + [ + lit("append"), + layouts("LAYOUTLIST"), + label("dataTarget", sort("DataTarget")), + layouts("LAYOUTLIST"), + label( + "statement", + conditional( + sort("Statement"), + {except("variableDeclaration"), except("functionDeclaration")} + ) + ) + ], + {\assoc(\non-assoc()), \tag("breakable"())} + ), + prod( + label("return", sort("Statement")), + [ + lit("return"), + layouts("LAYOUTLIST"), + label( + "statement", + conditional( + sort("Statement"), + {except("variableDeclaration"), except("functionDeclaration")} + ) + ) + ], + {\assoc(\non-assoc()), \tag("breakable"())} + ), + prod( + label("insert", sort("Statement")), + [ + lit("insert"), + layouts("LAYOUTLIST"), + label("dataTarget", sort("DataTarget")), + layouts("LAYOUTLIST"), + label( + "statement", + conditional( + sort("Statement"), + {except("variableDeclaration"), except("functionDeclaration")} + ) + ) + ], + {\assoc(\non-assoc()), \tag("breakable"())} + )} + ), + prod( + label("visit", sort("Statement")), + [ + label("label", sort("Label")), + layouts("LAYOUTLIST"), + label("visit", sort("Visit")) + ], + {\tag("breakable"())} + ), + prod( + label("break", sort("Statement")), + [ + lit("break"), + layouts("LAYOUTLIST"), + label("target", sort("Target")), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("breakable"())} + ), + prod( + label("for", sort("Statement")), + [ + label("label", sort("Label")), + layouts("LAYOUTLIST"), + lit("for"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "generators", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + label("body", sort("Statement")) + ], + {\tag("breakable"()), \tag("breakable"("{generators}"))} + ), + prod( + label("ifThenElse", sort("Statement")), + [ + label("label", sort("Label")), + layouts("LAYOUTLIST"), + lit("if"), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "conditions", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")"), + layouts("LAYOUTLIST"), + label("thenStatement", sort("Statement")), + layouts("LAYOUTLIST"), + lit("else"), + layouts("LAYOUTLIST"), + label( + "elseStatement", + conditional( + sort("Statement"), + {except("variableDeclaration"), except("functionDeclaration")} + ) + ) + ], + {\tag("breakable"())} + ), + prod(label("emptyStatement", sort("Statement")), [lit(";")], {}), + prod( + label("globalDirective", sort("Statement")), + [ + lit("global"), + layouts("LAYOUTLIST"), + label("type", sort("Type")), + layouts("LAYOUTLIST"), + label( + "names", + \iter-seps ( + sort("QualifiedName"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("breakable"())} + ), + prod( + label("assertWithMessage", sort("Statement")), + [ + lit("assert"), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + label("message", sort("Expression")), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("breakable"())} + )} + ), + sort("Literal") : choice( + sort("Literal"), + {prod( + label("dateTime", sort("Literal")), + [label("dateTimeLiteral", sort("DateTimeLiteral"))], + {} + ), + prod( + label("integer", sort("Literal")), + [label("integerLiteral", sort("IntegerLiteral"))], + {} + ), + prod( + label("boolean", sort("Literal")), + [label("booleanLiteral", lex("BooleanLiteral"))], + {} + ), + prod( + label("location", sort("Literal")), + [label("locationLiteral", sort("LocationLiteral"))], + {} + ), + prod( + label("real", sort("Literal")), + [label("realLiteral", lex("RealLiteral"))], + {} + ), + prod( + label("string", sort("Literal")), + [label("stringLiteral", sort("StringLiteral"))], + {} + ), + prod( + label("rational", sort("Literal")), + [label("rationalLiteral", lex("RationalLiteral"))], + {} + ), + prod( + label("regExp", sort("Literal")), + [label("regExpLiteral", lex("RegExpLiteral"))], + {} + )} + ), + sort("EvalCommand") : choice( + sort("EvalCommand"), + {prod( + label("import", sort("EvalCommand")), + [label("imported", sort("Import"))], + {} + ), + prod( + label("declaration", sort("EvalCommand")), + [label("declaration", sort("Declaration"))], + {} + ), + prod( + label("statement", sort("EvalCommand")), + [ + label( + "statement", + conditional( + sort("Statement"), + {except("variableDeclaration"), + except("visit"), + except("functionDeclaration")} + ) + ) + ], + {} + )} + ), + sort("FunctionModifier") : choice( + sort("FunctionModifier"), + {prod(label("test", sort("FunctionModifier")), [lit("test")], {}), + prod(label("default", sort("FunctionModifier")), [lit("default")], {}), + prod(label("java", sort("FunctionModifier")), [lit("java")], {})} + ), + sort("ProdModifier") : choice( + sort("ProdModifier"), + {prod(label("bracket", sort("ProdModifier")), [lit("bracket")], {}), + prod(label("tag", sort("ProdModifier")), [label("tag", sort("Tag"))], {}), + prod( + label("associativity", sort("ProdModifier")), + [label("associativity", sort("Assoc"))], + {} + )} + ), + lex("RegExpLiteral") : choice( + lex("RegExpLiteral"), + {prod( + lex("RegExpLiteral"), + [lit("/"), \iter-star(lex("RegExp")), lit("/"), lex("RegExpModifier")], + {} + )} + ), + lex("RegExp") : choice( + lex("RegExp"), + {prod(lex("RegExp"), [lex("Backslash")], {}), + prod( + lex("RegExp"), + [ + \char-class([range(92, 92)]), + \char-class([range(47, 47), range(60, 60), range(62, 62), range(92, 92)]) + ], + {} + ), + prod(lex("RegExp"), [lit("\<"), lex("Name"), lit("\>")], {}), + prod( + lex("RegExp"), + [ + \char-class([ + range(1, 46), range(48, 59), range(61, 61), range(63, 91), range(93, 16777215) + ]) + ], + {} + ), + prod( + lex("RegExp"), + [ + lit("\<"), + lex("Name"), + lit(":"), + \iter-star(lex("NamedRegExp")), + lit("\>") + ], + {} + )} + ), + lex("Nonterminal") : choice( + lex("Nonterminal"), + {prod( + lex("Nonterminal"), + [ + conditional( + seq([ + conditional( + \char-class([range(65, 90)]), + {\not-precede(\char-class([range(65, 90)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]), + {delete(keywords("RascalKeywords"))} + ) + ], + {} + )} + ), + lex("PreStringChars") : choice( + lex("PreStringChars"), + {prod( + lex("PreStringChars"), + [ + \char-class([range(34, 34)]), + \iter-star(lex("StringCharacter")), + \char-class([range(60, 60)]) + ], + {\tag("category"("Constant"))} + )} + ), + sort("StringTail") : choice( + sort("StringTail"), + {prod( + label("post", sort("StringTail")), + [label("post", lex("PostStringChars"))], + {} + ), + prod( + label("midInterpolated", sort("StringTail")), + [ + label("mid", lex("MidStringChars")), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + label("tail", sort("StringTail")) + ], + {} + ), + prod( + label("midTemplate", sort("StringTail")), + [ + label("mid", lex("MidStringChars")), + layouts("LAYOUTLIST"), + label("template", sort("StringTemplate")), + layouts("LAYOUTLIST"), + label("tail", sort("StringTail")) + ], + {} + )} + ), + sort("DataTypeSelector") : choice( + sort("DataTypeSelector"), + {prod( + label("selector", sort("DataTypeSelector")), + [ + label("sort", sort("QualifiedName")), + layouts("LAYOUTLIST"), + lit("."), + layouts("LAYOUTLIST"), + label("production", lex("Name")) + ], + {} + )} + ), + lex("TagString") : choice( + lex("TagString"), + {prod( + lex("TagString"), + [ + conditional(lit("{"), {\not-precede(lit("\\"))}), + label( + "contents", + \iter-star( + alt( + {\char-class([range(1, 122), range(124, 124), range(126, 16777215)]), + seq([lit("\\"), \char-class([range(123, 123), range(125, 125)])]), + lex("TagString")} + ) + ) + ), + conditional(lit("}"), {\not-precede(lit("\\"))}) + ], + {} + )} + ), + lex("NonterminalLabel") : choice( + lex("NonterminalLabel"), + {prod( + lex("NonterminalLabel"), + [ + \char-class([range(97, 122)]), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ], + {} + )} + ), + \parameterized-sort ( + "KeywordArgument", + [parameter("T", adt ( + "Tree", + [] + ))] + ) : choice( + \parameterized-sort ( + "KeywordArgument", + [parameter("T", adt ( + "Tree", + [] + ))] + ), + {prod( + label( + "default", + \parameterized-sort ( + "KeywordArgument", + [parameter("T", adt ( + "Tree", + [] + ))] + ) + ), + [ + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("expression", parameter("T", adt ( + "Tree", + [] + ))) + ], + {} + )} + ), + lex("Name") : choice( + lex("Name"), + {prod( + lex("Name"), + [ + conditional( + seq([ + conditional( + \char-class([range(65, 90), range(95, 95), range(97, 122)]), + {\not-precede(\char-class([range(65, 90), range(95, 95), range(97, 122)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]), + {delete(keywords("RascalKeywords"))} + ) + ], + {} + ), + prod( + lex("Name"), + [ + \char-class([range(92, 92)]), + \char-class([range(65, 90), range(95, 95), range(97, 122)]), + conditional( + \iter-star( + \char-class([ + range(45, 45), range(48, 57), range(65, 90), range(95, 95), range(97, 122) + ]) + ), + {\not-follow( + \char-class([ + range(45, 45), range(48, 57), range(65, 90), range(95, 95), range(97, 122) + ]) + )} + ) + ], + {} + )} + ), + sort("FunctionDeclaration") : choice( + sort("FunctionDeclaration"), + {prod( + label("expression", sort("FunctionDeclaration")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + label("visibility", sort("Visibility")), + layouts("LAYOUTLIST"), + label("signature", sort("Signature")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("Foldable"()), \tag("breakable"("{expression}"))} + ), + prod( + label("abstract", sort("FunctionDeclaration")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + label("visibility", sort("Visibility")), + layouts("LAYOUTLIST"), + label("signature", sort("Signature")), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + label("default", sort("FunctionDeclaration")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + label("visibility", sort("Visibility")), + layouts("LAYOUTLIST"), + label("signature", sort("Signature")), + layouts("LAYOUTLIST"), + label("body", sort("FunctionBody")) + ], + {\tag("Foldable"())} + ), + prod( + label("conditional", sort("FunctionDeclaration")), + [ + label("tags", sort("Tags")), + layouts("LAYOUTLIST"), + label("visibility", sort("Visibility")), + layouts("LAYOUTLIST"), + label("signature", sort("Signature")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + lit("when"), + layouts("LAYOUTLIST"), + label( + "conditions", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(";") + ], + {\tag("breakable"("{expression,conditions}")), \tag("Foldable"())} + )} + ), + sort("Start") : choice( + sort("Start"), + {prod(label("present", sort("Start")), [lit("start")], {}), + prod(label("absent", sort("Start")), [], {})} + ), + lex("LAYOUT") : choice( + lex("LAYOUT"), + {prod( + lex("LAYOUT"), + [ + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ], + {} + ), + prod(lex("LAYOUT"), [lex("Comment")], {})} + ), + sort("Body") : choice( + sort("Body"), + {prod( + label("toplevels", sort("Body")), + [ + label( + "toplevels", \iter-star-seps ( + sort("Toplevel"), + [layouts("LAYOUTLIST")] + ) + ) + ], + {} + )} + ), + sort("Import") : choice( + sort("Import"), + {prod( + label("syntax", sort("Import")), + [label("syntax", sort("SyntaxDefinition"))], + {} + ), + prod( + label("default", sort("Import")), + [ + lit("import"), + layouts("LAYOUTLIST"), + label("module", sort("ImportedModule")), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + label("extend", sort("Import")), + [ + lit("extend"), + layouts("LAYOUTLIST"), + label("module", sort("ImportedModule")), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + ), + prod( + label("external", sort("Import")), + [ + lit("import"), + layouts("LAYOUTLIST"), + label("name", sort("QualifiedName")), + layouts("LAYOUTLIST"), + lit("="), + layouts("LAYOUTLIST"), + label("at", sort("LocationLiteral")), + layouts("LAYOUTLIST"), + lit(";") + ], + {} + )} + ), + sort("ConcreteHole") : choice( + sort("ConcreteHole"), + {prod( + label("one", sort("ConcreteHole")), + [ + lit("\<"), + layouts("LAYOUTLIST"), + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("\>") + ], + {} + )} + ), + sort("UserType") : choice( + sort("UserType"), + {prod( + label("parametric", sort("UserType")), + [ + conditional(label("name", sort("QualifiedName")), {follow(lit("["))}), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label( + "parameters", + \iter-seps ( + sort("Type"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("name", sort("UserType")), + [label("name", sort("QualifiedName"))], + {} + )} + ), + sort("Variant") : choice( + sort("Variant"), + {prod( + label("nAryConstructor", sort("Variant")), + [ + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("("), + layouts("LAYOUTLIST"), + label( + "arguments", + \iter-star-seps ( + sort("TypeArg"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + label("keywordArguments", sort("KeywordFormals")), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + )} + ), + sort("FunctionModifiers") : choice( + sort("FunctionModifiers"), + {prod( + label("modifierlist", sort("FunctionModifiers")), + [ + label( + "modifiers", + \iter-star-seps ( + sort("FunctionModifier"), + [layouts("LAYOUTLIST")] + ) + ) + ], + {} + )} + ), + sort("Formals") : choice( + sort("Formals"), + {prod( + label("default", sort("Formals")), + [ + label( + "formals", + \iter-star-seps ( + sort("Pattern"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ) + ], + {} + )} + ), + sort("Comprehension") : choice( + sort("Comprehension"), + {prod( + label("set", sort("Comprehension")), + [ + lit("{"), + layouts("LAYOUTLIST"), + label( + "results", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("|"), + layouts("LAYOUTLIST"), + label( + "generators", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("}") + ], + {\tag("breakable"("{results,generators}"))} + ), + prod( + label("list", sort("Comprehension")), + [ + lit("["), + layouts("LAYOUTLIST"), + label( + "results", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("|"), + layouts("LAYOUTLIST"), + label( + "generators", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("]") + ], + {\tag("breakable"("{results,generators}"))} + ), + prod( + label("map", sort("Comprehension")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("from", sort("Expression")), + layouts("LAYOUTLIST"), + lit(":"), + layouts("LAYOUTLIST"), + label("to", sort("Expression")), + layouts("LAYOUTLIST"), + lit("|"), + layouts("LAYOUTLIST"), + label( + "generators", + \iter-seps ( + sort("Expression"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {\tag("breakable"("{from,to,generators}"))} + )} + ), + lex("TimeZonePart") : choice( + lex("TimeZonePart"), + {prod( + lex("TimeZonePart"), + [ + \char-class([range(43, 43), range(45, 45)]), + \char-class([range(48, 49)]), + \char-class([range(48, 57)]) + ], + {} + ), + prod(lex("TimeZonePart"), [lit("Z")], {}), + prod( + lex("TimeZonePart"), + [ + \char-class([range(43, 43), range(45, 45)]), + \char-class([range(48, 49)]), + \char-class([range(48, 57)]), + \char-class([range(48, 53)]), + \char-class([range(48, 57)]) + ], + {} + ), + prod( + lex("TimeZonePart"), + [ + \char-class([range(43, 43), range(45, 45)]), + \char-class([range(48, 49)]), + \char-class([range(48, 57)]), + lit(":"), + \char-class([range(48, 53)]), + \char-class([range(48, 57)]) + ], + {} + )} + ), + lex("PostStringChars") : choice( + lex("PostStringChars"), + {prod( + lex("PostStringChars"), + [ + \char-class([range(62, 62)]), + \iter-star(lex("StringCharacter")), + \char-class([range(34, 34)]) + ], + {\tag("category"("Constant"))} + )} + ), + lex("HexIntegerLiteral") : choice( + lex("HexIntegerLiteral"), + {prod( + lex("HexIntegerLiteral"), + [ + \char-class([range(48, 48)]), + \char-class([range(88, 88), range(120, 120)]), + conditional( + iter(\char-class([range(48, 57), range(65, 70), range(97, 102)])), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ], + {} + )} + ), + sort("Sym") : choice( + sort("Sym"), + {priority ( + sort("Sym"), + [ + choice( + sort("Sym"), + {prod( + label("except", sort("Sym")), + [ + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + lit("!"), + layouts("LAYOUTLIST"), + label("label", lex("NonterminalLabel")) + ], + {} + ), + prod( + label("iter", sort("Sym")), + [label("symbol", sort("Sym")), layouts("LAYOUTLIST"), lit("+")], + {} + ), + prod( + label("startOfLine", sort("Sym")), + [lit("^"), layouts("LAYOUTLIST"), label("symbol", sort("Sym"))], + {} + ), + prod( + label("iterSep", sort("Sym")), + [ + lit("{"), + layouts("LAYOUTLIST"), + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + label("sep", sort("Sym")), + layouts("LAYOUTLIST"), + lit("}"), + layouts("LAYOUTLIST"), + lit("+") + ], + {} + ), + prod( + label("parametrized", sort("Sym")), + [ + conditional( + label("nonterminal", lex("Nonterminal")), {follow(lit("["))} + ), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label( + "parameters", + \iter-seps ( + sort("Sym"), + [layouts("LAYOUTLIST"), lit(","), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("literal", sort("Sym")), + [label("string", lex("StringConstant"))], + {} + ), + prod( + label("optional", sort("Sym")), + [label("symbol", sort("Sym")), layouts("LAYOUTLIST"), lit("?")], + {} + ), + prod( + label("nonterminal", sort("Sym")), + [ + conditional( + label("nonterminal", lex("Nonterminal")), {\not-follow(lit("["))} + ) + ], + {} + ), + prod( + label("column", sort("Sym")), + [ + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + lit("@"), + layouts("LAYOUTLIST"), + label("column", sort("IntegerLiteral")) + ], + {} + ), + prod( + label("endOfLine", sort("Sym")), + [label("symbol", sort("Sym")), layouts("LAYOUTLIST"), lit("$")], + {} + ), + prod( + label("alternative", sort("Sym")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("first", sort("Sym")), + layouts("LAYOUTLIST"), + lit("|"), + layouts("LAYOUTLIST"), + label( + "alternatives", + \iter-seps ( + sort("Sym"), + [layouts("LAYOUTLIST"), lit("|"), layouts("LAYOUTLIST")] + ) + ), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("iterStarSep", sort("Sym")), + [ + lit("{"), + layouts("LAYOUTLIST"), + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + label("sep", sort("Sym")), + layouts("LAYOUTLIST"), + lit("}"), + layouts("LAYOUTLIST"), + lit("*") + ], + {} + ), + prod( + label("iterStar", sort("Sym")), + [label("symbol", sort("Sym")), layouts("LAYOUTLIST"), lit("*")], + {} + ), + prod( + label("sequence", sort("Sym")), + [ + lit("("), + layouts("LAYOUTLIST"), + label("first", sort("Sym")), + layouts("LAYOUTLIST"), + label("sequence", \iter-seps ( + sort("Sym"), + [layouts("LAYOUTLIST")] + )), + layouts("LAYOUTLIST"), + lit(")") + ], + {} + ), + prod( + label("characterClass", sort("Sym")), + [label("charClass", sort("Class"))], + {} + ), + prod( + label("empty", sort("Sym")), + [lit("("), layouts("LAYOUTLIST"), lit(")")], + {} + ), + prod( + label("caseInsensitiveLiteral", sort("Sym")), + [label("cistring", lex("CaseInsensitiveStringConstant"))], + {} + ), + prod( + label("start", sort("Sym")), + [ + lit("start"), + layouts("LAYOUTLIST"), + lit("["), + layouts("LAYOUTLIST"), + label("nonterminal", lex("Nonterminal")), + layouts("LAYOUTLIST"), + lit("]") + ], + {} + ), + prod( + label("parameter", sort("Sym")), + [ + lit("&"), + layouts("LAYOUTLIST"), + label("nonterminal", lex("Nonterminal")) + ], + {} + ), + prod( + label("labeled", sort("Sym")), + [ + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + label("label", lex("NonterminalLabel")) + ], + {} + )} + ), + associativity( + sort("Sym"), + \left(), + {prod( + label("notFollow", sort("Sym")), + [ + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + lit("!\>\>"), + layouts("LAYOUTLIST"), + label("match", sort("Sym")) + ], + {} + ), + prod( + label("precede", sort("Sym")), + [ + label("match", sort("Sym")), + layouts("LAYOUTLIST"), + lit("\<\<"), + layouts("LAYOUTLIST"), + label("symbol", sort("Sym")) + ], + {} + ), + prod( + label("follow", sort("Sym")), + [ + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + lit("\>\>"), + layouts("LAYOUTLIST"), + label("match", sort("Sym")) + ], + {} + ), + prod( + label("notPrecede", sort("Sym")), + [ + label("match", sort("Sym")), + layouts("LAYOUTLIST"), + lit("!\<\<"), + layouts("LAYOUTLIST"), + label("symbol", sort("Sym")) + ], + {} + )} + ), + prod( + label("unequal", sort("Sym")), + [ + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + lit("\\"), + layouts("LAYOUTLIST"), + label("match", sort("Sym")) + ], + {} + ) + ] + )} + ), + sort("QualifiedName") : choice( + sort("QualifiedName"), + {prod( + label("default", sort("QualifiedName")), + [ + conditional( + label( + "names", + \iter-seps ( + lex("Name"), + [layouts("LAYOUTLIST"), lit("::"), layouts("LAYOUTLIST")] + ) + ), + {\not-follow(lit("::"))} + ) + ], + {} + )} + ), + sort("StringMiddle") : choice( + sort("StringMiddle"), + {prod( + label("template", sort("StringMiddle")), + [ + label("mid", lex("MidStringChars")), + layouts("LAYOUTLIST"), + label("template", sort("StringTemplate")), + layouts("LAYOUTLIST"), + label("tail", sort("StringMiddle")) + ], + {} + ), + prod( + label("mid", sort("StringMiddle")), + [label("mid", lex("MidStringChars"))], + {} + ), + prod( + label("interpolated", sort("StringMiddle")), + [ + label("mid", lex("MidStringChars")), + layouts("LAYOUTLIST"), + label("expression", sort("Expression")), + layouts("LAYOUTLIST"), + label("tail", sort("StringMiddle")) + ], + {} + )} + ), + lex("URLChars") : choice( + lex("URLChars"), + {prod( + lex("URLChars"), + [ + \iter-star( + \char-class([ + range(1, 8), + range(11, 12), + range(14, 31), + range(33, 59), + range(61, 123), + range(125, 16777215) + ]) + ) + ], + {} + )} + ) + ) + ) ; + +str generateRascalParser() + = newGenerate( + "org.rascalmpl.library.lang.rascal.grammar.tests.generated_parsers", + "RascalParser", + Rascal + ); + +loc RascalParserLoc + = |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/| + + "generated_parsers/RascalParser.java.gz" ; + +void generateAndWriteRascalParser() { + writeFile(RascalParserLoc, generateRascalParser()); +} + +void warmup() { + for (int _ <- [1..10]) { + generateRascalParser(); + } +} + +int generateAndTimeRascalParser() { + warmup(); + t = cpuTime(); + generateRascalParser(); + used = (cpuTime() - t) / 1000000; + println("GenerateAndTimeRascalParser: ms"); + return used; +} + +value main() = generateAndTimeRascalParser(); + +test bool tstgenerateRascalParser() + = sameLines(generateRascalParser(), readFile(RascalParserLoc)); + +test bool cntChoice1() { + cnt = 0; + visit(Rascal) { + case choice(_, _): + cnt += 1; + } + ; + return cnt == 144; +} +test bool cntChoice2() = size([x | /x: choice(_, _) := Rascal]) == 144; + +test bool cntLex1() { + cnt = 0; + visit(Rascal) { + case lex(_): + cnt += 1; + } + ; + return cnt == 287; +} +test bool cntLex2() = size([x | /x: lex(_) := Rascal]) == 287; + +test bool cntEmpty1() { + cnt = 0; + visit(Rascal) { + case empty(): + cnt += 1; + } + ; + return cnt == 5; +} +test bool cntEmpty2() = size([x | /x: empty() := Rascal]) == 5; + +test bool cntSort1() { + cnt = 0; + visit(Rascal) { + case sort(_): + cnt += 1; + } + ; + return cnt == 1051; +} +test bool cntSort2() = size([x | /x: sort(_) := Rascal]) == 1051; + +test bool cntLit1() { + cnt = 0; + visit(Rascal) { + case lit(_): + cnt += 1; + } + ; + return cnt == 667; +} +test bool cntLit2() = size([x | /x: lit(_) := Rascal]) == 667; + +test bool cntLabel1() { + cnt = 0; + visit(Rascal) { + case label(_, _): + cnt += 1; + } + ; + return cnt == 961; +} +test bool cntLabel2() = size([x | /x: label(_, _) := Rascal]) == 961; + +test bool cntCharClass1() { + cnt = 0; + visit(Rascal) { + case \char-class(_): + cnt += 1; + } + ; + return cnt == 169; +} +test bool cntCharClass2() = size([x | /x: \char-class(_) := Rascal]) == 169; + +test bool cntProd1() { + cnt = 0; + visit(Rascal) { + case \prod(_, _, _): + cnt += 1; + } + ; + return cnt == 540; +} +test bool cntProd2() = size([x | /x: \prod(_, _, _) := Rascal]) == 540; + +test bool cntEmptyList1() { + cnt = 0; + visit(Rascal) { + case []: + cnt += 1; + } + ; + return cnt == 26; +} +test bool cntEmptyList2() = size([x | /x: [] := Rascal]) == 26; + +test bool cntList1() { + cnt = 0; + visit(Rascal) { + case [*value _]: + cnt += 1; + } + ; + return cnt == 837; +} +test bool cntList2() = size([x | /x: [*value _] := Rascal]) == 837; + +test bool cntEmptySet1() { + cnt = 0; + visit(Rascal) { + case {}: + cnt += 1; + } + ; + return cnt == 463; +} +test bool cntEmptySet2() = size([x | /x: {} := Rascal]) == 463; +@ignoreCompiler{Loop} +test bool cntSet1() { + cnt = 0; + visit(Rascal) { + case {*value _}: + cnt += 1; + } + ; + return cnt == 766; +} +@ignoreCompiler{Loop} +test bool cntSet2() = size([x | /x: {*value _} := Rascal]) == 766; +@ignoreInterpreter{gives wrong answer 1186} +test bool cntStr1() { + cnt = 0; + visit(Rascal) { + case str _: + cnt += 1; + } + ; + return cnt == 3967; +} +test bool cntStr2() = size([x | /x: str _ := Rascal]) == 3967; + +test bool cntInt1() { + cnt = 0; + visit(Rascal) { + case int _: + cnt += 1; + } + ; + return cnt == 808; +} +test bool cntInt2() = size([x | /x: int _ := Rascal]) == 808; + +test bool cntIter1() { + cnt = 0; + visit(Rascal) { + case \iter(_): + cnt += 1; + } + ; + return cnt == 12; +} +test bool cntIter2() = size([x | /x: \iter(_) := Rascal]) == 12; + +test bool cntIterStar1() { + cnt = 0; + visit(Rascal) { + case \iter-star(_): + cnt += 1; + } + ; + return cnt == 26; +} +test bool cntIterStar2() = size([x | /x: \iter-star(_) := Rascal]) == 26; + +test bool cntIterSeps1() { + cnt = 0; + visit(Rascal) { + case \iter-seps(_, _): + cnt += 1; + } + ; + return cnt == 51; +} +test bool cntIterSeps2() = size([x | /x: \iter-seps(_, _) := Rascal]) == 51; + +test bool cntIterStarSeps1() { + cnt = 0; + visit(Rascal) { + case \iter-star-seps(_, _): + cnt += 1; + } + ; + return cnt == 35; +} +test bool cntIterStarSeps2() + = size([x | /x: \iter-star-seps(_, _) := Rascal]) == 35; + +test bool cntConditional1() { + cnt = 0; + visit(Rascal) { + case \conditional(_, _): + cnt += 1; + } + ; + return cnt == 69; +} +test bool cntConditional2() = size([x | /x: \conditional(_, _) := Rascal]) == 69; + +test bool cntRange1() { + cnt = 0; + visit(Rascal) { + case \range(_, _): + cnt += 1; + } + ; + return cnt == 404; +} +test bool cntRange2() = size([x | /x: \range(_, _) := Rascal]) == 404; + +test bool cntPriority1() { + cnt = 0; + visit(Rascal) { + case \priority(_, _): + cnt += 1; + } + ; + return cnt == 5; +} +test bool cntPriority2() = size([x | /x: \priority(_, _) := Rascal]) == 5; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/grammar/tests/TestGrammars.rsc| +module lang::rascal::grammar::tests::TestGrammars + +import Grammar; +import ParseTree; + +public Grammar GEMPTY = grammar({sort("S")}, ()) ; + +private Production pr(Symbol rhs, list[Symbol] lhs) { + return prod(rhs, lhs, {}); +} + +public Grammar G0 + = grammar( + {sort("S")}, + ( + sort("S") : choice(sort("S"), {pr ( + sort("S"), + [lit("0")] + )}), + lit("0") : choice(lit("0"), {pr ( + lit("0"), + [\char-class([range(48, 48)])] + )}) + ) + ) ; + +public map[Symbol sort, Production def] Lit1 + = ( + lit("*") : choice(lit("*"), {pr ( + lit("*"), + [\char-class([range(42, 42)])] + )}), + lit("+") : choice(lit("+"), {pr ( + lit("+"), + [\char-class([range(43, 43)])] + )}), + lit("0") : choice(lit("0"), {pr ( + lit("0"), + [\char-class([range(48, 48)])] + )}), + lit("1") : choice(lit("1"), {pr ( + lit("1"), + [\char-class([range(49, 49)])] + )}) + ) ; + +public Grammar GEXP + = grammar( + {sort("E")}, + ( + sort("E") : choice( + sort("E"), + {pr ( + sort("E"), + [sort("E"), lit("*"), sort("B")] + ), + pr ( + sort("E"), + [sort("E"), lit("+"), sort("B")] + ), + pr ( + sort("E"), + [sort("B")] + )} + ), + sort("B") : choice(sort("B"), {pr ( + sort("B"), + [lit("0")] + ), pr ( + sort("B"), + [lit("1")] + )}) + ) + + Lit1 + ) ; + +public Grammar GEXPPRIO + = grammar( + {sort("E")}, + ( + sort("E") : choice(sort("E"), {pr ( + sort("E"), + [sort("T"), sort("E1")] + )}), + sort("E1") : choice( + sort("E1"), + {pr ( + sort("E1"), + [lit("+"), sort("T"), sort("E1")] + ), pr ( + sort("E1"), + [] + )} + ), + sort("T") : choice(sort("T"), {pr ( + sort("T"), + [sort("F"), sort("T1")] + )}), + sort("T1") : choice( + sort("T1"), + {pr ( + sort("F"), + [lit("*"), sort("F"), sort("T1")] + ), pr ( + sort("T1"), + [] + )} + ), + sort("F") : choice( + sort("F"), + {pr ( + sort("F"), + [lit("("), sort("E"), lit(")")] + ), pr ( + sort("F"), + [lit("id")] + )} + ), + lit("+") : choice(lit("+"), {pr ( + lit("+"), + [\char-class([range(43, 43)])] + )}), + lit("*") : choice(lit("*"), {pr ( + lit("*"), + [\char-class([range(42, 42)])] + )}), + lit("(") : choice(lit("("), {pr ( + lit("("), + [\char-class([range(40, 40)])] + )}), + lit(")") : choice(lit(")"), {pr ( + lit(")"), + [\char-class([range(41, 41)])] + )}), + lit("id") : choice( + lit("id"), + {pr ( + lit("id"), + [\char-class([range(105, 105)]), \char-class([range(100, 100)])] + )} + ) + ) + ) ; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/ide/Outline.rsc| +@bootstrapParser +module lang::rascal::ide::Outline + +import ParseTree; +import lang::rascal::\syntax::Rascal; +import Map; +import List; +import String; + +anno + str + node + @ + label +; +anno + loc + node + @ + \loc +; +anno + loc + FunctionDeclaration + @ + \loc +; +anno + loc + Declaration + @ + \loc +; +anno + loc + Name + @ + \loc +; +anno + loc + QualifiedName + @ + \loc +; +anno + loc + Signature + @ + \loc +; +anno + loc + Prod + @ + \loc +; + +node outline(start[Module] m) = outline(m.top); + +node outline(Module m) { + n = ""; + aliases = []; + annotations = []; + functions = (); + imports = []; + map[str, list[node]] grammars = (); + tags = []; + tests = (); + map[str, list[node]] adts = (); + variables = []; + list[node] e = []; + + top-down-break visit(m) { + case (Declaration) ` <{Variable ","}+ vars>;`: + variables += [clean(" ")()[@\loc = v@\loc] | v <- vars]; + case (Declaration) ` anno @;`: + annotations += [clean(" @")()[@\loc = name@\loc]]; + case (Declaration) ` alias = ;`: + aliases += [clean("")()[@\loc = u.name@\loc]]; + case (Declaration) ` tag on <{Type ","}+ _>;`: + tags += [clean("")()[@\loc = name@\loc]]; + + case (Declaration) ` data ;`: { + f = ""; + c = adts[""] ? e; + + if (kws is present) { + c + += [". "()[@\loc = k@\loc] + | KeywordFormal k <- kws.keywordFormalList]; + } + adts[f] = c; + } + + case (Declaration) ` data = <{Variant "|"}+ variants>;`: { + f = ""; + c = adts[f] ? e; + + if (kws is present) { + c += [". "()[@\loc = k@\loc] | k <- kws.keywordFormalList]; + } + c += [clean("")()[@\loc = v@\loc] | v <- variants]; + + adts[f] = c; + } + + case FunctionDeclaration func: { + f + = clean("")()[@label = " "][@\loc = func.signature@\loc]; + + if (/(FunctionModifier) `test` := func.signature) { + tests[clean("")] ? e += [f]; + } + else { + functions[clean("")] ? e += [f]; + } + } + + case (Import) `extend ;`: + imports += [""()[@\loc = mm@\loc]]; + + case (Import) `import ;`: + imports += [""()[@\loc = mm@\loc]]; + + case (Import) `import = ;`: + imports += [""()[@\loc = m2@\loc]]; + + case SyntaxDefinition def: { + f = ""; + c = grammars[f] ? e; + c + += ["

"()[@label = ""][@\loc = p@\loc] + | /Prod p := def.production, p is labeled || p is unlabeled, str prefix := (p is labeled ? ": " : "")]; + grammars[f] = c; + } + } + + map[node, list[node]] count(map[str, list[node]] m) + = ((!isEmpty(m[k]) + ? " ()"()[@\loc = (m[k][0])@\loc] + : " ()"()): + m[k] + | k <- m + ); + + return + n( + "Functions"(count(functions))[@label = "Functions ()"], + "Tests"(count(tests))[@label = "Tests ()"], + "Variables"(variables)[@label = "Variables ()"], + "Aliases"(aliases)[@label = "Aliases ()"], + "Data"(count(adts))[@label = "Data ()"], + "Annotations"(annotations)[@label = "Annotations ()"], + "Tags"(tags)[@label = "Tags ()"], + "Imports"(imports)[@label = "Imports ()"], + "Syntax"(count(grammars))[@label = "Syntax ()"] + ); +} + +// remove leading backslash +str clean(/\\/) = clean(rest); + +// multi-line becomes single line +str clean(str x: /\n/) = clean(visit(x) { + case /\n/ => " " + }); + +// cut-off too long +str clean(str x) = clean(x[..239]) + when size(x) > 256; + +// done +default str clean(str x) = x; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc| +@license{ + Copyright (c) 2023 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen Vinju - Jurgen.Vinju@cwi.nl} +@synopsis{Core functions for implementing fast pattern matching in the Rascal compiler.} +@description{ +These functions tie together the run-time features of IValue and ITree for computing fast +fingerprints, with compile-time information for generating switch cases that uses these fingerprints. + +There are several explicit contracts implemented here: + * a fingerprint is (almost) never `0`. + * the fingerprint functions in this module implement exactly the fingerprinting of the run-time that the generated code will be linked against. + This contract is tested with internal tests in this module: ((fingerprintAlignment)) and ((concreteFingerprintAlignment)). + If these tests fail, it is possible that during a bootstrap cycle of 3 steps, + the contract is temporarily not satisfied in the first and second steps. To break the impasse, the code below allows us to generate fingerprints for + the _next_ run-time version, while the current run-time still runs the _previous_ version of the compiler. We have to disable the ((concreteFingerprintAlignment)) + and ((fingerprintAlignment)) tests temporarily during the first and second run. + * `value matches pattern ==> fingerprint(pattern) == fingerprint(value)` such that a fingerprint is always an over-approximation of matching. It may + never be the case that a value should match a pattern and the fingerprint contradicts this. + This contract is tested by the pattern matching tests for the interpreter and the compiler. + * fingerprints distinguish the identity of the outermost value construct as much as possible. I.e. production rules and constructors are + mapped to different codes as much as possible, without breaking the fingerprinting contract. + This contract is not automatically tested. Performance regressions may be caused by accidental fingerprinting collisions. + * there is also an equals contract: `value1 equals value2 ==> fingerprint(value1) == fingerprint(value2)`, which is a corollary from the pattern + matching contract if you consider that patterns may also be equality tests. + +As you can read the computation of fingerprints reuses a lot of internal hashcodes. Mainly these boil down to the hash codes of: +* Java internal strings +* Java integers +* Vallang implementations of nested constructors for Symbol and Production. + +And so when one of these hashCode implementations changes, the code below may _not_ break and _not_ fail any test +and still break the backward compatibility of all previously generated code. The tests in the vallang project try to +detect such an event by replicating the hashcode computations literally in some of the regression tests. +} +module lang::rascal::matching::Fingerprint + +extend ParseTree; +import Node; +import List; + +@synopsis{Remove outer label from symbol, if any} +private Symbol delabel(Symbol s) = label(_, Symbol t) := s ? t : s; + +@synopsis{Computes a unique fingerprint for each kind of tree based on the identity of the top-level tree node.} +@description{ +Concrete fingerprint implements the pattern matching contract: +`value matches pattern ==> fingerprint(pattern) == fingerprint(value)` + +For normal parse trees the fingerprint function makes sure that there are different integers if the +top-level production is different. This makes it possible to quickly switch on the outermost production rule +while pattern matching. + +To complete the function for the other kinds of trees, even though less important for efficiency, we also +implement a sensible encoding that follows the contract and tries to differentiate as much as possible between different values. +} +int concreteFingerprint(appl(Production p, list[Tree] _)) + = concreteFingerprint(p); +int concreteFingerprint(amb({appl(prod(Symbol s, _, _), list[Tree] _), *_})) + = internalHashCode("amb") + 43 * internalHashCode(delabel(s)); +int concreteFingerprint(amb({})) = internalHashCode("amb"); +int concreteFingerprint(char(int ch)) + = internalHashCode("char") + internalHashCode(ch); +int concreteFingerprint(cycle(Symbol s, int _)) + = internalHashCode("cycle") + 13 * internalHashCode(s); + +@synopsis{Compute a fingerprint for a match pattern with this outermost production rule} +int concreteFingerprint(Production p) + = internalHashCode("appl") + 41 * internalHashCode(p); + +@synopsis{Computes a unique fingerprint for each kind of value based on the identity of the top-level kind.} +@description{ +Fingerprint implements the pattern matching contract: +`value matches pattern ==> fingerprint(pattern) == fingerprint(value)` + +Work is done to avoid generating the 0 fingerprint for simple values like empty strings and 0 integers, etc. +} +int fingerprint(str r) = hash == 0 ? internalHashCode("str") : hash + when int hash := internalHashCode(r); +int fingerprint(int r) = hash == 0 ? internalHashCode("int") : hash + when int hash := internalHashCode(r); +int fingerprint(real r) = hash == 0 ? internalHashCode("real") : hash + when int hash := internalHashCode(r); +int fingerprint(rat r) = hash == 0 ? internalHashCode("rat") : hash + when int hash := internalHashCode(r); +int fingerprint(value t) = tupleFingerprint(size(fields)) + when \tuple(list[Symbol] fields) := typeOf(t); +default int fingerprint(value n) = internalHashCode(n); + +int fingerprint(node n) = nodeFingerprint(getName(n), arity(n)); + +int fingerprint(list[value] l) = listFingerprint(); +int fingerprint(set[value] l) = setFingerprint(); +int fingerprint(map[value, value] l) = mapFingerprint(); + +int fingerprint(true) = internalHashCode("true"); +int fingerprint(false) = internalHashCode("true"); + +int nodeFingerprint("", int arity) = internalHashCode("node") + 131 * arity; +default int nodeFingerprint(str name, int arity) + = internalHashCode(name) + 131 * arity; + +int tupleFingerprint(int arity) = internalHashCode("tuple") + arity; +int listFingerprint() = internalHashCode("list"); +int setFingerprint() = internalHashCode("set"); +int mapFingerprint() = internalHashCode("map"); +int constructorFingerprint(str name, int arity) + = nodeFingerprint(name, arity); + +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +@synopsis{Compute the match fingerprint for any constant value. Only used for testing purposes.} +@description{ +To decouple the Rascal compilers code generator from the bootstrapped run-time it is running in itself, +the fingerprinting computation is replicated in this module. However, the computation should be the +same as this internalFingerprint function as long as nothing changes between compiler and run-time versions +in the computations for fingerprinting. +} +private java int internalFingerprint(value x); + +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +@synopsis{Compute the concrete match fingerprint for any parse `Tree`. Only used for testing purposes.} +@description{ +To decouple the Rascal compilers code generator from the bootstrapped run-time it is running in itself, +the fingerprinting computation is replicated in this module. However, the computation should be the +same as this internalFingerprint function as long as nothing changes between compiler and run-time versions +in the computations for fingerprinting. +} +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +private java int internalConcreteFingerprint(Tree x); + +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +@synopsis{Get the Object.hashCode() of the Java implementation of a Rascal value.} +@description{ +This hash code is sometimes a part of computing a fingerprint. Do not make this function +public. Rascal values are hashed already and exactly these hashes are used internally by the +set, relation and map data-structures. There is no need to write Rascal programs that "hash +on the hash", and it would leak implementation details that are very hard to encapsulate again. +} +private java int internalHashCode(value x); + +@synopsis{These two implementations are intentional clones.} +test bool fingerprintAlignment(value x) + = fingerprint(x) == internalFingerprint(x); + +@synopsis{These two implementations are intentional clones.} +test bool concreteFingerprintAlignment(Tree x) + = concreteFingerprint(x) == internalConcreteFingerprint(x); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/meta/ModuleInfo.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Mark Hills - mhills@cs.ecu.edu (ECU)} +@bootstrapParser +module lang::rascal::meta::ModuleInfo + +import lang::rascal::\syntax::Rascal; + +data ImportsInfo + = importsInfo(set[str] importedModules, set[str] extendedModules); + +public ImportsInfo getImports((Module)`

`) { + set[str] importedModules = {}; + set[str] extendedModules = {}; + + for (i <- h.imports, i has \module) { + if (i is \extend) { + extendedModules = extendedModules + ""; + } + else { + importedModules = importedModules + ""; + } + } + + return importsInfo(importedModules, extendedModules); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/meta/ModuleInfoTests.rsc| +@bootstrapParser +module lang::rascal::meta::ModuleInfoTests + +import lang::rascal::\syntax::Rascal; + +import lang::rascal::meta::ModuleInfo; + +test bool tstGetImports1() + = getImports((Module) `module MMM`) == importsInfo({}, {}); +test bool tstGetImports2() + = getImports((Module) `module MMM import A;`) == importsInfo({"A"}, {}); +test bool tstGetImports3() + = getImports((Module) `module MMM import A;extend B;import C;`) == importsInfo({"A", "C"}, {"B"}); +test bool tstGetImports4() + = getImports((Module) `module MMM syntax A = "a";`) == importsInfo({}, {}); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/mutation/ModuleMutator.rsc| +@license{ + Copyright (c) 2020, NWO-I Centrum Wiskunde & Informatica (CWI) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +} +@bootstrapParser +@synopsis{Mutant Generator for Rascal modules} +@description{ +Rascal module. The use case is to test how good the tests are for such a module. The tests + should be able to find the bugs we introduce using the mutators. + +Examples + +```rascal +import lang::rascal::mutation::ModuleMutator; +mutate(|project://rascal/src/org/rascalmpl/library/lang/rascal/format/Grammar.rsc|, mutations=5) +``` +} +@contributor{Cleverton Hentz} +@contributor{Jurgen Vinju} +module lang::rascal::mutation::ModuleMutator + +import lang::rascal::\syntax::Rascal; +import List; +import ParseTree; +import IO; + +list[str] mutate( + loc input, + int mutations = 5, + str folder = "mutants", + str prefix = "" +) { + m = parse(#start[Module], input).top; + return + for (new <- mutate(m, mutations = mutations, prefix = prefix, parentMod = folder)) { + muModName = ".rsc"; + writeFile(input.parent + folder + muModName, ""); + append muModName; + } +} + +list[Module] mutate( + Module input, + int mutations = 5, + str prefix = "", + str parentMod = "" +) { + list[Module] ret = []; + + for (opId <- muOpers) { + ret = ret + make(input, opId, prefix = prefix, parentMod = parentMod); + if (size(ret) >= mutations) + break; + } + return ret; +} + +private list[int] muOpers = [0..6] ; + +tuple[bool, PatternWithAction] muOperPatt( + 0, (PatternWithAction) ` =\> ` +) + = <true, + (PatternWithAction) ` : throw "mutant! OP0: Remove pattern rewrite.";`> + when !(p is concrete); + +tuple[bool, PatternWithAction] muOperPatt( + 1, (PatternWithAction) ` : ` +) + = <true, + (PatternWithAction) ` : throw "mutant! OP1: Remove pattern with action.";`> + when !(p is concrete); + +default tuple[bool, PatternWithAction] muOperPatt( + int opId, PatternWithAction pa +) + = <false, pa>; + +tuple[bool, Statement] muOperStm( + 2, (Statement) `if (<{Expression ","}+ cond>) ` +) + = <true, + (Statement) `if (<{Expression ","}+ cond>)  + ' throw "mutant! OP2: Remove if conditionals. Case 1.";`> + when !(cond is concrete); + +tuple[bool, Statement] muOperStm( + 3, + (Statement) `if (<{Expression ","}+ cond>) else ` +) + = <true, + (Statement) `if (<{Expression ","}+ cond>) throw "mutant! OP3: Remove if conditionals. Case 2."; else `> + when !(cond is concrete); + +tuple[bool, Statement] muOperStm( + 4, + (Statement) `if (<{Expression ","}+ cond>) else ` +) + = <true, + (Statement) `if (<{Expression ","}+ cond>) else throw "mutant! OP4: Remove if conditionals. Case 3.";`> + when !(cond is concrete); + +tuple[bool, Statement] muOperStm( + 5, (Statement) `while (<{Expression ","}+ cond>) ` +) + = <true, + (Statement) `while (<{Expression ","}+ cond>) throw "mutant! OP5: Remove while conditionals.";`> + when !(cond is concrete); + +tuple[bool, Statement] muOperStm( + 6, (Statement) `for (<{Expression ","}+ cond>) ` +) + = <true, + (Statement) `for (<{Expression ","}+ cond>) throw "mutant! OP6: Remove for conditionals.";`> + when !(cond is concrete); + +default tuple[bool, Statement] muOperStm(int opId, Statement stm) + = <false, stm>; + +list[Module] mutationOp(int opId, Module input) { + if (opId > muOpers[-1]) + throw "mutationOp: Invalid mutation operator!"; + list[Module] lMMod = []; + + Module m = input; + bool runOp = false; + int curMuOperPoint = 0; + int curMuOper = 1; + + while(!runOp) { + runOp = true; + curMuOperPoint = 0; + + m = top-down visit(input) { + // do not mutate inside test definitions: + case FunctionDeclaration d + => d + when runOp, \test() <- d.signature.modifiers.modifiers + + case PatternWithAction pa: { + if (runOp) { + tuple[bool r, PatternWithAction s] ret = muOperPatt(opId, pa); + if (ret.r) { + curMuOperPoint += 1; + if (curMuOper == curMuOperPoint) { + runOp = false; + insert ret.s; + } + } + } + } + + case Statement stm: { + if (!(stm is \visit) && runOp) { + tuple[bool r, Statement s] ret = muOperStm(opId, stm); + if (ret.r) { + curMuOperPoint += 1; + + if (curMuOper == curMuOperPoint) { + runOp = false; + insert ret.s; + } + } + } + } + } + ; + + if (!runOp) + lMMod += m; + curMuOper += 1; + } + return lMMod; +} + +list[Module] make(Module input, int opId, str prefix = "", str parentMod = "") { + list[Module] lMMod = []; + + if (opId > muOpers[-1]) + throw "make: Invalid mutation operator!"; + lMMod = mutationOp(opId, input); + + if (size(lMMod) == 0) + return []; + int bIndex = opId * 1000; + lMMod + = [rename(bIndex + mIdx, prefix, parentMod, lMMod[mIdx]) + | mIdx <- [0..size(lMMod)]]; + return lMMod; +} + +Module rename( + int c, str prefix, "", + (Module) ` module <{Name "::"}+ p> :: ` +) + = (Module) `  + 'module <{Name "::"}+ p>:: + ' + ' + ' + '` + when Name newName := [Name] ""; + +Module rename( + int c, str prefix, str parentMod, + (Module) ` module <{Name "::"}+ p> :: ` +) + = (Module) `  + 'module <{Name "::"}+ p>::::  + ' + ' + ' + '` + when Name newName := [Name] "" && Name f := [Name] ""; + +Module rename( + int c, str prefix, "", + (Module) ` module ` +) + = (Module) `  + 'module  + ' + ' + ' + '` + when Name newName := [Name] ""; + +Module rename( + int c, str prefix, str parentMod, + (Module) ` module ` +) + = (Module) ` + 'module :: + ' + ' + ' + '` + when Name newName := [Name] "" && Name f := [Name] ""; + +default str rename(Module x) { + throw "can not rename name of "; +} + +str getModuleName( + (Module) ` module <{Name "::"}+ _> :: ` +) + = ""; +str getModuleName( + (Module) ` module ` +) + = ""; +default str getModuleName(Module x) { + throw "can not find name of "; +} + +test bool renameTest1() + = rename(20, "", "", (Module) `module a::b::X`) == (Module) `module a::b::X20`; +test bool renameTest2() + = rename(20, "", "", (Module) `module X`) == (Module) `module X20`; + +test bool renameTest3() + = rename(20, "", "mutants", (Module) `module a::b::X`) == (Module) `module a::b::mutants::X20`; +test bool renameTest4() + = rename(20, "", "mutants", (Module) `module X`) == (Module) `module mutants::X20`; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/scrap/Patch.rsc| +module lang::rascal::scrap::Patch + +import lang::rascal::\syntax::Rascal; +import util::Reflective; +import ParseTree; +import List; +import String; + +@synopsis{Convert a sequence of commands to a textual patch value to be applied to the editor +containing the commands. The patch is based on the results of evaluating the commands +and comparing the outputs with what is in the source (pt) itself. Differences in command +output are reconciled through the patch. + +A patch is list of tuples from loc to str. Loc to "" represents removal. +A loc with length=0 to x represents insertion of x.} +lrel[loc, str] commands2patch(start[Commands] pt) { + // don't evaluate commands that represent output + cmds = ["" | c <- pt.top.commands, !(c is output)]; + results = evalCommands(cmds, pt@\loc); + + patch = []; + + args = pt.top.args[0].args; + // the list of Commands (EvalCommand+) + delta = 0; + // maintain where we are in the results list. + addedSpace = false; + + // whether a leading space was added as part of output. + change = false; + for (i <- [0..size(args)]) { + if (i mod 2 == 0) { + // a non-layout node + if (!(args[i] is output)) { + // a proper command + = resultSource(results[(i / 2) - delta]); + + old = ""; + j = i + 2; + + // collect all subsequent layout and outputs following + // the current command to determine whether the computed + // output is different from the output in the previous run. + while(j < size(args), args[j] is output) { + old += ""; + j += 2; + } + + // if there's a change in output, add a tuple + // to the patch. + if (new != "" && trim(old) != trim(new)) { + org = args[i]@\loc; + at = org.offset + org.length; + l = org[offset = at][length = 0]; + // insert + patch += []; + change = true; + } + else { + // signal to following iterations that there was no change. + change = false; + } + } + else { + if (change) { + // only remove previous output nodes if there was a change + patch += []; + } + // output commands are not evaluated by evalCommands above; + // delta maintains the difference for indexing. + delta += 1; + } + } + else { + l = ""; + if (addedSpace && change && startsWith(l, " ")) { + // if a leading space was added in the case of changed output, + // remove it here. Otherwise leave the layout unchanged. + org = args[i]@\loc; + patch += []; + addedSpace = false; + } + } + } + + return patch; +} + +private +tuple[bool, str] resultSource(tuple[str val, str out, str err] output) { + code = ""; + addedSpace = false; + if (output.val == "", output.out == "", output.err == "") { + return ; + } + if (output.val != "") { + x = output.val; + if (contains(x, "origin=")) { + x = substring(x, 0, findFirst(x, ":")); + } + code += " ⇨ \n"; + addedSpace = true; + } + if (output.out != "") { + if (!endsWith(code, "\n")) { + code += "\n"; + } + code += "≫ "; + } + if (output.err != "") { + if (!endsWith(code, "\n")) { + code += "\n"; + } + err = output.err; + if (contains(err, "")) { + err = substring(err, 0, findFirst(err, "")); + } + code += "⚠ "; + } + return ; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/syntax/Rascal.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Michael Steindorfer - Michael.Steindorfer@cwi.nl - CWI} +@synopsis{The syntax definition of Rascal, excluding concrete syntax fragments} +@bootstrapParser +module lang::rascal::\syntax::Rascal + +lexical BooleanLiteral + = "true" + | "false" + ; + +syntax Literal + = @category="number" integer: IntegerLiteral integerLiteral + | @category="regexp" regExp: RegExpLiteral regExpLiteral + | @category="number" \real: RealLiteral realLiteral + | boolean: BooleanLiteral booleanLiteral + | string: StringLiteral stringLiteral + | dateTime: DateTimeLiteral dateTimeLiteral + | @category="string" location: LocationLiteral locationLiteral + | @category="number" rational: RationalLiteral rationalLiteral + ; + +syntax Expression = concrete: Concrete concrete; +syntax Pattern = concrete: Concrete concrete; + +lexical Concrete = typed: "(" LAYOUTLIST l1 Sym symbol LAYOUTLIST l2 ")" LAYOUTLIST l3 "`" ConcretePart* parts "`"; + +lexical ConcretePart + = @category="string" text: ![`\<\>\\\n]+ !>> ![`\<\>\\\n] + | newline: "\n" [\ \t\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]* "\'" + | @category="variable" hole: ConcreteHole hole + | @category="string" lt: "\\\<" + | @category="string" gt: "\\\>" + | @category="string" bq: "\\`" + | @category="string" bs: "\\\\" + ; + +syntax ConcreteHole = \one: "\<" Sym symbol Name name "\>"; + +start syntax Module = \default: Header header Body body; + +syntax ModuleParameters = \default: "[" {TypeVar ","}+ parameters "]"; + +lexical DateAndTime + = "$" DatePart "T" TimePartNoTZ !>> [+\-] "$" + | "$" DatePart "T" TimePartNoTZ TimeZonePart "$" + ; + +syntax Strategy + = topDownBreak: "top-down-break" + | topDown: "top-down" + | bottomUp: "bottom-up" + | bottomUpBreak: "bottom-up-break" + | outermost: "outermost" + | innermost: "innermost" + ; + +lexical UnicodeEscape + = utf16: "\\" [u] [0-9A-Fa-f] [0-9A-Fa-f] [0-9A-Fa-f] [0-9A-Fa-f] + | utf32: "\\" [U] (("0" [0-9A-Fa-f]) | "10") [0-9A-Fa-f] [0-9A-Fa-f] [0-9A-Fa-f] [0-9A-Fa-f] + // 24 bits + | ascii: "\\" [a] [0-7] [0-9A-Fa-f] + ; + +syntax Variable + = initialized: Name name "=" Expression initial + | unInitialized: Name name + ; + +lexical OctalIntegerLiteral = [0] [0-7]+ !>> [0-9A-Z_a-z]; + +syntax TypeArg + = \default: Type type + | named: Type type Name name + ; + +syntax Renaming = \default: Name from "=\>" Name to; + +syntax Catch + = \default: "catch" ":" Statement body + | binding: "catch" Pattern pattern ":" Statement body + ; + +lexical PathChars = URLChars [|]; + +syntax Signature + = withThrows: FunctionModifiers modifiers Type type Name name Parameters parameters "throws" {Type ","}+ exceptions + | noThrows: FunctionModifiers modifiers Type type Name name Parameters parameters + ; + +syntax Sym + // named non-terminals + = nonterminal: Nonterminal nonterminal !>> "[" + | parameter: "&" Nonterminal nonterminal + | parametrized: Nonterminal nonterminal >> "[" "[" {Sym ","}+ parameters "]" + | \start: "start" "[" Nonterminal nonterminal "]" + | labeled: Sym symbol NonterminalLabel label + // literals + | characterClass: Class charClass + | literal: StringConstant string + | caseInsensitiveLiteral: CaseInsensitiveStringConstant cistring + // regular expressions + | iter: Sym symbol "+" + | iterStar: Sym symbol "*" + | iterSep: "{" Sym symbol Sym sep "}" "+" + | iterStarSep: "{" Sym symbol Sym sep "}" "*" + | optional: Sym symbol "?" + | alternative: "(" Sym first "|" {Sym "|"}+ alternatives ")" + | sequence: "(" Sym first Sym+ sequence ")" + // TODO: MinimalIter: Sym symbol IntegerConstant minimal "+" + // TODO: MinimalIterSep: "{" Sym symbol Symbol sep "}" IntegerConstant minimal "+" + // TODO | Permutation: "(" Sym first "~" {Sym "~"}+ participants ")" + // TODO | Combination: "(" Sym first "#" {Sym "#"}+ elements ")" + | empty: "(" ")" + // conditionals + | column: Sym symbol "@" IntegerLiteral column + | endOfLine: Sym symbol "$" + | startOfLine: "^" Sym symbol + | except: Sym symbol "!" NonterminalLabel label + > assoc ( left ( follow: Sym symbol "\>\>" Sym match | notFollow: Sym symbol "!\>\>" Sym match ) + | right ( precede: Sym match "\<\<" Sym symbol | notPrecede: Sym match "!\<\<" Sym symbol ) + ) + > left unequal: Sym symbol "\\" Sym match + ; + +lexical TimePartNoTZ + = [0-2] [0-9] [0-5] [0-9] [0-5] [0-9] ([,.] [0-9] ([0-9] [0-9]?)?)? + | [0-2] [0-9] ":" [0-5] [0-9] ":" [0-5] [0-9] ([,.] [0-9] ([0-9] [0-9]?)?)? + ; + +syntax Header + = parameters: Tags tags "module" QualifiedName name ModuleParameters params Import* imports + | \default: Tags tags "module" QualifiedName name Import* imports + ; + +lexical Name + // Names are surrounded by non-alphabetical characters, i.e. we want longest match. + = ([A-Za-z_] !<< [A-Z_a-z] [0-9A-Z_a-z]* !>> [0-9A-Z_a-z]) \ RascalKeywords + | [\\] [A-Z_a-z] [\-0-9A-Z_a-z]* !>> [\-0-9A-Z_a-z] + ; + +syntax SyntaxDefinition + = @Foldable \layout: Visibility vis "layout" Sym defined "=" Prod production ";" + | @Foldable \lexical: "lexical" Sym defined "=" Prod production ";" + | @Foldable \keyword: "keyword" Sym defined "=" Prod production ";" + | @Foldable language: Start start "syntax" Sym defined "=" Prod production ";" + ; + +syntax Kind + = function: "function" + | variable: "variable" + | \all: "all" + | \anno: "anno" + | \data: "data" + | view: "view" + | \alias: "alias" + | \module: "module" + | \tag: "tag" + ; + +syntax ImportedModule + = \default: QualifiedName name + | actualsRenaming: QualifiedName name ModuleActuals actuals Renamings renamings + | renamings: QualifiedName name Renamings renamings + | actuals: QualifiedName name ModuleActuals actuals + ; + +syntax Target + = empty: + | labeled: Name name + ; + +syntax IntegerLiteral + = /*prefer()*/ decimalIntegerLiteral: DecimalIntegerLiteral decimal + | /*prefer()*/ hexIntegerLiteral: HexIntegerLiteral hex + | /*prefer()*/ octalIntegerLiteral: OctalIntegerLiteral octal + ; + +syntax FunctionBody = \default: "{" Statement* statements "}"; + +syntax Expression + = nonEmptyBlock: "{" Statement+ statements "}" + | bracket \bracket: "(" Expression expression ")" + | closure: Type type Parameters parameters "{" Statement+ statements "}" + | stepRange: "[" Expression first "," Expression second ".." Expression last "]" + | voidClosure: Parameters parameters "{" Statement* statements0 "}" + | \visit: Label label Visit visit + | reducer: "(" Expression init "|" Expression result "|" {Expression ","}+ generators ")" + | reifiedType: "type" "(" Expression symbol "," Expression definitions ")" + | callOrTree: Expression ! transitiveClosure ! transitiveReflexiveClosure ! isDefined + expression "(" {Expression ","}* arguments KeywordArguments[Expression] keywordArguments ")" + | literal: Literal literal + | \any: "any" "(" {Expression ","}+ generators ")" + | \all: "all" "(" {Expression ","}+ generators ")" + | comprehension: Comprehension comprehension + | \set: "{" {Expression ","}* elements0 "}" + | \list: "[" {Expression ","}* elements0 "]" + | reifyType: "#" Type type !>> "[" ! selector + | range: "[" Expression first ".." Expression last "]" + | \tuple: "\<" {Expression ","}+ elements "\>" + | \map: "(" {Mapping[Expression] ","}* mappings ")" + | \it: [A-Za-z_] !<< "it" !>> [A-Za-z_] + | qualifiedName: QualifiedName qualifiedName + | subscript: Expression expression ! transitiveClosure ! transitiveReflexiveClosure + ! isDefined "[" {Expression ","}+ subscripts "]" + | slice: Expression expression ! transitiveClosure ! transitiveReflexiveClosure + ! + isDefined "[" OptionalExpression optFirst ".." OptionalExpression optLast "]" + | sliceStep: Expression expression ! transitiveClosure ! transitiveReflexiveClosure + ! + isDefined "[" OptionalExpression optFirst "," Expression second ".." OptionalExpression optLast "]" + | fieldAccess: Expression expression "." Name field + | fieldUpdate: Expression expression "[" Name key "=" Expression replacement "]" + | fieldProject: Expression expression ! transitiveClosure ! transitiveReflexiveClosure + ! isDefined "\<" {Field ","}+ fields "\>" + | setAnnotation: Expression expression "[" "@" Name name "=" Expression value "]" + | getAnnotation: Expression expression >> "@" "@" Name name + | is: Expression expression "is" Name name + | has: Expression expression "has" Name name + | transitiveClosure: Expression argument "+" !>> "=" + | transitiveReflexiveClosure: Expression argument "*" !>> "=" + > isDefined: Expression argument "?" + > negation: "!" Expression ! match ! noMatch argument + | negative: "-" Expression argument + | non-assoc splice: "*" Expression argument + | asType: "[" Type type "]" Expression ! match ! noMatch argument + > left composition: Expression lhs "o" Expression rhs + > left ( product: Expression lhs "*" () !>> "*" Expression ! noMatch ! match rhs + | \join: Expression lhs "join" Expression rhs + | remainder: Expression lhs "%" Expression rhs + | division: Expression lhs "/" Expression rhs + ) + > left intersection: Expression lhs "&" !>> "&" Expression rhs + > left ( addition: Expression lhs "+" Expression ! noMatch ! match rhs + | subtraction: Expression ! transitiveClosure ! transitiveReflexiveClosure lhs "-" Expression rhs + | appendAfter: Expression lhs "\<\<" !>> "=" Expression rhs + | insertBefore: Expression lhs "\>\>" Expression rhs + ) + > left modulo: Expression lhs "mod" Expression rhs + > non-assoc ( notIn: Expression lhs "notin" Expression rhs + | \in: Expression lhs "in" Expression rhs + ) + > non-assoc ( greaterThanOrEq: Expression lhs "\>=" Expression rhs + | lessThanOrEq: Expression lhs "\<=" Expression rhs + | lessThan: Expression lhs "\<" !>> "-" Expression rhs + | greaterThan: Expression lhs "\>" Expression rhs + ) + > non-assoc ( equals: Expression lhs "==" Expression rhs + | nonEquals: Expression lhs "!=" Expression rhs + ) + > non-assoc ifDefinedOtherwise: Expression lhs "?" Expression rhs + > non-assoc ( noMatch: Pattern pattern "!:=" Expression expression + | match: Pattern pattern ":=" Expression expression + | enumerator: Pattern pattern "\<-" Expression expression + ) + > non-assoc ( implication: Expression lhs "==\>" Expression rhs + | equivalence: Expression lhs "\<==\>" Expression rhs + ) + > left and: Expression lhs "&&" Expression rhs + > left or: Expression lhs "||" Expression rhs + > right ifThenElse: Expression condition "?" Expression thenExp ":" Expression elseExp + ; + +syntax OptionalExpression + = expression: Expression expression + | noExpression: () + ; + +syntax UserType + = name: QualifiedName name + | parametric: QualifiedName name >> "[" "[" {Type ","}+ parameters "]" + ; + +syntax Import + = \extend: "extend" ImportedModule module ";" + | \default: "import" ImportedModule module ";" + | \external: "import" QualifiedName name "=" LocationLiteral at ";" + | \syntax: SyntaxDefinition syntax + ; + +syntax Body = toplevels: Toplevel* toplevels; + +lexical URLChars = ![\t-\n\r\ \<|]*; + +lexical TimeZonePart + = [+\-] [0-1] [0-9] ":" [0-5] [0-9] + | "Z" + | [+\-] [0-1] [0-9] + | [+\-] [0-1] [0-9] [0-5] [0-9] + ; + +syntax ProtocolPart + = nonInterpolated: ProtocolChars protocolChars + | interpolated: PreProtocolChars pre Expression expression ProtocolTail tail + ; + +syntax StringTemplate + = ifThen: "if" "(" {Expression ","}+ conditions ")" "{" Statement* preStats StringMiddle body Statement* postStats "}" + | ifThenElse: "if" "(" {Expression ","}+ conditions ")" "{" Statement* preStatsThen StringMiddle thenString Statement* postStatsThen "}" "else" "{" Statement* preStatsElse StringMiddle elseString Statement* postStatsElse "}" + | \for: "for" "(" {Expression ","}+ generators ")" "{" Statement* preStats StringMiddle body Statement* postStats "}" + | doWhile: "do" "{" Statement* preStats StringMiddle body Statement* postStats "}" "while" "(" Expression condition ")" + | \while: "while" "(" Expression condition ")" "{" Statement* preStats StringMiddle body Statement* postStats "}" + ; + +lexical PreStringChars = @category="string" [\"] StringCharacter* [\<]; + +lexical CaseInsensitiveStringConstant = @category="string" "\'" StringCharacter* chars "\'"; + +lexical Backslash = [\\] !>> [/\<\>\\]; + +syntax Label + = \default: Name name ":" + | empty: + ; + +lexical MidProtocolChars = "\>" URLChars "\<"; + +lexical NamedBackslash = [\\] !>> [\<\>\\]; + +syntax Field + = index: IntegerLiteral fieldIndex + | name: Name fieldName + ; + +lexical JustDate = "$" DatePart "$"; + +lexical PostPathChars = "\>" URLChars "|"; + +syntax PathPart + = nonInterpolated: PathChars pathChars + | interpolated: PrePathChars pre Expression expression PathTail tail + ; + +lexical DatePart + = [0-9] [0-9] [0-9] [0-9] "-" [0-1] [0-9] "-" [0-3] [0-9] + | [0-9] [0-9] [0-9] [0-9] [0-1] [0-9] [0-3] [0-9] + ; + +syntax FunctionModifier + = java: "java" + | \test: "test" + | \default: "default" + ; + +syntax Assignment + = ifDefined: "?=" + | division: "/=" + | product: "*=" + | intersection: "&=" + | subtraction: "-=" + | \default: "=" + | addition: "+=" + | \append: "\<\<=" + ; + +syntax Assignable + = bracket \bracket: "(" Assignable arg ")" + | variable: QualifiedName qualifiedName + | subscript: Assignable receiver "[" Expression subscript "]" + | slice: Assignable receiver "[" OptionalExpression optFirst ".." OptionalExpression optLast "]" + | sliceStep: Assignable receiver "[" OptionalExpression optFirst "," Expression second ".." OptionalExpression optLast "]" + | fieldAccess: Assignable receiver "." Name field + | ifDefinedOrDefault: Assignable receiver "?" Expression defaultExpression + | constructor: Name name "(" {Assignable ","}+ arguments ")" + | \tuple: "\<" {Assignable ","}+ elements "\>" + | annotation: Assignable receiver "@" Name annotation + ; + +lexical StringConstant = @category="string" "\"" StringCharacter* chars "\""; + +syntax Assoc + = associative: "assoc" + | left: "left" + | nonAssociative: "non-assoc" + | right: "right" + ; + +syntax Replacement + = unconditional: Expression replacementExpression + | conditional: Expression replacementExpression "when" {Expression ","}+ conditions + ; + +syntax DataTarget + = empty: + | labeled: Name label ":" + ; + +lexical StringCharacter + = "\\" [\"\'\<\>\\bfnrt] + | UnicodeEscape + | ![\"\'\<\>\\] + | [\n] [\ \t\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]* [\'] + // margin + ; + +lexical JustTime + = "$T" TimePartNoTZ !>> [+\-] "$" + | "$T" TimePartNoTZ TimeZonePart "$" + ; + +lexical MidStringChars = @category="string" [\>] StringCharacter* [\<]; + +lexical ProtocolChars = [|] URLChars "://" !>> [\t-\n\r\ \u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]; + +lexical RegExpModifier = [dims]*; + +syntax CommonKeywordParameters + = absent: () + | present: "(" {KeywordFormal ","}+ keywordFormalList ")" + ; + +syntax Parameters + = \default: "(" Formals formals KeywordFormals keywordFormals ")" + | varArgs: "(" Formals formals "..." KeywordFormals keywordFormals ")" + ; + +lexical OptionalComma = \default: ","?; + +syntax KeywordFormals + = \default: OptionalComma optionalComma [,\ (\t\n] << {KeywordFormal ","}+ keywordFormalList + | none: () + ; + +syntax KeywordFormal = \default: Type type Name name "=" Expression expression; + +syntax KeywordArguments[&T] + = \default: OptionalComma optionalComma [,\ (\t\n] << {KeywordArgument[&T] ","}+ keywordArgumentList + | none: () + ; + +syntax KeywordArgument[&T] = \default: Name name "=" &T expression; + +lexical RegExp + = ![/\<\>\\] + | "\<" Name "\>" + | [\\] [/\<\>\\] + | "\<" Name ":" NamedRegExp* "\>" + | Backslash + // | @category="variable" [\<] Expression expression [\>] TODO: find out why this production existed + ; + +layout LAYOUTLIST = LAYOUT* !>> [\u0009-\u000D\u0020\u0085\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000] !>> "//" !>> "/*"; + +syntax LocalVariableDeclaration + = \default: Declarator declarator + | \dynamic: "dynamic" Declarator declarator + ; + +lexical RealLiteral + = [0-9]+ [DFdf] + | [0-9]+ [Ee] [+\-]? [0-9]+ [DFdf]? + | [0-9]+ "." !>> "." [0-9]* [DFdf]? + | [0-9]+ "." [0-9]* [Ee] [+\-]? [0-9]+ [DFdf]? + | [.] !<< "." [0-9]+ [DFdf]? + | [.] !<< "." [0-9]+ [Ee] [+\-]? [0-9]+ [DFdf]? + ; + +syntax Range + = fromTo: Char start "-" Char end + | character: Char character + ; + +syntax LocationLiteral = \default: ProtocolPart protocolPart PathPart pathPart; + +syntax ShellCommand + = setOption: "set" QualifiedName name OptionalEqualSign sign Expression expression OptionalTerminator terminator + | setOptionTrue: "set" QualifiedName name OptionalTerminator terminator + | unsetOption: "unset" QualifiedName name OptionalTerminator terminator + | undeclare: "undeclare" QualifiedName? optName OptionalTerminator terminator + | help: "help" OptionalTerminator terminator + | edit: "edit" QualifiedName name OptionalTerminator terminator + | unimport: "unimport" QualifiedName name OptionalTerminator terminator + | unextend: "unextend" QualifiedName name OptionalTerminator terminator + | listDeclarations: "declarations" OptionalTerminator terminator + | quit: "quit" OptionalTerminator terminator + | history: "history" OptionalTerminator terminator + | \test: "test" QualifiedName? optName OptionalTerminator terminator + | listModules: "modules" OptionalTerminator terminator + | clear: "clear" OptionalTerminator terminator + ; + +lexical OptionalEqualSign + = absent: () + | present: "=" + ; + +lexical OptionalTerminator + = absent: () + | present: ";" + ; + +syntax StringMiddle + = mid: MidStringChars mid + | template: MidStringChars mid StringTemplate template StringMiddle tail + | interpolated: MidStringChars mid Expression expression StringMiddle tail + ; + +syntax QualifiedName = \default: {Name "::"}+ names !>> "::"; + +lexical RationalLiteral + = [0-9] [0-9]* [r] + | [1-9] [0-9]* [r] [0-9] [0-9]* !>> [0-9A-Z_a-z] + ; + +lexical DecimalIntegerLiteral + = "0" !>> [0-9A-Z_a-z] + | [1-9] [0-9]* !>> [0-9A-Z_a-z] + ; + +syntax DataTypeSelector = selector: QualifiedName sort "." Name production; + +syntax StringTail + = midInterpolated: MidStringChars mid Expression expression StringTail tail + | post: PostStringChars post + | midTemplate: MidStringChars mid StringTemplate template StringTail tail + ; + +syntax PatternWithAction + = replacing: Pattern pattern "=\>" Replacement replacement + | arbitrary: Pattern pattern ":" Statement statement + ; + +lexical LAYOUT + = Comment + // all the white space chars defined in Unicode 6.0 + | [\u0009-\u000D\u0020\u0085\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000] + ; + +syntax Visit + = givenStrategy: Strategy strategy "visit" "(" Expression subject ")" "{" Case+ cases "}" + | defaultStrategy: "visit" "(" Expression subject ")" "{" Case+ cases "}" + ; + +start syntax Commands = \commandlist: EvalCommand+ commands; + +start syntax EvalCommand + = declaration: Declaration declaration + | statement: Statement ! variableDeclaration ! functionDeclaration ! visit statement + | \import: Import imported + | output: Output + ; + +lexical Output + = @category="string" resultOutput: "⇨" ![\n\r]* [\n] + | @category="string" stdoutOutput: ^"≫" ![\n\r]* [\n] + | @category="string" stderrOutput: ^"⚠" ![\n\r]* [\n] + ; + +start syntax Command + = expression: Expression ! nonEmptyBlock expression + | declaration: Declaration declaration + | shell: ":" ShellCommand command + | statement: Statement ! variableDeclaration ! functionDeclaration ! visit statement + | \import: Import imported + ; + +lexical TagString = "\\" !<< "{" (![{}] | ("\\" [{}]) | TagString)* contents "\\" !<< "}"; + +syntax ProtocolTail + = mid: MidProtocolChars mid Expression expression ProtocolTail tail + | post: PostProtocolChars post + ; + +lexical Nonterminal = ([A-Z] !<< [A-Z] [0-9A-Z_a-z]* !>> [0-9A-Z_a-z]) \ RascalKeywords; + +syntax PathTail + = mid: MidPathChars mid Expression expression PathTail tail + | post: PostPathChars post + ; + +syntax Visibility + = \private: "private" + | \default: + | \public: "public" + ; + +syntax StringLiteral + = template: PreStringChars pre StringTemplate template StringTail tail + | interpolated: PreStringChars pre Expression expression StringTail tail + | nonInterpolated: StringConstant constant + ; + +lexical Comment + = @category="comment" "/*" (![*] | [*] !>> [/])* "*/" + | @category="comment" "//" ![\n]* !>> [\ \t\r\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]$ + // the restriction helps with parsing speed + ; + +syntax Renamings = \default: "renaming" {Renaming ","}+ renamings; + +syntax Tags = \default: Tag* tags; + +syntax Formals = \default: {Pattern ","}* formals; + +lexical PostProtocolChars = "\>" URLChars "://"; + +syntax Start + = absent: + | present: "start" + ; + +syntax Statement + = @breakable \assert: "assert" Expression expression ";" + | @breakable assertWithMessage: "assert" Expression expression ":" Expression message ";" + | @breakable expression: Expression ! visit ! nonEmptyBlock expression ";" + | @breakable \visit: Label label Visit visit + | @breakable \while: Label label "while" "(" {Expression ","}+ conditions ")" Statement ! variableDeclaration ! functionDeclaration body + | @breakable doWhile: Label label "do" Statement body "while" "(" Expression condition ")" ";" + | @breakable @breakable{generators} \for: Label label "for" "(" {Expression ","}+ generators ")" Statement body + | @breakable ifThen: Label label "if" "(" {Expression ","}+ conditions ")" Statement ! variableDeclaration ! functionDeclaration thenStatement () !>> "else" + | @breakable ifThenElse: Label label "if" "(" {Expression ","}+ conditions ")" Statement thenStatement "else" Statement ! variableDeclaration ! functionDeclaration elseStatement + | @breakable \switch: Label label "switch" "(" Expression expression ")" "{" Case+ cases "}" + | @breakable \fail: "fail" Target target ";" + | @breakable \break: "break" Target target ";" + | @breakable \continue: "continue" Target target ";" + | @breakable \filter: "filter" ";" + | @breakable \solve: "solve" "(" {QualifiedName ","}+ variables Bound bound ")" Statement ! variableDeclaration ! functionDeclaration body + | @breakable non-assoc \try: "try" Statement body Catch+ handlers + | @breakable tryFinally: "try" Statement body Catch+ handlers "finally" Statement ! variableDeclaration ! functionDeclaration finallyBody + | nonEmptyBlock: Label label "{" Statement+ statements "}" + | emptyStatement: ";" + | @breakable globalDirective: "global" Type type {QualifiedName ","}+ names ";" + | @breakable assignment: Assignable assignable Assignment operator Statement ! functionDeclaration ! variableDeclaration statement + | non-assoc ( @breakable \return: "return" Statement ! functionDeclaration ! variableDeclaration statement + | @breakable \throw: "throw" Statement ! functionDeclaration ! variableDeclaration statement + | @breakable \insert: "insert" DataTarget dataTarget Statement ! functionDeclaration ! variableDeclaration statement + | @breakable \append: "append" DataTarget dataTarget Statement ! functionDeclaration ! variableDeclaration statement + ) + | @breakable functionDeclaration: FunctionDeclaration functionDeclaration + | @breakable variableDeclaration: LocalVariableDeclaration declaration ";" + ; + +syntax StructuredType = \default: BasicType basicType "[" {TypeArg ","}+ arguments "]"; + +lexical NonterminalLabel = [a-z] [0-9A-Z_a-z]* !>> [0-9A-Z_a-z]; + +syntax FunctionType = typeArguments: Type type "(" {TypeArg ","}* arguments ")"; + +syntax Case + = @Foldable patternWithAction: "case" PatternWithAction patternWithAction + | @Foldable \default: "default" ":" Statement statement + ; + +syntax Declarator = \default: Type type {Variable ","}+ variables; + +syntax Bound + = \default: ";" Expression expression + | empty: + ; + +keyword RascalKeywords + = "o" + | "syntax" + | "keyword" + | "lexical" + | "int" + | "break" + | "continue" + | "rat" + | "true" + | "bag" + | "num" + | "node" + | "finally" + | "private" + | "real" + | "list" + | "fail" + | "filter" + | "if" + | "tag" + | BasicType + | "extend" + | "append" + | "rel" + | "lrel" + | "void" + | "non-assoc" + | "assoc" + | "test" + | "anno" + | "layout" + | "data" + | "join" + | "it" + | "bracket" + | "in" + | "import" + | "false" + | "all" + | "dynamic" + | "solve" + | "type" + | "try" + | "catch" + | "notin" + | "else" + | "insert" + | "switch" + | "return" + | "case" + | "while" + | "str" + | "throws" + | "visit" + | "tuple" + | "for" + | "assert" + | "loc" + | "default" + | "map" + | "alias" + | "any" + | "module" + | "mod" + | "bool" + | "public" + | "one" + | "throw" + | "set" + | "start" + | "datetime" + | "value" + ; + +syntax Type + = bracket \bracket: "(" Type type ")" + | user: UserType user + | function: FunctionType function + | structured: StructuredType structured + | basic: BasicType basic + | selector: DataTypeSelector selector + | variable: TypeVar typeVar + | symbol: Sym ! nonterminal ! labeled ! parametrized ! parameter symbol + ; + +syntax Declaration + = variable: Tags tags Visibility visibility Type type {Variable ","}+ variables ";" + | annotation: Tags tags Visibility visibility "anno" Type annoType Type onType "@" Name name ";" + | \alias: Tags tags Visibility visibility "alias" UserType user "=" Type base ";" + | \tag: Tags tags Visibility visibility "tag" Kind kind Name name "on" {Type ","}+ types ";" + | dataAbstract: Tags tags Visibility visibility "data" UserType user CommonKeywordParameters commonKeywordParameters ";" + | @Foldable \data: Tags tags Visibility visibility "data" UserType user CommonKeywordParameters commonKeywordParameters "=" {Variant "|"}+ variants ";" + | function: FunctionDeclaration functionDeclaration + ; + +syntax Class + = simpleCharclass: "[" Range* ranges "]" + | complement: "!" Class charClass + > left difference: Class lhs "-" Class rhs + > left intersection: Class lhs "&&" Class rhs + > left union: Class lhs "||" Class rhs + | bracket \bracket: "(" Class charClass ")" + ; + +lexical RegExpLiteral = "/" RegExp* "/" RegExpModifier; + +syntax FunctionModifiers = \modifierlist: FunctionModifier* modifiers; + +syntax Comprehension + = @breakable{results,generators} \set: "{" {Expression ","}+ results "|" {Expression ","}+ generators "}" + | @breakable{from,to,generators} \map: "(" Expression from ":" Expression to "|" {Expression ","}+ generators ")" + | @breakable{results,generators} \list: "[" {Expression ","}+ results "|" {Expression ","}+ generators "]" + ; + +syntax Variant = nAryConstructor: Name name "(" {TypeArg ","}* arguments KeywordFormals keywordArguments ")"; + +syntax FunctionDeclaration + = abstract: Tags tags Visibility visibility Signature signature ";" + | @Foldable @breakable{expression} expression: Tags tags Visibility visibility Signature signature "=" Expression expression ";" + | @Foldable @breakable{expression,conditions} conditional: Tags tags Visibility visibility Signature signature "=" Expression expression "when" {Expression ","}+ conditions ";" + | @Foldable \default: Tags tags Visibility visibility Signature signature FunctionBody body + ; + +lexical PreProtocolChars = "|" URLChars "\<"; + +lexical NamedRegExp + = "\<" Name "\>" + | [\\] [/\<\>\\] + | NamedBackslash + | ![/\<\>\\] + ; + +syntax ProdModifier + = associativity: Assoc associativity + | \bracket: "bracket" + | \tag: Tag tag + ; + +syntax Toplevel = givenVisibility: Declaration declaration; + +lexical PostStringChars = @category="string" [\>] StringCharacter* [\"]; + +lexical HexIntegerLiteral = [0] [Xx] [0-9A-Fa-f]+ !>> [0-9A-Z_a-z]; + +syntax TypeVar + = free: "&" Name name + | bounded: "&" Name name "\<:" Type bound + ; + +syntax BasicType + = \value: "value" + | \loc: "loc" + | \node: "node" + | \num: "num" + | \type: "type" + | \bag: "bag" + | \int: "int" + | rational: "rat" + | relation: "rel" + | listRelation: "lrel" + | \real: "real" + | \tuple: "tuple" + | string: "str" + | \bool: "bool" + | \void: "void" + | dateTime: "datetime" + | \set: "set" + | \map: "map" + | \list: "list" + ; + +lexical Char + = @category="string" "\\" [\ \"\'\-\<\>\[\\\]bfnrt] + | @category="string" ![\ \"\'\-\<\>\[\\\]] + | @category="string" UnicodeEscape + ; + +syntax Prod + = reference: ":" Name referenced + | labeled: ProdModifier* modifiers Name name ":" Sym* syms + | unlabeled: ProdModifier* modifiers Sym* syms + | @Foldable associativityGroup: Assoc associativity "(" Prod group ")" + // | TODO add bracket rule for easy readability + > left \all: Prod lhs "|" Prod rhs + > left first: Prod lhs "\>" !>> "\>" Prod rhs + ; + +syntax DateTimeLiteral + = /*prefer()*/ dateLiteral: JustDate date + | /*prefer()*/ timeLiteral: JustTime time + | /*prefer()*/ dateAndTimeLiteral: DateAndTime dateAndTime + ; + +lexical PrePathChars = URLChars "\<"; + +syntax Mapping[&T] = \default: &T ! ifDefinedOtherwise from ":" &T to; + +lexical MidPathChars = "\>" URLChars "\<"; + +/* + Note that Pattern must closely follow the definitions of Expression because eventually + these two non-terminals will be fused just before AST generation. +*/syntax Pattern + = \set: "{" {Pattern ","}* elements0 "}" + | \list: "[" {Pattern ","}* elements0 "]" + | qualifiedName: QualifiedName qualifiedName + | multiVariable: QualifiedName qualifiedName "*" + | splice: "*" Pattern argument + | splicePlus: "+" Pattern argument + | negative: "-" Pattern argument + | literal: Literal literal + | \tuple: "\<" {Pattern ","}+ elements "\>" + | typedVariable: Type type Name name + | \map: "(" {Mapping[Pattern] ","}* mappings ")" + | reifiedType: "type" "(" Pattern symbol "," Pattern definitions ")" + | callOrTree: Pattern expression "(" {Pattern ","}* arguments KeywordArguments[Pattern] keywordArguments ")" + > variableBecomes: Name name ":" Pattern pattern + | asType: "[" Type type "]" Pattern argument + | descendant: "/" Pattern pattern + | anti: "!" Pattern pattern + | typedVariableBecomes: Type type Name name ":" Pattern pattern + ; + +syntax Tag + = @Folded @category="comment" \default: "@" Name name TagString contents + | @Folded @category="comment" empty: "@" Name name + | @Folded @category="comment" expression: "@" Name name "=" Expression expression !>> "@" + ; + +syntax ModuleActuals = \default: "[" {Type ","}+ types "]"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/syntax/tests/ConcreteSyntax.rsc| +module lang::rascal::\syntax::tests::ConcreteSyntax + +import Node; + +syntax A + = a: "a" + | b: "b" + | left two: A lhs A rhs + ; + +syntax S + = "s" + | "epsilon" () "." + | "opt" S? "." + | "seq2" (S S) "." + | "seq3" (S S S) "." + | "alt2" (S | T) "." + | "star" S* list "." + | "plus" S+ plist "." + | "starsep" {S T}* slist "." + | "plussep" {S T}+ pslist "." + ; + +syntax T = "t"; + +layout WS = [\t\n\ ]* !>> [\t\n\ ]; + +public A given_a = (A) `a` ; +public A given_b = (A) `b` ; +public A given_aa = (A) `a a` ; +public A given_ab = (A) `a b` ; + +public A twoA = (A) ` ` ; +public A threeA = (A) ` ` ; + +public test bool typeMatch() = A _ := given_a; +public +test bool twoMatch() + = (A) ` ` := twoA && a1 == given_a && a2 == given_a; +public +test bool twoMatch2() + = (A) ` ` := given_ab && a1 := given_a && a2 := given_b; +public +test bool threeMatch() + = (A) ` ` := threeA + && a1 := given_a + && a2 := given_a + && a3 := given_a; + +// testing regular expressions +public S s = (S) `s` ; +public T t = (T) `t` ; +public S eps = (S) `epsilon.` ; +public S opt1 = (S) `opt.` ; +public S opt2 = (S) `opt .` ; +public S seq2 = (S) `seq2 .` ; +public S seq3 = (S) `seq3 .` ; +public S alt2_1 = (S) `alt2 .` ; +public S alt2_2 = (S) `alt2 .` ; +public S star_none = (S) `star.` ; +public S star_one = (S) `star .` ; +public S star_two = (S) `star .` ; +public S plus_one = (S) `plus .` ; +public S plus_two = (S) `plus .` ; + +public S star_sep_none = (S) `starsep.` ; +public S star_sep__one = (S) `starsep .` ; +public S star_sep__two = (S) `starsep .` ; +public S plus_sep__one = (S) `plussep .` ; +public S plus_sep__two = (S) `plussep .` ; + +public test bool matcheps() = (S) `epsilon .` := eps; + +public test bool matchopt1() = (S) `opt .` := opt1; +public test bool matchopt2() = (S) `opt s.` := opt2; + +//public test bool matchopt2() = (S) `opt .` := opt1; +//public test bool matchopt2() = (S) `opt .` := opt2; +public test bool matchseq1() = (S) `seq2 s s.` := seq2; +public test bool matchseq2() = (S) `seq2 s .` := seq2; + +//public test bool matchseq3() = (S) `seq2 <(S S) _>.` := seq2; +public test bool matchstar() = (S) `star .` := star_none; +public test bool matchstar2() = (S) `star s.` := star_one; +public test bool matchstar3() = (S) `star .` := star_one && s1 == s; +public test bool matchstar4() = (S) `star .` := star_two; +public test bool matchstar5() = (S) `star .` := star_two; + +public +test bool splicestar1() + = (S*) x := star_two.\list && (S) `star s s s s.` := (S) `star .`; + +test bool noLocAnnos() { + a = (A) `a b`; + kwParams = {kw| /node t := a, kw <- getKeywordParameters(t)}; + return "loc" notin kwParams; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/syntax/tests/ExpressionGrammars.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module lang::rascal::\syntax::tests::ExpressionGrammars + +import ParseTree; + +lexical Id = [a-z] !<< [a-z]+ !>> [a-z]; +lexical Num = [0-9]+; +layout W = [\ \t\n\r]*; + +syntax Exp + = id: Id + | number: Num + | lst: "[" {Exp ! com ","}* "]" + | ind: Exp ! clop ! clos "[" {Exp ! com ","}+ "]" + > clop: Exp "+" + | clos: Exp "*" + > left mul: Exp "*" Exp + > left ( add: Exp "+" Exp | sub: Exp "-" Exp ) + > bracket "(" Exp exp ")" + | com: Exp "," Exp + ; + +syntax Exp = left ( add: Exp "+" Exp | minmin: Exp "--" Exp ); + +// "modular" extensions in the priority relation +syntax Exp + = :mul + > left Exp "/" Exp + > :add + ; +syntax Exp + = :add + > Exp "." Exp + ; + +syntax F + = left "-" F + | "f" + | right F "+" + ; +syntax G + = left ( G "+" G | "-" G | "^" G ) + | "g" + | right ( G "-" G | G "+" ) + ; + +syntax Opt = Exp ! com? "?"; +syntax Seq = (Exp ! com Exp ! number); +syntax Alt = (Exp ! com | (Exp "," Exp)); + +Exp removeBrackets(Exp e) = visit(e) { + case (Exp) `()` => b + }; + +test bool ext1() = (Exp) `e+e.e` := removeBrackets((Exp) `(e+e).e`); +test bool ext2() = (Exp) `e*e/e+e` := removeBrackets((Exp) `((e*e)/e)+e`); +test bool ext3() = (Exp) `e*e--e` := removeBrackets((Exp) `(e*e)--e`); +test bool ext4() = (Exp) `e*e.e` := removeBrackets((Exp) `(e*e).e`); +test bool ext5() = (Exp) `e+e.e` := removeBrackets((Exp) `(e+e).e`); +test bool ext6() = (Exp) `e--e-e` := removeBrackets((Exp) `(e--e)-e`); +test bool ext7() = (Exp) `e-e--e` := removeBrackets((Exp) `(e-e)--e`); + +test bool safeLeft() = F _ := parse(#F, "--f"); +test bool safeRight() = F _ := parse(#F, "f++"); +test bool safeGroupLeft() = G _ := parse(#G, "--g"); +test bool safeGroupRight() = G _ := parse(#G, "g++"); + +Exp noBrackets(Exp e) = visit(e) { + case (Exp) `()` => a + }; +bool hasAmb(Tree x) = /amb(_) := x; + +test bool \assoc() = x := noBrackets(parse(#Exp, "(a + b) + c")) + when x := parse(#Exp, "a + b + c"); + +test bool \mutualAssoc1() = x := noBrackets(parse(#Exp, "(a - b) + c")) + when x := parse(#Exp, "a - b + c"); + +test bool \mutualAssoc2() = x := noBrackets(parse(#Exp, "(a + b) - c")) + when x := parse(#Exp, "a + b - c"); + +test bool \prio() = x := noBrackets(parse(#Exp, "(a*b)+c")) + when x := parse(#Exp, "a*b+c"); + +test bool \safePrio1() = parse(#Exp, "a[a+b]") is ind; + +test bool \safePrio2() = parse(#Exp, "a**b") is mul; + +test bool \safePrio3() = parse(#Exp, "a*b*") is mul; + +test bool \safePrio4() = parse(#Exp, "(a*)[b]") is ind; + +test bool \transPrio() = x := noBrackets(parse(#Exp, "a,(b*c)")) + when x := parse(#Exp, "a,b*c"); + +test bool \exceptNormal() = parse(#Exp, "a+[a]") is add; + +test bool \exceptInList() = !hasAmb(parse(#Exp, "[a,a]")); + +test bool \exceptInOpt() { + try { + parse(#Opt, "a,a?"); + return false; + } + catch value _: { + return true; + } +} + +test bool \exceptInSeq1() { + try { + parse(#Seq, "a,a a"); + return false; + } + catch value _: { + return true; + } +} + +test bool \exceptInSeq2() { + try { + parse(#Seq, "a+a 1"); + return false; + } + catch value _: { + return true; + } +} + +test bool \exceptInAlt() = !hasAmb(parse(#Alt, "a,a")); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/syntax/tests/ImplodeTestGrammar.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Tests the potential clashes among value constructors of different adts, plus, the identified clash with: bool eq(value, value);} +module lang::rascal::\syntax::tests::ImplodeTestGrammar + +import ParseTree; + +lexical Id = [a-z] !<< [a-z]+ !>> [a-z]; +layout W = [\ \t\n\r]*; + +lexical Num = \int: [0-9]+; +syntax Exp + = id: Id name + | number: Num n + | non-assoc eq: Exp e1 "==" Exp e2 + ; + +lexical Number = \int: [0-9]+; +syntax Expr + = id: Id name + | number: Number n + | non-assoc eq: Expr e1 "==" Expr e2 + ; + +public Exp parseExp(str s) = parse(#Exp, s); +public Exp expLit1() = (Exp) `a == b`; +public Exp expLit2() = (Exp) `a == 11`; + +public Expr parseExpr(str s) = parse(#Expr, s); +public Expr exprLit1() = (Expr) `a == b`; +public Expr exprLit2() = (Expr) `a == 11`; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/syntax/tests/ImplodeTests.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Tests the potential clashes among value constructors of different adts, plus, the identified clash with: bool eq(value, value);} +module lang::rascal::\syntax::tests::ImplodeTests + +import lang::rascal::\syntax::tests::ImplodeTestGrammar; +import ParseTree; +import Exception; + +public data Num(loc src = |unknown:///|, map[int, list[str]] comments = ()); +public data Exp (loc src = |unknown:///|, map[int, list[str]] comments = ()) + = id(str name); +public Exp number(Num::\int("0")) = Exp::number(Num::\int("01")); + +public data Number(loc src = |unknown:///|, map[int, list[str]] comments = ()); +public data Expr (loc src = |unknown:///|, map[int, list[str]] comments = ()) + = id(str name); +public Expr number(Number::\int("0")) = Expr::number(Number::\int("02")); + +public Exp implodeExp(str s) = implode(#Exp, parseExp(s)); +public Exp implodeExpLit1() = implode(#Exp, expLit1()); +public Exp implodeExpLit2() = implode(#Exp, expLit2()); + +public Expr implodeExpr(str s) = implode(#Expr, parseExp(s)); +public Expr implodeExprLit1() = implode(#Expr, exprLit1()); +public Expr implodeExprLit2() = implode(#Expr, exprLit2()); + +// ---- test1 ---- +test bool test11() { + try + return Exp::id(_) := implodeExp("a"); + catch ImplodeError(_): + return false; +} + +test bool test12() { + try + return Exp::number(Num::\int("0")) := implodeExp("0"); + catch ImplodeError(_): + return false; +} + +test bool test13() { + try + return Exp::eq(Exp::id(_), Exp::id(_)) := implodeExp("a == b"); + catch ImplodeError(_): + return false; +} + +test bool test14() { + try + return + Exp::eq(Exp::number(Num::\int("0")), Exp::number(Num::\int("1"))) + := implodeExp("0 == 1"); + catch ImplodeError(_): + return false; +} + +test bool test15() { + try + return Expr::id(_) := implodeExpr("a"); + catch ImplodeError(_): + return false; +} + +test bool test16() { + try + return Expr::number(Number::\int("0")) := implodeExpr("0"); + catch ImplodeError(_): + return false; +} + +test bool test17() { + try + return Expr::eq(Expr::id(_), Expr::id(_)) := implodeExpr("a == b"); + catch ImplodeError(_): + return false; +} + +test bool test18() { + try + return + Expr::eq( + Expr::number(Number::\int("0")), Expr::number(Number::\int("1")) + ) := implodeExpr("0 == 1"); + catch ImplodeError(_): + return false; +} + +// ---- test2 ---- +test bool test21() { + try + return Exp::eq(Exp::id("a"), Exp::id("b")) := implodeExpLit1(); + catch ImplodeError(_): + return false; +} + +test bool test22() { + try + return + Exp::eq(Exp::id("a"), Exp::number(Num::\int("11"))) := implodeExpLit2(); + catch ImplodeError(_): + return false; +} + +test bool test23() { + try + return Expr::eq(Expr::id("a"), Expr::id("b")) := implodeExprLit1(); + catch ImplodeError(_): + return false; +} + +test bool test24() { + try + return + Expr::eq(Expr::id("a"), Expr::number(Number::\int("11"))) := + implodeExprLit2(); + catch ImplodeError(_): + return false; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/syntax/tests/KnownIssues.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{This module documents known issues in Rascal that still need solving. +Perhaps they are hard to solve, but one day the inversion of these tests will +end up in SolvedIssues} +@bootstrapParser +module lang::rascal::\syntax::tests::KnownIssues + +import lang::rascal::\syntax::Rascal; +import ParseTree; + +public bool isAmb(Tree t) = /amb(_) := t; + +public +test bool literalAmb() + = isAmb(parse(#Command, "\"a\"+ b;", allowAmbiguity = true)); + +public test bool basicAmb() = amb(_) := amb({}); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/syntax/tests/ParsingRegressionTests.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module lang::rascal::\syntax::tests::ParsingRegressionTests + +import util::Reflective; +import IO; +import util::FileSystem; +import ParseTree; +import analysis::grammars::Ambiguity; + +public bool hasAmb(Tree x) = /amb(_) := x; + +public bool testModules(list[loc] files, list[loc] _) { + errors = []; + for (f <- files) { + try { + t = parseModule(f); + if (hasAmb(t)) { + println("Ambiguity found while parsing: "); + iprintln(diagnose(t)); + errors += []; + } + } + catch value x: { + errors += []; + } + } + + if (errors != []) { + for ( <- errors) + println("failed : "); + return false; + } + return true; +} + +public +test bool StandardLibrary() + = testModules ( + [f + | /file(f) <- crawl(|std:///|), f.extension == "rsc", /experiments/ !:= f.path], + [] + ); + +public +test bool testTutor() + = testModules ( + [f | /file(f) <- crawl(|tutor:///|), f.extension == "rsc"], + [|tutor:///|] + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/syntax/tests/PreBootstrap.rsc| +@bootstrapParser +module lang::rascal::\syntax::tests::PreBootstrap + +import lang::rascal::\syntax::Rascal; + +public test bool expr() = Expression _ := (Expression) `1`; +public +test bool lit() + = (Sym) `` := (Sym) `'hello'`; +public test bool nont() = (Sym) `` := (Sym) `X`; +public +test bool paramnont() + = (Sym) `[<{Sym ","}+ _>]` := (Sym) `List[String,String]`; +public test bool string() = (Sym) `` := (Sym) `"hello"`; +public +test bool match() + = (Expression) `` := (Expression) `1`; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/syntax/tests/SolvedIssues.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@bootstrapParser +@synopsis{In this test module we collect test cases that are associated with bugs from the past. +This is just to make sure the bugs are not re-introduced accidentally.} +module lang::rascal::\syntax::tests::SolvedIssues + +import lang::rascal::\syntax::Rascal; +import ParseTree; +import Exception; + +public bool notAmb(Tree t) = /amb(_) !:= t; + +public test bool amb1() = notAmb(parse(#Expression, "1 + -1")); + +public test bool amb2() = notAmb(parse(#Command, "\"1\" + 2")); + +public test bool amb3() = notAmb(parse(#Command, "true ? [1]")); + +public test bool capitalA() { + try { + parse(#LocationLiteral, "|project://A|"); + return true; + } + catch ParseError(loc _): + return false; +} + +public test bool keywordSuffix1() { + try { + parse(#Command, "syntax Aall = \"a\";"); + return true; + } + catch ParseError(loc _): + return false; +} + +public test bool keywordSuffix2() { + try { + parse(#Command, "syntax Bany = \"a\";"); + return true; + } + catch ParseError(loc _): + return false; +} + +public test bool keywordSuffix3() { + try { + parse(#Command, "syntax Canno = \"a\";"); + return true; + } + catch ParseError(loc _): + return false; +} + +public test bool keywordSuffix4() { + try { + parse(#Command, "syntax Ddata = \"a\";"); + return true; + } + catch ParseError(loc _): + return false; +} + +public test bool keywordSuffix5() { + try { + parse(#Command, "syntax Falias = \"a\";"); + return true; + } + catch ParseError(loc _): + return false; +} + +public test bool keywordSuffix6() { + try { + parse(#Command, "syntax Gmodule = \"a\";"); + return true; + } + catch ParseError(loc _): + return false; +} + +public test bool keywordSuffix7() { + try { + parse(#Command, "syntax Htag = \"a\";"); + return true; + } + catch ParseError(loc _): + return false; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Booleans.rsc| +@license{ + Copyright (c) 2009-2020 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module lang::rascal::tests::basic::Booleans + +import Boolean; +import Exception; + +// Operators +test bool sanity() = true != false; + +test bool or(bool b) { + if (true || b == true, b || true == true, false || false == false) + return true; + else + return false; +} + +test bool and(bool b) { + if ((false && b) == false, (b && false) == false, (true && true) == true) + return true; + else + return false; +} + +test bool not1(bool b) = !!b == b; + +test bool not2() = (!true == false) && (!false == true); + +test bool equiv(bool b1, bool b2) = (b1 <==> b2) <==> (!b1 && !b2 || b1 && b2); + +test bool impl(bool b1, bool b2) = (b1 ==> b2) <==> !(b1 && !b2); + +test bool assignAnd(bool b1, bool b2) { + int n = b1 && b2 ? 10 : 20; + if (b1 && b2) + return n == 10; + else + return n == 20; +} + +test bool assignOr(bool b1, bool b2) { + int n = b1 || b2 ? 10 : 20; + if (b1 || b2) + return n == 10; + else + return n == 20; +} + +// Library functions +test bool tstArbBool() { + b = arbBool(); + return b == true || b == false; +} + +test bool fromString1() + = fromString("true") == true && fromString("false") == false; + +@expected{IllegalArgument} +test bool fromString2(str s) = fromString(s); + +// will fail in the rare situation that "true" or "false" are passed as argument. +test bool tstToInt() = toInt(false) == 0 && toInt(true) == 1; + +test bool tstToReal() = toReal(false) == 0.0 && toInt(true) == 1.0; + +test bool tstToString() + = toString(false) == "false" && toString(true) == "true"; + +@ignoreCompiler{Undetermined} +test bool shortCircuiting() { + try { + return false ==> (1 / 0 == 0) && true || (1 / 0 == 0) && !(false && (1 / 0 == 0)); + } + catch ArithmeticException(str _): { + return false; + } +} + +test bool matchAnonymous() { + b = [_] := [1]; + return b; +} + +test bool matchAnonymousReturn() { + return [_] := [1]; +} + +test bool matchTypedAnonymous() { + b = [int _] := [1]; + return b; +} + +test bool matchTypedAnonymousReturn() { + return [int _] := [1]; +} + +test bool matchVar() { + b = [_] := [1]; + return b; +} + +test bool matchVarReturn() { + return [_] := [1]; +} + +test bool matchTypedVar() { + b = [int _] := [1]; + return b; +} + +test bool matchTypedVarReturn() { + return [int _] := [1]; +} + +test bool matchAnonymousListVar1() { + b = [*_] := [1]; + return b; +} + +test bool matchAnonymousListVar1Return() { + return [*_] := [1]; +} + +test bool matchAnonymousListVar2() { + b = [*_] := [1]; + return b; +} + +test bool matchAnonymousListVar2Return() { + return [*_] := [1]; +} + +test bool matchTypedAnonymousListVar() { + b = [*int _] := [1]; + return b; +} + +test bool matchTypedAnonymousListVarReturn() { + return [*int _] := [1]; +} + +test bool matchListVar1() { + b = [*_] := [1]; + return b; +} + +test bool matchListVar1Return() { + return [*_] := [1]; +} + +test bool matchListVar2() { + b = [*_] := [1]; + return b; +} + +test bool matchListVar2Return() { + return [*_] := [1]; +} + +test bool matchTypedListVar() { + b = [*int _] := [1]; + return b; +} + +test bool matchTypedListVarReturn() { + return [*int _] := [1]; +} + +test bool matchTypedVarAndTrue() { + ten = 10; + b = [int _] := [1] && ten > 9; + return b; +} + +test bool matchTypedVarAndFalse() { + ten = 10; + b = [int _] := [1] && 9 > ten; + return !b; +} + +test bool matchTypedListVarAnd() { + ten = 10; + b = [*int _] := [1] && ten > 9; + return b; +} + +test bool matchTypedListVarAndFalse() { + ten = 10; + b = [*int _] := [1] && 9 > ten; + return !b; +} + +test bool compositeAnd() { + ten = 10; + b = [*int _, int _, *int _] := [1, 2, 3] && ten > 9; + return b; +} + +test bool compositeAndFalse() { + ten = 10; + b = [*int _, int _, *int _] := [1, 2, 3] && 9 > ten; + return !b; +} + +test bool compositeAndCnt() { + n = 0; + ten = 10; + if ([*int _, int _, *int _] := [1, 2, 3] && ten > 9) { + n = n + 1; + fail; + } + return n == 3; +} + +test bool compositeAndCommaCnt() { + n = 0; + ten = 10; + if ([*int _, int _, *int _] := [1, 2, 3], ten > 9) { + n = n + 1; + fail; + } + return n == 3; +} + +test bool compositeAndBTLast() { + ten = 10; + return ten > 9 && [*int _, int _, *int _] := [1, 2, 3]; +} + +test bool compositeAndCntBTLast() { + n = 0; + ten = 10; + if (ten > 9 && [*int _, int _, *int _] := [1, 2, 3]) { + n = n + 1; + fail; + } + return n == 3; +} + +test bool compositeAndBothBT() { + return [*int _, int _, *int _] := [1, 2, 3] && [*int _, int _, *int _] := [4, 5, 6]; +} + +test bool compositeAndBothBTCnt() { + n = 0; + if ([*int _, int _, *int _] := [1, 2, 3] && [*int _, int _, *int _] := [4, 5, 6]) { + n = n + 1; + fail; + } + return n == 9; +} + +@ignoreCompiler{Undetermined cause} +test bool compositeOrCntBTLast() { + n = 0; + if (int _ := 3 || ([*int _, *int _] := [4, 5, 6])) { + n = n + 1; + fail; + } + return n == 5; +} + +test bool compositeOrCntBTFirst() { + n = 0; + if ([*int _, *int _] := [4, 5, 6] || int _ := 3) { + n = n + 1; + fail; + } + return n == 5; +} + +test bool compositeAndOrCnt() { + n = 0; + if (([*int _, *int _] := [1, 2, 3] && int _ := 3) || ([*int _, *int _] := [4, 5, 6] && int _ := 4)) + { + n = n + 1; + fail; + } + return n == 8; +} + +// implies ==> +@ignoreCompiler{Backtracking in arguments of ==>} +test bool compositeImplTrue() { + ten = 10; + b = [*int _, int _, *int _] := [1, 2, 3] ==> ten > 9; + return b; +} + +@ignoreCompiler{Backtracking in arguments of ==>} +test bool compositeImplFalse() { + ten = 10; + b = [*int _, int _, *int _] := [1, 2, 3] ==> 9 > ten; + return !b; +} + +@ignoreCompiler{Backtracking in arguments of ==>} +test bool compositeImplBTLast1() { + ten = 10; + b = 9 > ten ==> [*int _, int _, *int _] := [1, 2, 3]; + return b; +} + +@ignoreCompiler{Backtracking in arguments of ==>} +test bool compositeImplBTLast2() { + ten = 10; + b = ten > 9 ==> [*int _, int _, *int _] := [1, 2, 3]; + return b; +} + +@ignoreCompiler{Backtracking in arguments of ==>} +test bool compositeImplVarBothBT() { + ten = 10; + b = [*int _, int _, *int _] := [1, 2, 3] ==> ([*int _, *int _] := [4, 5, 6] && int _ := 4); + return b; +} + +@ignoreCompiler{Backtracking in arguments of ==>} +test bool compositeImplCnt() { + n = 0; + ten = 10; + if ([*int _, int _, *int _] := [1, 2, 3] ==> ten > 9) { + n = n + 1; + fail; + } + return n == 1; +} + +@ignoreCompiler{Backtracking in arguments of ==>} +test bool compositeImplCntBTLast1() { + ten = 10; + return ten > 9 ==> [*int _, int _, *int _] := [1, 2, 3]; +} + +@ignoreCompiler{Backtracking in arguments of ==>} +test bool compositeImplCntBTLast2() { + n = 0; + ten = 10; + if (ten > 9 ==> [*int _, int _, *int _] := [1, 2, 3]) { + n = n + 1; + fail; + } + return n == 3; +} + +@ignoreCompiler{Backtracking in arguments of ==>} +test bool compositeImplReturnBothBT() { + return [*int _, int _, *int _] := [1, 2, 3] ==> [*int _, int _, *int _] := [4, 5, 6]; +} + +@ignoreCompiler{Backtracking in arguments of ==>} +test bool compositeImplBothBTCnt() { + n = 0; + if ([*int _, int _, *int _] := [1, 2, 3] ==> [*int _, int _, *int _] := [4, 5, 6]) { + n = n + 1; + fail; + } + return n == 3; +} + +// equivalent <==> +@ignoreCompiler{Backtracking in arguments of <==>} +test bool compositeEquivTrue() { + ten = 10; + b = [*int _, int _, *int _] := [1, 2, 3] <==> ten > 9; + return b; +} + +@ignoreCompiler{Backtracking in arguments of <==>} +test bool compositeEquivFalse() { + ten = 10; + b = [*int _, int _, *int _] := [1, 2, 3] <==> 9 > ten; + return !b; +} + +@ignoreCompiler{Backtracking in arguments of <==>} +test bool compositeEquivBTLast1() { + ten = 10; + b = 9 > ten <==> [*int _, int _, *int _] := [1, 2, 3]; + return !b; +} + +@ignoreCompiler{Backtracking in arguments of <==>} +test bool compositeEquivBTLast2() { + ten = 10; + b = ten > 9 <==> [*int _, int _, *int _] := [1, 2, 3]; + return b; +} + +@ignoreCompiler{Backtracking in arguments of <==>} +test bool compositeEquivVarBothBT() { + ten = 10; + b = [*int _, int _, *int _] := [1, 2, 3] <==> ([*int _, *int _] := [4, 5, 6] && int _ := 4); + return b; +} + +@ignoreCompiler{Backtracking in arguments of <==>} +test bool compositeEquivCnt() { + n = 0; + ten = 10; + if ([*int _, int _, *int _] := [1, 2, 3] <==> ten > 9) { + n = n + 1; + fail; + } + return n == 3; +} + +@ignoreCompiler{Backtracking in arguments of <==>} +test bool compositeEquivCntBTLast1() { + ten = 10; + return ten > 9 <==> [*int _, int _, *int _] := [1, 2, 3]; +} + +@ignoreCompiler{Backtracking in arguments of <==>} +test bool compositeEquivCntBTLast2() { + n = 0; + ten = 10; + if (ten > 9 <==> [*int _, int _, *int _] := [1, 2, 3]) { + n = n + 1; + fail; + } + return n == 3; +} + +@ignoreCompiler{Backtracking in arguments of <==>} +test bool compositeEquivReturnBothBT() { + return [*int _, int _, *int _] := [1, 2, 3] <==> [*int _, int _, *int _] := [4, 5, 6]; +} + +@ignoreCompiler{Backtracking in arguments of <==>} +test bool compositeEquivBothBTCnt() { + n = 0; + if ([*int _, int _, *int _] := [1, 2, 3] <==> [*int _, int _, *int _] := [4, 5, 6]) { + n = n + 1; + fail; + } + return n == 3; +} + +//// +data AnotherAndData = a(); + +anno + list[int] + AnotherAndData + @ + l +; + +test bool anotherAnd() { + v = a()[@l = [1, 2, 3]]; + list[list[int]] res = []; + if (v@l? && [*int x, *int y] := v@l) { + res = res + [x, y]; + fail; + } + return res == [[], [1, 2, 3], [1], [2, 3], [1, 2], [3], [1, 2, 3], []]; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/CompilerIssues/LIstMatchInOR.rsc| +module lang::rascal::tests::basic::CompilerIssues::LIstMatchInOR + +import IO; + +data Symbol + = strt() + | par(list[Symbol] parameters) + // <6>> + ; + +//@ignoreCompiler{Generates incorrect code} +//test bool listMatchInOR() { +// bool unquotable(Symbol x) +// = par([*Symbol _]) := x +// || strt() := x +// ; +// return unquotable(\strt()); +//} +data Symbol + = sym(str name) + | label(str name, Symbol sym) + ; + +// Simplified version from lang::rascal::grammar::analyze::Dependency +set[Symbol] symbolDependenciesOld(set[Symbol] sses) + = {from| s <- sses, (label(_, Symbol from) := s || Symbol from := s)}; + +// Test for original version (with probably unintended result) +@ignoreCompiler{Generates incorrect code} +test bool symbolDependenciesOld1() + = symbolDependenciesOld({sym("a"), label("x", sym("b"))}) == {sym("a"), sym("b"), label("x", sym("b"))}; + +// Rewritten version with intended output, compiler behaves well on it +set[Symbol] symbolDependenciesNew(set[Symbol] sses) + = {from| s <- sses, Symbol from := ((label(_, Symbol f) := s) ? f : s)}; + +test bool symbolDependenciesNew1() + = symbolDependenciesNew({sym("a"), label("x", sym("b"))}) == {sym("a"), sym("b")}; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/CompilerIssues/ListMatchInIfCondition.rsc| +module lang::rascal::tests::basic::CompilerIssues::ListMatchInIfCondition + +int f(list[int] ds) { + if ([int xxx] := ds, xxx > 0) { + return 1; + } + else { + return 2; + } +} + +test bool ListMatchInIfCondition1() = f([1]) == 1; + +test bool ListMatchInIfCondition2() = f([-1]) == 2; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/CompilerIssues/NoMatchInWhen.rsc| +module lang::rascal::tests::basic::CompilerIssues::NoMatchInWhen + +data AType + = aparameter(str name, AType bound) + | a() + ; +bool asubtype(AType x, x) = true; + +bool asubtype(aparameter(str pname1, AType bound1), AType r) = true + when aparameter(pname1, _) !:= r; + +@ignoreCompiler{Control flow for !:= is not handled properly} +test bool aparam1() = asubtype(aparameter("A", a()), a()); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/CompilerIssues/QualifiedName.rsc| +module lang::rascal::tests::basic::CompilerIssues::QualifiedName + +data G + = g(int n) + | g1() + ; + +@ignore{Defeats the compiler's strategy in lang::rascalcore::compile::Rascal2muRascal::RascalPattern for qualified name pattern} +test bool nestedfunctionInTrueBranch() { + int f(int x) = 2 * x; + return [g(n) := g1() ? f(n) : -1] == [-1]; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/CompilerIssues/TemplateInConditional.rsc| +module lang::rascal::tests::basic::CompilerIssues::TemplateInConditional + +data D + = d1(int nfield) + | d2(str sfield) + ; + +@ignoreCompiler{x.nfield if moved before loop when x may not satisfy x is d1} +test bool templateInConditional() + = {x is d1 ? "" : "x"| x <- [d2("abc")]} == {"x"}; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/CompilerIssues/TypedQualifiedName.rsc| +module lang::rascal::tests::basic::CompilerIssues::TypedQualifiedName + +data G + = g(int n) + | g1() + ; + +@ignoreCompiler{Defeats the strategy in lang::rascalcore::compile::Rascal2muRascal::RascalPattern fortyped qualified name pattern} +test bool nestedfunctionInTrueBranch() { + int f(int x) = 2 * x; + return [g(int n) := g1() ? f(n) : -1] == [-1]; +} + +data D + = d(int n) + | d() + ; + +@ignoreCompiler{Defeats the strategy in lang::rascalcore::compile::Rascal2muRascal::RascalPattern for typed qualified name pattern} +test bool nestedfunctionInTrueBranch2() { + int f(int n) = n; + for (((d(int t) := d()) ? f(t) : 4) == 4) { + return true; + } + return false; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/CompilerIssues/VariableBoundInConditionalInComprehension.rsc| +module lang::rascal::tests::basic::CompilerIssues::VariableBoundInConditionalInComprehension + +data AType + = avalue() + | aparameter(str pname, AType bound) + ; + +// The original code from CollectDeclaration: +// The issue is that "typeVarBounds[tvname2]" is moved before the comprehension (because the variable "tvname2" has to be declared) +// and then gives IndexOutOfBounds error +map[str, AType] propagateParams(map[str, AType] typeVarBounds) { + return + (tvname: + (aparameter(tvname2, _) := typeVarBounds[tvname]) + ? typeVarBounds[tvname2] + : typeVarBounds[tvname] + | tvname <- typeVarBounds + ); +} + +// The following equivalent code is compiled correcly: +//map[str, AType] propagateParams(map[str,AType] typeVarBounds){ +// AType find(str tvname) = (aparameter(tvname2, _) := typeVarBounds[tvname]) ? typeVarBounds[tvname2] : typeVarBounds[tvname] ; +// return (tvname : find(tvname) | tvname <- typeVarBounds); +//} +@ignoreCompiler{Compiled code crahes with IndexOutOfBounds} +test bool variableBoundInConditionalInComprehension() + = propagateParams(("T" : avalue())) == ("T" : avalue()); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/CompilerIssues/VariableInOr.rsc| +module lang::rascal::tests::basic::CompilerIssues::VariableInOr + +data D + = d1(int n) + | label(D d) + ; + +@ignoreCompiler{Generates erroneous Java code} +int f(D d) = e.n + when label(D e) := d || e := d; + +@ignoreCompiler{Generates erroneous Java code} +test bool testF() = f(label(d1(10))) == 10; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/DynamicDispatch.rsc| +module lang::rascal::tests::basic::DynamicDispatch + +data D = d(); + +bool foo(d()) { + return true; + ; +} + +default bool foo(value n) { + return false; +} + +test bool dispatchOnRuntimeType() { + value x = d(); + return foo(x) == true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Equality.rsc| +module lang::rascal::tests::basic::Equality + +import util::Math; +import Set; +import Map; +import Type; +import Node; + +// the only way two values can be equal while their run-time types are not is due to conversion between int, real, rat by `==` +test bool canonicalTypes(&T x, &Y y) + = x == y + ==> (typeOf(x) == typeOf(y)) + || size({typeOf(x), typeOf(y)} & {\int(), \real(), \rat()}) > 1; + +test bool canonicalTypesRegression1() = canonicalTypes(0.0, 0); +test bool canonicalTypesRegression2() = canonicalTypes(0r, 0); +test bool canonicalTypesRegression3() = canonicalTypes(0r, 0.0); + +// values have an equivalence relation +test bool reflexEq1(value x) = x == x; +test bool transEq1(value x, value y, value z) = (x == y && y == z) ==> (x == z); +test bool commutativeEq1(value x, value y) = (x == y) <==> (y == x); + +// the matching operator is also an equivalence relation on values: +test bool reflexEq2(value x) = x := x; +test bool transEq2(value x, value y, value z) = (x := y && y := z) ==> (x := z); +test bool commutativeEq2(value x, value y) = (x := y) <==> (y := x); + +// equality subsumes matching, but we focus on nodes to avoid problems with num coercions of `==`: +test bool allEqualValuesMatch(node a, node b) = a == b ==> a := b; +test bool noMatchImpliesUnequal(node a, node b) = !(a := b) ==> a != b; + +test bool matchIsEqualityModuloKeywordFields(node x, node y) + = (unsetRec(x) == unsetRec(y)) <==> x := y; + +// values have an equivalence relation, and by requiring the arguments to have the same types we may trigger bugs sooner: +test bool transEqSame(&Same x, &Same y, &Same z) = (x == y && y == z) ==> (x == z); +test bool commutativeEqSame(&Same x, &Same y) = (x == y) <==> (y == x); + +// values are partially ordered +test bool reflexLTE(value x) = (x <= x); +test bool antiSymmetricLTE(value x, value y) = (x <= y && y <= x) ==> (x == y); +test bool transLTE(value x, value y, value z) = (x <= y && y <= z) ==> x <= z; + +// values are partially ordered, and by requiring the arguments to have the same type we may trigger bugs sooner: +@Ignore +test bool antiSymmetricLTESame(&Same <: node x, &Same <: node y) + = (x <= y && y <= x) ==> (x == y); +test bool transLTESame(&Same <: node x, &Same <: node y, &Same <: node z) + = (x <= y && y <= z) ==> x <= z; + +@Ignore +test bool antiSymmetricLTEWithKeywordParamsLt1() + = antiSymmetricLTESame(""(), ""(x = 3)); +@Ignore +test bool antiSymmetricLTEWithKeywordParamsLt2() + = antiSymmetricLTESame(""(x = 2), ""(x = 3)); +@Ignore +test bool antiSymmetricLTEWithKeywordParamsEq() + = antiSymmetricLTESame(""(x = 3), ""(x = 3)); + +// numbers are totally ordered +test bool numTotalLTE1(num x, num y) = x <= y || y <= x; +test bool numAntiSymmetricLTE(num x, num y) = (x <= y && y <= x) ==> (x == y); +test bool numTransLTE(num x, num y, num z) = (x <= y && y <= z) ==> (x <= z); +test bool numValueReflex(num x) { + value y = x; + return x == y && y == x; +} + +// ints are totally ordered +test bool intTotalLTE(int x, int y) = x <= y || y <= x; +test bool intAntiSymmetricLTE(int x, int y) = (x <= y && y <= x) ==> (x == y); +test bool intTransLTE(int x, int y, int z) = (x <= y && y <= z) ==> (x <= z); +test bool intValueReflex(int x) { + value y = x; + return x == y && y == x; +} + +// reals are totally ordered +test bool realTotalLTE(real x, real y) = x <= y || y <= x; +test bool realAntiSymmetricLTE(real x, real y) = (x <= y && y <= x) ==> (x == y); +test bool realTransLTE(real x, real y, real z) = (x <= y && y <= z) ==> (x <= z); +test bool realValueReflex(real x) { + value y = x; + return x == y && y == x; +} + +// rat are totally ordered +test bool ratTotalLTE(rat x, rat y) = x <= y || y <= x; +test bool ratAntiSymmetricLTE(rat x, rat y) = (x <= y && y <= x) ==> (x == y); +test bool ratTransLTE(rat x, rat y, rat z) = (x <= y && y <= z) ==> (x <= z); +test bool ratValueReflex(rat x) { + value y = x; + return x == y && y == x; +} + +// strings are totally ordered +test bool numTotalLTE2(str x, str y) = x <= y || y <= x; +test bool strAntiSymmetricLTE(str x, str y) = (x <= y && y <= x) ==> (x == y); +test bool strTransLTE(str x, str y, str z) = (x <= y && y <= z) ==> x <= z; +test bool strValueReflex(rat x) { + value y = x; + return x == y && y == x; +} + +// lists are partially ordered +test bool listReflexLTE(list[value] x) = (x <= x); +test bool listAntiSymmetricLTE(list[value] x, list[value] y) + = (x <= y && y <= x) ==> (x == y); +test bool listTransLTE(list[value] x, list[value] y, list[value] z) + = (x <= y && y <= z) ==> x <= z; + +// sets are ordered via sub-set relation +test bool subsetOrdering1(set[value] x, set[value] y) = x <= x + y; +test bool subsetOrdering2(set[value] x, set[value] y) + = (x <= y) <==> (x == {} || all(e <- x, e in y)); + +// sets are partially ordered +test bool setReflexLTE1(set[value] x) = (x <= x); +test bool setAntiSymmetricLTE1(set[value] x, set[value] y) + = (x <= y && y <= x) ==> (x == y); +test bool setTransLTE1(set[value] x, set[value] y, set[value] z) + = (x <= y && y <= z) ==> x <= z; + +// map are ordered via sub-map relation +/*TODO: + +java.lang.Exception: Test submapOrdering1 failed due to + io.usethesource.vallang.type.IntegerType cannot be cast to io.usethesource.vallang.util.TrieMap$CompactMapNode + +Actual parameters: + map[value, value] =>(true:("":[])) + map[value, value] =>("":0.5741726876359169,true:-405555075,639525932r165438573:233378841r1234953134,"M"(true,|tmp:///|):true,|tmp:///|:|tmp:///g7/J|) + +*/test bool submapOrdering1(map[value, value] x, map[value, value] y) = x <= y + x; + +// remember map join is not commutative +/*TODO: +java.lang.Exception: failed for arguments: (true:"",-1185257414:"1sn"({""()},"冖񓱍资"(|tmp:///|),-304421973r46873778,["R7jZ"()])) + (true:"",$3632-03-24T14:03:39.476+01:00$:["0Xo","",""],|tmp:///|:$2015-08-06T08:23:51.810+01:00$,|tmp:///R66k|:<"h7"()>) +*/test bool submapOrdering2(map[value, value] x, map[value, value] y) + = (x <= y) <==> (x == () || all(e <- x, e in y, eq(y[e], x[e]))); + +// maps are partially ordered +test bool setReflexLTE2(map[value, value] x) = (x <= x); +test bool setAntiSymmetricLTE2(map[value, value] x, map[value, value] y) + = (x <= y && y <= x) ==> (x == y); +test bool setTransLTE2( + map[value, value] x, map[value, value] y, map[value, value] z +) + = (x <= y && y <= z) ==> x <= z; + +// locs are partially ordered +test bool locReflexLTE(loc x) = (x <= x); +test bool locAntiSymmetricLTE(loc x, loc y) = (x <= y && y <= x) ==> (x == y); +test bool locTransLTE(loc x, loc y, loc z) = (x <= y && y <= z) ==> x <= z; + +// conversions +test bool intToReal1(int i) = i == toReal(i); +test bool ratToReal1(rat r) = r == toReal(r); +test bool intToReal2(int i) = i <= toReal(i); +test bool ratToReal2(rat r) = r <= toReal(r); +test bool intToReal3(int i) = toReal(i) >= i; +test bool ratToReal3(rat r) = toReal(r) >= r; +test bool lessIntReal(int i) = !(i < toReal(i)); +test bool lessRatReal(int i) = !(i < toReal(i)); + +// set containment +test bool differentElements(int i) = size({i, toReal(i), toRat(i, 1)}) == 3; +// yes, really 3. +test bool differentElement2(int i, rat r) = i == r ==> size({i, r}) == 2; +// yes, really 2. +test bool differentElement3(int i, real r) = i == r ==> size({i, r}) == 2; + +// yes, really 2. +// map keys +test bool differentKeys1(int i, real r) = ((i : 10, + r : 20)[toReal(i)] ? 0) == 0; +test bool differentKeys2(int i, rat r) = ((i : 10, + r : 20)[toRat(i, 1)] ? 0) == 0; +test bool differentKeys3(int i) + = size((i : 10) + (toRat(i, 1) : 20) + (toReal(i) : 30)) == 3; + +// == vs eq +test bool eqImpliesEquals(value x, value y) = eq(x, y) ==> (x == y); +test bool nonComparabilityImpliesNonEq(value x, value y) + = !comparable(typeOf(x), typeOf(y)) ==> !eq(x, y); +test bool comparabilityImpliesEquivalence(value x, value y) + = comparable(typeOf(x), typeOf(y)) ==> (eq(x, y) <==> x == y); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/EscapedNames.rsc| +module lang::rascal::tests::basic::EscapedNames + +//import lang::rascal::tests::basic::\Escaped-name-module; +// +//test bool escapedNameModule() = importedFromEscapedNameModule(); + +int \an-int = 42; + +test bool escapedGlobal() = \an-int == 42; + +int \twice(int n) = 2 * n; +int \do-twice(int n) = 2 * n; + +test bool escapedFunctionName1() = \twice(2) == 4; +test bool escapedFunctionName2() = twice(2) == 4; +test bool escapedFunctionName3() = \do-twice(2) == 4; + +int same1(int \a) = a; +int same2(int \a) = \a; +int same3(int \an-a) = \an-a; + +test bool escapedPositionalFormal1() = same1(2) == 2; +test bool escapedPositionalFormal2() = same2(2) == 2; +test bool escapedPositionalFormal3() = same3(2) == 2; + +int samekw1(int \n = 42) = n; +int samekw2(int \n = 42) = \n; +int samekw3(int \a-n = 42) =\a-n; + +test bool escapedKeywordFormal1() = samekw1(n = 3) == 3; +test bool escapedKeywordFormal2() = samekw2(\n = 3) == 3; +test bool escapedKeywordFormal3() = samekw3(\a-n = 3) == 3; + +data D1 = d1(); +data \D2 = d2(); +data \A-D3 = d3(); + +test bool escapedADTName1() { \D1 x = d1(); return x == d1(); } +test bool escapedADTName2() { \D2 x = d2(); return x == d2(); } +test bool escapedADTName3() { D2 x = d2(); return x == d2(); } +test bool escapedADTName4() { \A-D3 x = d3(); return x == d3(); } + +data E1 = e1(); +data E2 = \an-e2(); + +test bool escapedConstructorName1() = \e1() == e1(); +test bool escapedConstructorName2() = \an-e2() == \an-e2(); + +data F1 = f1(int n); +data F2 = f2(int \a-n); + +test bool escapedPositionalField1() = f1(3).n == 3; +test bool escapedPositionalField2() = f1(3).\n == 3; +test bool escapedPositionalField3() = f2(3).\a-n == 3; + +data G1 = g1(int n = 42); +data G2 = g2(int \n = 42); +data G3 = g3(int \a-n = 42); + +test bool escapedkeywordField1a() = g1(n=3).n == 3; +test bool escapedkeywordField1b() = g1(\n=3).n == 3; +test bool escapedkeywordField1c() = g1(n=3).\n == 3; +test bool escapedkeywordField1d() = g1(\n=3).\n == 3; + +test bool escapedkeywordField2a() = g2(n=3).n == 3; +test bool escapedkeywordField2b() = g2(\n=3).n == 3; +test bool escapedkeywordField2c() = g2(n=3).\n == 3; +test bool escapedkeywordField2d() = g2(\n=3).\n == 3; + +test bool escapedkeywordField3() = g3(\a-n=3).\a-n == 3; + +test bool escapedkeywordFieldNode() = "f"(\a-n=3).\a-n == 3; + +data H1[&T] = h1(&T n); +data H2[&\T] = h2(&T n); +data H3[&\T] = h3(&\T n); +data H4[&T] = h4(&\T n); + +test bool escapedTypeParameter1() { H1[int] x = h1(3); return x.n == 3; } +test bool escapedTypeParameter2() { H2[int] x = h2(3); return x.n == 3; } +test bool escapedTypeParameter3() { H3[int] x = h3(3); return x.n == 3; } +test bool escapedTypeParameter4() { H4[int] x = h4(3); return x.n == 3; } + +alias A = int; +alias \B = int; +alias C = B; + +test bool escapedAlias1() { A x = 3; return x == 3; } +test bool escapedAlias2() { \A x = 3; return x == 3; } + +test bool escapedAlias3() { B x = 3; return x == 3; } +test bool escapedAlias4() { \B x = 3; return x == 3; } + +test bool escapedAlias5() { C x = 3; return x == 3; } +test bool escapedAlias6() { \C x = 3; return x == 3; } + +test bool escapedPatternName1a() = \x := 3 && x == 3; +test bool escapedPatternName1b() = \a-x := 3 && \a-x == 3; + +test bool escapedPatternName2a() = int \x := 3 && x == 3; +test bool escapedPatternName2b() = int \a-x := 3 && \a-x == 3; + +test bool escapedPatternName3a() = \y: int \x := 3 && y == 3; +test bool escapedPatternName3b() = \a-y: int \x := 3 && \a-y == 3; + +test bool escapedPatternName4a() = [\x] := [3] && x == 3; +test bool escapedPatternName4b() = [\a-x] := [3] && \a-x == 3; + +test bool escapedPatternName5a() = [*\x] := [3, 4] && x == [3, 4]; +test bool escapedPatternName5b() = [*\a-x] := [3, 4] && \a-x == [3, 4]; + +test bool escapedPatternName6a() = [*int \x] := [3, 4] && x == [3, 4]; +test bool escapedPatternName6b() = [*int \a-x] := [3, 4] && \a-x == [3, 4]; + +test bool escapedPatternName7a() = {\x} := {3} && x == 3; +test bool escapedPatternName7b() = {\a-x} := {3} && \a-x == 3; + +test bool escapedPatternName8a() = {*\x} := {3, 4} && x == {3, 4}; +test bool escapedPatternName8b() = {*\a-x} := {3, 4} && \a-x == {3, 4}; + +test bool escapedPatternName9a() = {*int \x} := {3, 4} && x == {3, 4}; +test bool escapedPatternName9b() = {*int \a-x} := {3, 4} && \a-x == {3, 4}; + +test bool escapedPatternName10a() = <\x> := <3> && x == 3; +test bool escapedPatternName10b() = <\a-x> := <3> && \a-x == 3; + +@ignoreInterpreter{TBD} +test bool escapedPatternName11a() = /a<\z:[b]>c/ := "abc" && z == "b"; + +@ignoreInterpreter{TBD} +test bool escapedPatternName11b() = /a<\a-z:[b]>c/ := "abc" && \a-z == "b"; + +test bool escapedWhileLabel() { + \a-l: while(true){ + break \a-l; + } + return true; +} + +test bool escapedDoLabel() { + \a-l: do { + break \a-l; + } while(true); + return true; +} + +test bool escapedForLabel() { + \a-l: for(x <- [1..10]){ + break \a-l; + } + return true; +} + +test bool escapedParameterAndPatternVar(){ + bool f11(\a) = \a := 10; + bool f10(\a) = a := 10; + bool f01(a) = \a := 10; + bool f00(a) = a := 10; + + return f11(10) && f10(10) && f01(10) && f00(10); +} + +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Exceptions.rsc| +module lang::rascal::tests::basic::Exceptions + +import List; +import Exception; +import String; + +test bool exceptionHandling1() { + value f() { + throw "Try to catch me!"; + } + + str n = "0"; + + try { + n = n + " 1"; + try { + n = n + " 2"; + f(); + n = n + " 3"; + // dead code + } + catch 0: { + n = n + " 4"; + } + catch int _: { + n = n + " 5"; + } + n = n + " 6"; + } + catch "0": { + n = n + " 7"; + } + catch str _: { + n = n + " 8"; + } + return n == "0 1 2 8"; +} + +test bool exceptionHandling2() { + value f() { + throw "Try to catch me!"; + } + value f(str s) { + throw "Try to catch: !"; + } + + str n = "start"; + + // Example of try/catch blocks followed by each other + try { + throw 100; + // Example of the default catch + } + catch: { + n = n + ", then default"; + } + try { + throw 100; + // Example of an empty, default catch + } + catch: { + ; + } + + try { + try { + n = n + ", then 1"; + try { + n = n + ", then 2"; + f(); + n = n + ", then 3"; + // dead code + // Example of catch patterns that are incomplete with respect to the 'value' domain + } + catch 0: { + n = n + ", then 4"; + } + catch 1: { + n = n + ", then 5"; + } + catch "0": { + n = n + ", then 6"; + } + n = n + ", then 7"; + // dead code + // Example of overlapping catch patterns and try/catch block within a catch block + } + catch str _: { + n = n + ", then 8"; + try { + n = n + ", then 9"; + f(n); + } + catch int _: { + n = n + ", then 10"; + } + n = n + ", then 11"; + } + catch value _: { + n = n + ", then 12"; + } + } + catch value v: { + n = ", then last catch and $$$"; + } + + return + n == "Try to catch: start, then default, then 1, then 2, then 8, then 9!, then last catch and $$$"; +} + +test bool exceptionHandling3() { + value f() { + throw "Try to catch me!"; + } + value f(str s) { + throw "Try to catch: !"; + } + + str n = "start"; + + // Example of try/catch blocks followed by each other + try { + throw 100; + // Example of the default catch + } + catch: { + n = n + ", then default"; + } + + try { + throw 100; + // Example of an empty, default catch + } + catch: { + ; + } + + try { + try { + try { + n = n + ", then 1"; + try { + n = n + ", then 2"; + f(); + n = n + ", then 3"; + // dead code + // Example of catch patterns that are incomplete with respect to the 'value' domain + } + catch 0: { + n = n + ", then 4"; + } + catch 1: { + n = n + ", then 5"; + } + catch "0": { + n = n + ", then 6"; + } + n = n + ", then 7"; + // dead code + // Example of overlapping catch patterns and try/catch block within a catch block + } + catch str _: { + n = n + ", then 8"; + try { + n = n + ", then 9"; + f(n); + // re-throws + } + catch int _: { + // should not catch re-thrown value due to the type + n = n + ", then 10"; + } + n = n + ", then 11"; + } + catch value _: { + n = n + ", then 12"; + } + // The outer try block of the catch block that throws an exception (should not catch due to the type) + } + catch int _: { + n = n + ", then 13"; + } + // The outer try block of the catch block that throws an exception (should catch) + } + catch value v: { + n = ", then last catch and $$$"; + } + + return + n == "Try to catch: start, then default, then 1, then 2, then 8, then 9!, then last catch and $$$"; +} + +test bool exceptionHandling4() { + try { + head([]); + } + catch EmptyList(): + return true; + return false; +} + +test bool exceptionHandlingFinally1() { + str main() { + str n = "0"; + + try { + n = n + " 1"; + try { + n = n + " 2"; + + // Inline in a 'try' block + return n + " has been returned!"; + } + catch 0: { + n = n + " 4"; + } + catch int _: { + n = n + " 5"; + } + finally { + n = n + " 6"; + + // Inline in a 'finally' block + return n + " has been returned from the inner finally!"; + } + } + catch "0": { + n = n + " 8"; + } + catch str _: { + n = n + " 9"; + } + finally { + n = n + " 10"; + } + return "no case matched"; + } + + return main() == "0 1 2 6 has been returned from the inner finally!"; +} + +test bool exceptionHandlingFinally2() { + str main() { + str n = "0"; + try { + n = n + " 1"; + try { + n = n + " 2"; + + // Inline in a 'try' block + return n + " has been returned!"; + } + catch 0: { + n = n + " 4"; + } + catch int _: { + n = n + " 5"; + } + finally { + n = n + " 6"; + + // Inline in a 'finally' block + return n + " has been returned from the inner finally!"; + } + } + catch "0": { + n = n + " 8"; + } + catch str _: { + n = n + " 9"; + } + finally { + n = n + " 10"; + + // Inline in a 'finally' block + return n + " has been returned from the outer finally!"; + } + } + + return main() == "0 1 2 6 10 has been returned from the outer finally!"; +} + +test bool exceptionHandlingFinally3() { + str main() { + str n = "0"; + + try { + // 'Try-catch-finally' + n = n + " 1"; + try { + // 'Try-catch' + n = n + " 2"; + + // Inline in a 'try' block + return n + " has been returned!"; + } + catch 0: { + n = n + " 4"; + } + catch int _: { + n = n + " 5"; + } + n = n + " 7"; + return " should not come here"; + } + catch "0": { + n = n + " 8"; + } + catch str _: { + n = n + " 9"; + } + finally { + n = n + " 10"; + + // Inline in a 'finally' block + return n + " has been returned from the outer finally!"; + } + } + return main() == "0 1 2 10 has been returned from the outer finally!"; +} + +test bool exceptionHandlingFinally4() { + str main() { + str n = "0"; + + // No exceptions and no returns + try { + n = n + " 1"; + try { + n = n + " 2"; + n = n + " 3"; + } + catch str _: { + n = n + " 5"; + } + finally { + n = n + " 6"; + } + n = n + " 7"; + } + catch "0": { + n = n + " 8"; + } + catch str _: { + n = n + " 9"; + } + finally { + n = n + " 10"; + } + return n; + } + return main() == "0 1 2 3 6 7 10"; +} + +test bool exceptionHandlingFinally5() { + value f() { + throw "Try to catch me!"; + } + + str main() { + str n = "0"; + + try { + n = n + " 1"; + try { + n = n + " 2"; + f(); + n = n + " 3"; + // dead code + return " should not come here"; + } + catch "0": { + n = n + " 4"; + + // Inline in a 'catch' block + return n; + } + catch str _: { + n = n + " 5"; + + // Inline in 'catch' block + return n + " has been returned from the inner catch!"; + } + finally { + n = n + " 6"; + + // Inline in a 'finally' block + return n + " has been returned from the inner finally!"; + } + } + catch "0": { + n = n + " 8"; + } + catch str _: { + n = n + " 9"; + } + finally { + n = n + " 10"; + + // Inline in a 'finally' block + return n + " has been returned from the outer finally!"; + } + } + return main() == "0 1 2 5 6 10 has been returned from the outer finally!"; +} + +test bool exceptionHandlingFinally6() { + value f() { + throw "Try to catch me!"; + } + + str main() { + str n = "0"; + try { + try { + n = n + " 1"; + try { + n = n + " 2"; + f(); + n = n + " 3"; + // dead code + } + catch 0: { + n = n + " 4"; + + // Inline in a 'catch' block: does not match! + return n; + } + catch int _: { + n = n + " 5"; + + // Inline in 'catch' block: does not match! + return n + " has been returned from the inner catch!"; + } + finally { + n = n + " 6"; + } + n = n + " 7"; + return " should not come here"; + } + catch "0": { + n = n + " 8"; + } + catch str _: { + n = n + " 9"; + + // Inline in 'catch' block: does match! + return n + " has been returned from the outer catch!"; + } + finally { + n = n + " 10"; + + // Inline in a 'finally' block + return n + " has been returned from the outer finally!"; + } + } + catch: { + ; + } + finally { + // Inline in a 'finally' block + n = n + " and last finally"; + return n; + } + } + return main() == "0 1 2 6 9 10 and last finally"; +} + +test bool exceptionHandlingFinally7() { + value f() { + throw "Try to catch me!"; + } + + str main() { + str n = "0"; + try { + try { + n = n + " 1"; + try { + n = n + " 2"; + f(); + // Throws + n = n + " 3"; + // dead code + } + catch 0: { + // Does not match! + n = n + " 4"; + } + catch int _: { + n = n + " 5"; + + // Inline in 'catch' block: does not match! + return n + " has been returned from the inner catch!"; + } + finally { + n = n + " 6"; + } + n = n + " 7"; + } + catch "0": { + n = n + " 8"; + } + catch str _: { + // Does match! + n = n + " 9"; + } + finally { + n = n + " 10"; + } + n = n + " 11"; + f(); + // Throws again! + n = n + " 12"; + } + catch: { + n = n + " 13"; + } + finally { + // Inline in a 'finally' block + n = n + " and last finally"; + } + n = n + ", then before return!"; + return n; + // result: 0 1 2 6 9 10 11 13 and last finally, then before return! + } + return main() == "0 1 2 6 9 10 11 13 and last finally, then before return!"; +} + +test bool exceptionHandlingFinally8() { + value f() { + throw "Try to catch me!"; + } + + str main() { + str n = "0"; + try { + try { + n = n + " 1"; + try { + n = n + " 2"; + f(); + // Throws + n = n + " 3"; + // dead code + } + catch 0: { + // Does not match! + n = n + " 4"; + } + catch int _: { + n = n + " 5"; + + // Inline in 'catch' block: does not match! + return n + " has been returned from the inner catch!"; + } + finally { + n = n + " 6"; + } + n = n + " 7"; + } + catch "0": { + n = n + " 8"; + } + catch str _: { + // Does match! + n = n + " 9"; + } + finally { + n = n + " 10"; + f(); + // Throws again! + n = n + " 11"; + } + n = n + " 12"; + f(); + // Throws again, but unreachable + n = n + " 13"; + } + catch value _: { + n = n + " 14"; + } + finally { + // Inline in a 'finally' block + n = n + " and last finally"; + } + n = n + ", then before return!"; + return n; + // result: 0 1 2 6 9 10 14 and last finally, then before return! + } + return main() == "0 1 2 6 9 10 14 and last finally, then before return!"; +} + +test bool exceptionHandlingNotHandled() { + value f() { + throw "Try to catch me!"; + } + value f(str s) { + throw "Try to catch: !"; + } + + str main() { + str n = "start"; + try { + try { + n = n + ", then 1"; + try { + n = n + ", then 2"; + f(); + n = n + ", then 3"; + // dead code + } + catch 0: { + n = n + ", then 4"; + } + catch 1: { + n = n + ", then 5"; + } + catch "0": { + n = n + ", then 6"; + } + n = n + ", then 7"; + // dead code + } + catch str _: { + n = n + ", then 8"; + try { + n = n + ", then 9"; + f(n); + } + catch int _: { + n = n + ", then 10"; + } + n = n + ", then 11"; + } + } + catch int _: { + n = n + ", then 13"; + } + return n; + } + + try { + main(); + return false; + } + catch str s: { + return s == "Try to catch: start, then 1, then 2, then 8, then 9!"; + } +} + +test bool exceptionHandlingNotHandledSimple1() { + void divide() { + 1/0; + } + + void main() { + divide(); + } + + try { + main(); + return false; + } + catch value v: { + return ArithmeticException(str msg) := v && endsWith(msg, "by zero"); + } +} + +test bool rascalException1() { + map[int, str] m = (0 : "0", + 1 : "1", + 2 : "2"); + + str trace = ""; + + try { + m[3]; + } + catch NoSuchKey(k): { + trace = trace + " (not found)"; + } + finally { + trace = "map key: " + trace; + } + return trace == "map key: 3 (not found)"; +} + +test bool rascalRuntimeExceptionsPlusOverloading() { + str trace = ""; + + void f(int _) { + map[int, str] m = (0 : "0", + 1 : "1", + 2 : "2"); + trace = trace + "Bad function f; "; + m[3]; + } + + void g(0) { + try { + return f(0); + } + catch NoSuchKey(k): { + trace = trace + "map key: (not found); "; + } + finally { + trace = trace + "finally; "; + } + fail; + } + + default void g(int i) { + trace = trace + "default void g(int);"; + } + + trace = ""; + g(0); + return + trace == "Bad function f; map key: 3 (not found); finally; default void g(int);"; +} + +test bool untypedCatch1() { + x = 1; + try { + throw "exception"; + } + catch _: + x += 1; + return x == 2; +} + +test bool untypedCatch2() { + x = 1; + try { + throw "exception"; + } + catch int _: + return false; + catch _: + x += 1; + return x == 2; +} + +test bool untypedCatch3() { + x = 1; + try { + throw "exception"; + } + catch int _: + return false; + catch _: + x += 1; + finally + ; + + return x == 2; +} + +test bool definedVarCatch() { + x = 1; + try { + throw 1; + } + catch x: + x += 1; + finally + ; + + return x == 2; +} + +test bool valueCatch() { + x = 1; + try { + throw "exception"; + } + catch value _: + x += 1; + finally + ; + + return x == 2; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/FunctionAssignment.rsc| +module lang::rascal::tests::basic::FunctionAssignment + +import String; + +public int i = 0 ; + +test bool triggerAssignmentBug() { + str(str) stealTheFunction = toUpperCase; + + // by now the interpreter would fail with an exception: + // Undeclared variable: toUpperCase + return toUpperCase("abc") == "ABC"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Functions.rsc| +module lang::rascal::tests::basic::Functions + +import List; +import Node; +import Exception; + +data B + = and(B lhs, B rhs) + | or(B lhs, B rhs) + | t() + | f() + ; + +B and(B b1, and(B b2, B b3)) = and(and(b1, b2), b3); + +value callDelAnnotations() = delAnnotations("f"(1, 2, 3)); + +test bool testCallWithTypeParameterBound() + = callDelAnnotations() == "f"(1, 2, 3); + +test bool normalizedCall(B b1, B b2, B b3) + = and(b1, and(b2, b3)) == and(and(b1, b2), b3); + +test bool normalizedVisit() + = /and(_, and(_, _)) !:= + visit(or(or(t(), t()), or(t(), t()))) { + case or(a, b) => and(a, b) + }; + +B(B, B) giveOr() = or; + +test bool giveOr1() = giveOr()(t(), f()) == or(t(), f()); + +private test bool callKwp() { + kwp(x = 2); + // this would previously change the static type of the x argument of kwp to int + return true; +} + +private void kwp(value x = 1) { + // this is a regression test for a bug; + x = "node"(); +// if the static type of the kwp is value, then we should be allowed to assign a node into it. +} + +private str g(str s) = inter + when str inter := s; + +test bool functionWithWhen() { + return g("Called!") == "Called!"; +} + +private +str fn(str s, int ints..., str kw = "keyword") + = s + "-" + intercalate("-", ints) + "-" + kw; + +test bool functionWithVarargsAndKeyword1() = fn("a") == "a--keyword"; +test bool functionWithVarargsAndKeyword2() = fn("b", kw = "xxx") == "b--xxx"; +test bool functionWithVarargsAndKeyword3() + = fn("c", 1, 2, 3) == "c-1-2-3-keyword"; +test bool functionWithVarargsAndKeyword4() + = fn("d", 1, 2, 3, kw = "xxx") == "d-1-2-3-xxx"; + +int fvarargs(str _, value _...) = 0; +int fvarargs(int _, str s, value _...) = 1; +int fvarargs(bool _, int _, str _, value _...) = 2; + +test bool overloadedWithVarArgs1() = fvarargs("a") == 0; +test bool overloadedWithVarArgs2() = fvarargs("a", 1) == 0; +test bool overloadedWithVarArgs3() = fvarargs("a", 1, 2) == 0; + +test bool overloadedWithVarArgs4() = fvarargs(10, "a") == 1; +test bool overloadedWithVarArgs5() = fvarargs(10, "a", 1) == 1; +test bool overloadedWithVarArgs6() = fvarargs(10, "a", 1, 2) == 1; + +test bool overloadedWithVarArgs7() = fvarargs(true, 10, "a") == 2; +test bool overloadedWithVarArgs8() = fvarargs(true, 10, "a", 1) == 2; +test bool overloadedWithVarArgs9() = fvarargs(true, 10, "a", 1, 2) == 2; + +int f1() { + int g1() = 101; + int() k1() { + int() h1() = g1; + return h1(); + } + ; + return k1()(); +} + +value nestedFunctions1() { + return f1(); +} + +str f2(0) { + res = g2("0"); + return "f2(0); " + res; +} +str f2(1) { + res = g2("1"); + fail; +} +default str f2(int n) { + res = g2(""); + return "default f2(1);" + res; +} + +str g2("0") = "g2(\"0\")"; +str g2("1") = "g2(\"1\")"; +default str g2(str s) = "default g2();"; + +test bool nestedFunctionCall1() = f2(0) == "f2(0); g2(\"0\")"; +test bool nestedFunctionCall2() = f2(1) == "default f2(1);g2(\"1\")"; +test bool nestedFunctionCall3() = f2(5) == "default f2(1);default g2(5);"; + +str f3(0) { + res = g3("0"); + return "f3(0); " + res; +} +str f3(1) { + str res = "***init***"; + try { + res = g3("1"); + } + catch str s: { + res = "catched(); g3(\"1\")"; + } + ; + fail; +} +default str f3(int n) { + str res = "***init***"; + try { + res = g3(""); + } + catch str s: { + res = "catched(); g3()"; + } + return "default f3(); " + res; +} + +str g3("0") = "g3(\"0\")"; +str g3("1") { + throw "Try to catch me!!!"; +} +default str g3(str s) = "default g3()"; + +test bool nestedFunctions3() { + return + f3(0) + " ;; " + f3(1) + " ;; " + f3(5) == "f3(0); g3(\"0\") ;; default f3(1); catched(Try to catch me!!!); g3(1) ;; default f3(5); default g3(5)"; +} + +int() g4() { + int k4() = 666; + int h4() = k4(); + return h4; +} + +test bool nestedFunctions4() { + return g4()() == 666; +} + +public int(int) f5() { + int n = 100; + return int (int i){ return i + n; }; +} + +public int(int) h5(int n1) { + int n2 = 50; + int k5(int i) { + return n1 + n2 + i; + } + return k5; +} + +test bool capture1() { + g5 = f5(); + res1 = g5(11); + + l5 = h5(1); + res2 = l5(2); + + return res1 + res2 == 111 + 53; +} + +test bool closure1() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + list[int] outputs + = [int + (int n){ + switch(n) { + case 0: + return 1; + case 1: + return 1; + case int m: + return m * (m - 1); + default: + return -1; + } + }/* renamed n to m*/ ( + int + (int n){ + switch(n) { + case 0: + return 0; + case 1: + return 1; + case int m: + return (m - 1) + (m - 2); + default: + return -1; + } + }/* renamed n to m*/ ( + i + ) + ) | int i <- inputs]; + return outputs == [1, 1, 1, 6, 20, 42, 72, 110, 156, 210]; +} + +private &T something(set[&T] x) { + if (e <- x) + return e; + fail; +} + +private default value something({}) = "hello"; + +test bool parameterizedFunctionsDoNotReturnVoid() { + set[value] emptySet = {}; + return "hello" == something(emptySet); +} + +test bool functionTypeArgumentVariance1() + = int(value _) _ := int (int x){ return 1; }; + +test bool functionTypeArgumentVariance2() + = int(int _) _ := int (value x){ return 1; }; + +test bool functionTypeArgumentVariance3() { + value f = int (str x){ return 1; }; + return int(int _) _ !:= f; +} + +test bool higherOrderFunctionCompatibility1() { + // the parameter function is specific to int + int parameter(int _) { + return 0; + } + + // the higher order function expects to call the + // parameter function with other things too + int hof(int(value) p, value i) { + return p(i); + } + + // still this is ok, since functions in Rascal + // are partial. This call should simply succeed: + if (hof(parameter, 1) != 0) { + return false; + } + // but the next call produces a CallFailed, since + // the parameter function is not defined on strings: + try { + // statically allowed! but dynamically failing + hof(parameter, "string"); + return false; + } + catch CallFailed(_): + return true; +} + +test bool higherOrderFunctionCompatibility2() { + // the parameter function is very generic + int parameter(value _) { + return 0; + } + + // the higher order function expects to call the + // parameter function with only integers + int hof(int(int) p, value i) { + return p(i); + } + + // this is ok, a more generic function should be + // able to handle ints. This call should simply succeed: + if (hof(parameter, 1) != 0) { + return false; + } + return true; +} + +// @ignore{this fails also in the interpreter because the algorithm +// for binding type parameter uses `match` in two directions which +// implements comparability rather than intersectability} +// test bool higherOrderFunctionCompatibility3() { +// // the parameter function is specific for tuple[int, value] +// int parameter(tuple[int, value] _) { return 0; } +// // the higher order function expects to call the +// // parameter function with tuple[value, int] +// int hof(int (tuple[value, int]) p, tuple[value,int] i) { return p(i); } +// // this is ok, the parameter function's type has a non-empty +// // intersection at tuple[int, int], so at least for such +// // tuples the function should succeed +// if (hof(parameter, <1,1>) != 0) { +// return false; +// } +// // however, when called with other tuples the parameter fails +// // at run-time: +// try { +// // statically allowed! but dynamically failing +// hof(parameter, <"1", 1>); +// return false; +// } +// catch CallFailed(_): +// return true; +// return false; +// } +test bool higherOrderVoidFunctionCompatibility() { + bool hof(void(int s) g) { + g(0); + return true; + } + void ff(int _) { + return ; + } + + try { + return hof(ff); + } + catch CallFailed(_): + return false; +} + +test bool returnOfAnInstantiatedGenericFunction() { + &S(&U) curry(&S(&T, &U) f, &T t) = &S( &U u ) { + return f(t, u); + }; + + int add(int i, int j) = i + j; + + // issue #1467 would not allow this assignment because the + // returned closure was not instantiated properly from `&S (&U)` to `int(int)` + int(int) f = curry(add, 1); + + return f(1) == 2 && (f o f)(1) == 3; +} + +test bool curryAConstructor() { + &S(&U) c(&S(&T, &U) f, &T t) = &S( &U u ) { + return f(t, u); + }; + + B(B) f = c(and, t()); + + return f(t()) == and(t(), t()); +} + +test bool selfApplyCurry() { + &S(&U) curry(&S(&T, &U) f, &T t) = &S( &U u ) { + return f(t, u); + }; + + int addition(int i, int j) = i + j; + + func = curry(curry, addition); + + assert int(int)(int) _ := func; + + func2 = func(1); + + assert int(int) _ := func2; + + return func2(1) == 2; +} + +test bool accessParameterInClosure() { + int() make(int x) = int( ) { + return x; + }; + + int() use(int n) = make(n); + + return use(3)() == 3; +} + +test bool assignVariableInNestedFunctions() { + int X = 0; + int Y = 0; + + int incX() { + X += 1; + return X; + } + int incY() { + Y += 1; + return Y; + } + + int incXY() = incX() + incY(); + + incX(); + incX(); + incX(); + incY(); + incY(); + + return incXY() == 7; +} + +test bool assignVariableInNestedFunctionWithVisit() { + int uniqueItem = 1; + int newItem() { + uniqueItem += 1; + return uniqueItem; + } + ; + + list[str] rewrite(list[str] p) + = visit(p) { + case "a": + newItem(); + case "b": + newItem(); + }; + return rewrite(["a", "b", "c"]) == ["a", "b", "c"] && uniqueItem == 3; +} + +int container(int n) { + int f() = g(); + + int g() = h(); + + int h() = n + 1; + + return f(); +} + +test bool sharedFormal() = container(12) == 13; + +test bool assignVariableInNestedFunctions1() { + int x = 1; + + void setX() { + x = 10; + } + + setX(); + return x == 10; +} + +test bool assignVariableInVisit1() { + int x = 1; + + visit([1, 2, 3]) { + case 1: + x = 10; + } + return x == 10; +} + +test bool assignVariableInNestedFunctions2(int x) { + void setX() { + x = 10; + } + + setX(); + return x == 10; +} + +test bool assignVariableInVisit2(int x) { + visit([1, 2, 3]) { + case 1: + x = 10; + } + + return x == 10; +} + +test bool assignVariableInNestedFunctions3() { + int x = 1; + int y = 100; + + void setX() { + x = 10; + } + void setY() { + y = 20; + } + + x1 = x; + y1 = y; + setX(); + if (x != 10 || y != y1) + return false; + x1 = x; + y1 = y; + setY(); + if (x != x1 || y != 20) + return false; + return true; +} + +test bool assignVariableInVisit3() { + int x = 1; + int y = 100; + + x1 = x; + y1 = y; + visit([1, 2, 3]) { + case 1: + x = 10; + } + if (x != 10 || y != y1) + return false; + x1 = x; + y1 = y; + visit([1, 2, 3]) { + case 1: + y = 20; + } + if (x != x1 || y != 20) + return false; + return true; +} + +test bool assignVariableInNestedFunctions4() { + int x = 1; + int y = x; + + void setX() { + x = 10; + } + void setY() { + y = 20; + } + + x1 = x; + y1 = y; + setX(); + if (x != 10 || y != y1) + return false; + x1 = x; + y1 = y; + setY(); + if (x != x1 || y != 20) + return false; + return true; +} + +test bool assignVariableInVisit4() { + int x = 1; + int y = x; + + x1 = x; + y1 = y; + visit([1, 2, 3]) { + case 1: + x = 10; + } + if (x != 10 || y != y1) + return false; + x1 = x; + y1 = y; + visit([1, 2, 3]) { + case 1: + y = 20; + } + if (x != x1 || y != 20) + return false; + return true; +} + +test bool assignVariableInNestedFunctions5(int x) { + int y = x; + int z = 3; + + void setX() { + x = 10; + } + void setY() { + y = 20; + } + void setZ() { + z = 30; + } + + x1 = x; + y1 = y; + z1 = z; + setX(); + if (x != 10 || y != y1 || z != z1) + return false; + x1 = x; + y1 = y; + z1 = z; + + setY(); + if (x != x1 || y != 20 || z != z1) + return false; + x1 = x; + y1 = y; + z1 = z; + setZ(); + + if (x != x1 || y != y1 || z != 30) + return false; + return true; +} + +test bool assignVariableInVisit5(int x) { + int y = x; + int z = 3; + + x1 = x; + y1 = y; + z1 = z; + visit([1, 2, 3]) { + case 1: + x = 10; + } + if (x != 10 || y != y1 || z != z1) + return false; + x1 = x; + y1 = y; + z1 = z; + + visit([1, 2, 3]) { + case 1: + y = 20; + } + if (x != x1 || y != 20 || z != z1) + return false; + x1 = x; + y1 = y; + z1 = z; + visit([1, 2, 3]) { + case 1: + z = 30; + } + + if (x != x1 || y != y1 || z != 30) + return false; + return true; +} + +data D = one_d(int(int a) n); + +test bool assignVariableViaFunctionValue() { + set[int] x = {}; + + int add(int n) { + x += n; + return n; + } + + D make() { + return one_d(add); + } + + m = make(); + m.n(3); + m.n(4); + return x == {3, 4}; +} + +test bool namedParameterInClosure() { + int collect(current: int n) { + return int (){ return current; }(); + } + return collect(5) == 5; +} + +data E = e(int n); + +int useExtraFormalInConstructor(e(int n)) { + int g() { + return n; + } + return g(); +} + +test bool useExtraFormalInConstructor1() + = useExtraFormalInConstructor(e(42)) == 42; + +data F = f(list[int] ints); + +int useExtraFormalInListPattern(f([*int ints])) { + int g() { + return ints[0]; + } + return g(); +} + +test bool useExtraFormalInListPattern1() + = useExtraFormalInListPattern(f([42])) == 42; + +int _f(int n) = n; + +test bool functionNameStartsWithUnderscore() = _f(13) == 13; + +test bool innerAndOuterFunctionUseSameParameterName1() { + int outer(int t) { + int inner(t: 3) = t; + default int inner(int t) = 10 * t; + return inner(t); + } + + return outer(3) == 3 && outer(5) == 50; +} + +test bool innerAndOuterFunctionUseSameParameterName2() { + int outer(int t) { + int inner(int t: 3) = t; + default int inner(int t) = 10 * t; + return inner(t); + } + + return outer(3) == 3 && outer(5) == 50; +} + +@ignoreCompiler{"Return type `int` expected, found `str`"} +test bool innerAndOuterFunctionUseSameParameterName3() { + int outer(str t) { + int inner(t: 3) = t; + default int inner(int t) = 10 * t; + return inner(3); + } + + return outer("a") == 30; +} + +test bool innerAndOuterFunctionUseSameParameterName4() { + int outer(str t) { + int inner(int t: 3) = t; + default int inner(int t) = 10 * t; + return inner(3); + } + + return outer("a") == 3; +} + +test bool stackoverflow() { + int f(int i) = f(i); + + try { + f(1); + return false; + } + catch StackOverflow(): + return true; +} + +@synopsis{Nested clones in different scopes are acceptable} +int fun_with_clone(int n) { + if (n > 0) { + int h(int n) = 2 * n; + return h(n); + } + else { + int h(int n) = 2 * n; + return h(n); + } +} + +test bool noCodeClone() = fun_with_clone(3) == 6; + +test bool javaFunctionsKeepLabels() { + lrel[int a, int b] abs = [<3, 4 >, + <1, 2 >]; + return sort(abs).a == [1, 3]; +} + +alias Entry = tuple[int a, int b]; +test bool javaFunctionsLaterBindings() { + return + sort([<3, 4 >, + <1, 2 >], bool (Entry left, Entry right) { + return left.a < right.a; + }).a == [1, 3]; +} + +int aFunction() = 42; +default int bFunction() = 84; +/* must be default to force the choice to `aFunction` in this test */int testFunction(int() aFunction/* must be the same as `aFunction` */ ) = aFunction(); + +test bool functionParameterNameEqualToFunctionName() { + assert testFunction(aFunction) == 42; + + // issue #2575 made `aFunction` in `testFunction` merge the parameter + // function `aFunction` with the defined function `aFunction`, such that + // it becomes a cached prefix in `testFunction` and always produce the + // result when it was called with `aFunction` for the last time. + assert testFunction(bFunction) == 84; + /* expect 42 when buggy */ return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Generics.rsc| +@synopsis{tests specific aspects of generic functions and generic data-types in Rascal} +module lang::rascal::tests::basic::Generics + +import Exception; +import util::Maybe; + +data Wrapper[&SAME] = something(&SAME wrapped); +alias Graph[&SAME] = rel[&SAME from, &SAME to]; + +@synopsis{it matters for testing that '&SAME' is the same name as in the definition of Wrapper} +&SAME getIt(Wrapper[&SAME] x) = x.wrapped; + +test bool hygienicGenericADT() { + // in the same context, we bind the same type parameter in + // different ways to see if nothing is leaking. + int i = something(1).wrapped; + int j = getIt(something(2)); + int k = getIt(something(3)); + str x = something("x").wrapped; + str y = getIt(something("y")); + + return i == 1 && j == 2 && k == 3 && x == "x" && y == "y"; +} + +int recursiveGenericFunction(&T n) { + if (str _() := n) { + return 0; + } + // test rebinding the generic type to another unrelated type: + assert recursiveGenericFunction(""()) == 0; + + if (str _(value arg) := n) { + // call a recursive function, if the type resolution is not hygienic, + // then &T would be bound to itself or to a caller instance type + // which might lead to infinite instantiation cycles in vallang + // if not corrected for. + return 1 + recursiveGenericFunction(arg); + } + return 1; +} + +test bool genericFunction1() + = recursiveGenericFunction("aap"("noot")) == 2; +test bool genericFunction2() + = recursiveGenericFunction("aap"("noot"("mies"))) == 3; + +bool less(&T a, &T b) = a < b; + +test bool lessIsConsistentThroughTypeParameters(num x, num y) + = (x < y) ==> less(x, y); + +@ignoreCompiler{How to make 1 compatible with &T?} +&T avoidEmpty(list[&T] _) { + return 1; +} +@ignoreCompiler{How to make 1 compatible with &T?} +&T avoidEmpty(list[&T] _) { + throw "this should happen"; +} + +@ignoreCompiler{How to make 1 compatible with &T?} +test bool voidReturnIsNotAllowed() { + try { + avoidEmpty([]); + return false; + } + catch "this should happen": + return true; +} + +&T cast(type[&T] _, value x) = y + when &T y := x; + +test bool typeParametersAreCheckedStaticallyButAlsoBoundDynamically() { + // statically type[num] but dynamically type[int] + type[num] t = #int; + num r = 1r2; + + try { + // we can only guarantee cast will return `num` + // but it should still fail because r is not an int + // the run-time does not know the static types of all expressions + // passed to a generic function, so all it has are the dynamic + // types then. Even for reified types this is the case, due + // to the co-variance of the type[_] type. + num n = cast(t, r); + return false; + } + catch CallFailed(_): + return true; +} + +// the filter functies guarantees statically all elements will be sub-type of the static instance of &T at the call +// site, by making sure to use the _dynamic_ type of the reified type value t during pattern matching! The compiler +// does not require to maintain information about static types at run-time because of this. +list[&T] \filter(type[&T] _, list[value] elems) = [e | &T e <- elems]; + +test bool typeParametersAreCheckedStaticallyButAlsoBoundDynamically2() + = [1, 2, 3] == \filter ( + #int, + [1, "1", 1r, 2, "2", 1r2, 3, "3", 1r3] + ); + +test bool typeParametersAreCheckedStaticallyButAlsoBoundDynamically2_2( +) + = [1, 2, 3] == \filter ( + t, + [1, "1", 1r, 2, "2", 1r2, 3, "3", 1r3] + ) + when type[num] t := #int; + +test bool typeParametersAreCheckedStaticallyButAlsoBoundDynamically3() + = [1, 1r, 2, 1r2, 3, 1r3] == \filter ( + #num, + [1, "1", 1r, 2, "2", 1r2, 3, "3", 1r3] + ); + +test bool typeParametersAreCheckedStaticallyButAlsoBoundDynamically3_2( +) + = [1, 1r, 2, 1r2, 3, 1r3] == \filter ( + t, + [1, "1", 1r, 2, "2", 1r2, 3, "3", 1r3] + ) + when type[value] t := #num; + +test bool typeParametersAreCheckedStaticallyButAlsoBoundDynamically3_3( +) + = [1, "1", 1r, 2, "2", 1r2, 3, "3", 1r3] == \filter ( + t, + [1, "1", 1r, 2, "2", 1r2, 3, "3", 1r3] + ) + when type[value] t := #value; + +test bool staticTypeParametersKeepElementLabelsAlsoWithListMatch() { + &T first([&T head, *&T _]) = head; + + lrel[int first, int second] myList = [<1, 2 >, + <2, 3 >]; + + myElem = first(myList); + + return myElem.first == 1 && myElem.second == 2; +} + +test bool staticTypeParametersKeepElementLabelsAlsoWithSetMatch() { + &T take({&T some, *&T _}) = some; + + rel[int first, int second] mySet = {<1, 2 >, + <2, 3 >}; + + myElem = take(mySet); + + return myElem.first == 1 && myElem.second == 2; +} + +test bool recursiveOverloadedGenericFunction() { + str f(int i) = ""; + str f(map[&K, &V] m) = "(<for (k <- m) {>:, <}>)"; + str f(list[&E] l) = "[<for (e <- l) {>, <}>]"; + + return f((1 : (1 : 2))) == "(1:(1:2, ), )"; +} + +test bool voidMaybeShouldShouldThrowException() { + &T testFunction(Maybe[&T] m) = m.val; + + try { + Maybe[value] m = nothing(); + value x = testFunction(m); + return x != 123; + // this comparison never happens + } + catch NoSuchField(_): + return true; +} + +test bool voidListsShouldThrowException() { + tuple[&T, list[&T]] headTail(list[&T] l) { + if ([&T h, *&T t] := l) { + return ; + } + fail; + } + + try { + list[value] m = []; + tuple[value, list[value]] x = headTail(m); + return x != <123, [456]>; + // this comparison never happens + } + catch CallFailed([[]]): + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/IO.rsc| +module lang::rascal::tests::basic::IO + +import String; + +import IO; +import util::UUID; +import Exception; +import ValueIO; + +private loc aFile = |memory://test-tmp/basic-io-< "" >.txt| ; + +test bool writeReadFile(str content) { + if (size(content) == 0 || any(c <- [0..size(content)], content[c] == "\a00")) + return true; + writeFile(aFile, content); + return readFile(aFile) == content; +} + +@ignore{not all values can be read in back (such as constructors that turn into nodes)} +test bool writeReadValue(value x) { + writeTextValueFile(aFile, x); + y = readTextValueFile(aFile); + if (x != y) println(" != ???"); + return x == y; +} + +data Encoding + = utf8() + | utf16le() + | utf16be() + | utf16() + | utf32le() + | utf32be() + | utf32() + ; +map[Encoding, str] encodingNames + = ( + utf8() : "UTF-8", + utf16le() : "UTF-16LE", + utf16be() : "UTF-16BE", + utf16() : "UTF-16", + utf32le() : "UTF-32LE", + utf32be() : "UTF-32BE", + utf32() : "UTF-32" + ) ; + +test bool correctEncoding(Encoding enc, str content) { + content = removeZeroIAmbBOM(enc, content); + writeFileEnc(aFile, encodingNames[enc], content); + return readFileEnc(aFile, encodingNames[enc]) == content; +} + +test bool correctEncodingImplicit(Encoding enc, str content) { + content = removeZeroIAmbBOM(enc, content); + writeFileEnc(aFile, encodingNames[enc], content); + return readFile(aFile) == content; +} + +public str removeZeroIAmbBOM(Encoding enc, str s) { + if (size(s) > 0 && s[0] == "\a00" && (enc == utf16() || enc == utf16le())) { + return "\a01" + (size(s) > 1 ? s[1..] : ""); + } + return s; +} + +test bool appendWorksCorrectly(Encoding enc, str a, str b) { + a = removeZeroIAmbBOM(enc, a); + b = removeZeroIAmbBOM(enc, b); + writeFileEnc(aFile, encodingNames[enc], a); + appendToFileEnc(aFile, encodingNames[enc], b); + r = readFile(aFile); + if (r != a + b) { + println("a: "); + println("b: "); + println("e: "); + println("r: "); + } + return r == (a + b); +} + +test bool appendWorksCorrectlyImplicit(Encoding enc, str a, str b) { + a = removeZeroIAmbBOM(enc, a); + b = removeZeroIAmbBOM(enc, b); + writeFileEnc(aFile, encodingNames[enc], a); + appendToFile(aFile, b); + return readFile(aFile) == a + b; +} + +test bool readOffsetStart(str a, str b) { + if (a != "", b != "", size(a) + size(b) == size(a + b)) { + writeFileEnc(aFile, "UTF8", a + b); + return readFileEnc(aFile[offset = 0][length = size(a)], "utf8") == a; + } + return true; +} + +test bool readOffsetEnd(str a, str b) { + if (size(a) + size(b) == size(a + b)) { + writeFileEnc(aFile, "UTF8", a + b); + return readFileEnc(aFile[offset = size(a)][length = size(b)], "utf8") == b; + } + return true; +} + +test bool readOffsetMiddle(str a, str b, str c) { + if (size(a) == 0 || size(b) == 0) + return true; + if (size(a) + size(b) + size(c) == size(a + b + c)) { + writeFileEnc(aFile, "UTF8", a + b + c); + return readFileEnc(aFile[offset = size(a)][length = size(b)], "utf8") == b; + } + return true; +} + +test bool md5Hash() { + writeFileEnc( + |memory://test-tmp/basic-io-md5.txt|, + encodingNames[utf8()], + "abc\n123\n!@#$%\n" + ); + return + md5HashFile(|memory://test-tmp/basic-io-md5.txt|) == "931210fcfae2c4979e5d51a264648b82"; +} + +data Compression + = gzip() + //| Z() // read-only + | xz() + | bzip2() + | zstd() + //| lzma() //read-only + ; + +map[Compression, str] comprExtension + = (gzip() : "gz", + //Z() : "Z", + xz() : "xz", + bzip2() : "bz2", + //, + zstd() : "zst"//lzma() : "lzma" + ) ; + +@tries{100} +test bool compressionWorks(str a, Compression comp) { + targetFile = aFile[extension = aFile.extension + "." + comprExtension[comp]]; + targetFile = targetFile[scheme = "compressed+" + targetFile.scheme]; + writeFile(targetFile, a); + return readFile(targetFile) == a; +} + +@tries{100} +test bool compressionWorksWithEncoding( + str a, Compression comp, Encoding enc +) { + targetFile = aFile[extension = aFile.extension + "." + comprExtension[comp]]; + targetFile = targetFile[scheme = "compressed+" + targetFile.scheme]; + writeFileEnc(targetFile, encodingNames[enc], a); + return readFileEnc(targetFile, encodingNames[enc]) == a; +} + +@expected{PathNotFound} +test bool writeFileOffsetNonExistingFile() { + writeFile( + aFile[file = aFile.file + "invald"][offset = 0][length = 10], "Foobar" + ); + return false; +} + +@expected{PathNotFound} +test bool writeFileOffsetNonExistingFile2() { + writeFile( + aFile[file = aFile.file + "invald"][offset = 200][length = 10], "Foobar" + ); + return false; +} + +test bool writeFileOffsetEnd(Encoding enc, str a, str b) { + a = removeZeroIAmbBOM(enc, a); + b = removeZeroIAmbBOM(enc, b); + if (a == "" || b == "") { + return true; + } + writeFileEnc(aFile, encodingNames[enc], a); + l2 = aFile[offset = size(a)][length = 0]; + writeFileEnc(l2, encodingNames[enc], b); + return readFileEnc(aFile, encodingNames[enc]) == a + b; +} +test bool writeFileOffsetEndInvalidLength(Encoding enc, str a, str b) { + a = removeZeroIAmbBOM(enc, a); + b = removeZeroIAmbBOM(enc, b); + if (a == "" || b == "") { + return true; + } + writeFileEnc(aFile, encodingNames[enc], a); + l2 = aFile[offset = size(a)][length = 10]; + writeFileEnc(l2, encodingNames[enc], b); + return readFileEnc(aFile, encodingNames[enc]) == a + b; +} + +test bool writeFileOffsetEnd2(Encoding enc, str a, str b) { + a = removeZeroIAmbBOM(enc, a); + b = removeZeroIAmbBOM(enc, b); + if (a == "" || b == "") { + return true; + } + writeFileEnc(aFile, encodingNames[enc], a + b); + l2 = aFile[offset = size(a)][length = size(b)]; + writeFileEnc(l2, encodingNames[enc], b); + return readFileEnc(aFile, encodingNames[enc]) == a + b; +} + +test bool writeFileOffsetMiddle(Encoding enc, str a, str b) { + a = removeZeroIAmbBOM(enc, a); + b = removeZeroIAmbBOM(enc, b); + if (a == "" || b == "") { + return true; + } + writeFileEnc(aFile, encodingNames[enc], a + a); + l2 = aFile[offset = size(a)][length = size(a)]; + writeFileEnc(l2, encodingNames[enc], b); + return readFileEnc(aFile, encodingNames[enc]) == a + b; +} + +test bool writeFileOffsetMiddle2(Encoding enc, str a, str b) { + a = removeZeroIAmbBOM(enc, a); + b = removeZeroIAmbBOM(enc, b); + if (a == "" || b == "") { + return true; + } + writeFileEnc(aFile, encodingNames[enc], a + a); + l2 = aFile[offset = size(a)][length = 0]; + writeFileEnc(l2, encodingNames[enc], b); + return readFileEnc(aFile, encodingNames[enc]) == a + b + a; +} + +test bool writeFileOffsetStart(Encoding enc, str a, str b) { + a = removeZeroIAmbBOM(enc, a); + b = removeZeroIAmbBOM(enc, b); + if (a == "" || b == "") { + return true; + } + writeFileEnc(aFile, encodingNames[enc], a + a); + l2 = aFile[offset = 0][length = size(a)]; + writeFileEnc(l2, encodingNames[enc], b); + return readFileEnc(aFile, encodingNames[enc]) == b + a; +} + +test bool md5ValueTest() + = md5Hash("test") == "303b5c8988601647873b4ffd247d83cb"; + +// "test" as md5sum (yes, nested quotes) +test bool md5FileTest() { + writeFile(aFile, "test"); + return md5HashFile(aFile) == "098f6bcd4621d373cade4e832627b4f6"; +// test as md5sum +} + +test bool findResourcesWorks() = findResources("IO.rsc") != {}; + +test bool findResourcesFindNothingForRandomFiles(int a, int b) + = findResources("Foo_.rsc") == {}; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Integers.rsc| +module lang::rascal::tests::basic::Integers + +import Exception; + +@expected{ArithmeticException} +test bool divByZero(num x) = x / 0 == 0; + +test bool commAdd(int i, int j) = i + j == j + i; + +test bool commMul(int i, int j) = i * j == j * i; + +test bool assocAdd(int i, int j, int k) = (i + j) + k == i + (j + k); + +test bool assocMul(int i, int j, int k) = (i * j) * k == i * (j * k); + +test bool idemZero(int i) = 0 * i == 0; + +test bool neutralAddZero(int i) = 0 + i == i; + +test bool neutralMulOne(int i) = 1 * i == i; + +test bool neutralDivOne(int i) = i / 1 == i; + +test bool dualMulDiv(int i, int j) = j != 0 ==> ((i * j) / j == i); + +test bool MulDivMod(int i, int j) = j != 0 ==> (((i / j) * j) + (i % j) == i); + +test bool dualAddSub(int i, int j) = (i - j) + j == i && (i + j) - j == i; + +test bool distMulAdd(int i, int j, int k) = i * (j + k) == i * j + i * k; + +test bool lessGreater(int i, int j) = i <= j <==> j >= i; + +test bool less(int i, int j) = i < j <==> j > i; + +test bool greaterEqual(int i, int j) = i >= j <==> i == j || i > j; + +test bool transLess(int i, int j, int k) = (i < j && j < k) ==> i < k; + +test bool transEq(int i, int j, int k) = (i == j && j == k) ==> i == k; + +test bool reflexEq(int i) = i == i; + +test bool commEq(int i, int j) = i == j <==> j == i; + +test bool plusMinStable(int a, int b, int c, int d) = (a - b) + (c - d) == (a + c) - (b + d); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Intermix.rsc| +module lang::rascal::tests::basic::Intermix + +test bool simpler() { + l = [1, 1, 1]; + while([*pre, a, b, *pst] := l, !(a == 0 || b == 0)) { + l = [*pre, a, 0, b, *pst]; + } + return l == [1, 0, 1, 0, 1]; +} + +test bool iterOnly() = [a, 0 | [*_, a, _, *_] := [1, 2, 3]] == [1, 0, 2, 0]; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/IsDefined.rsc| +module lang::rascal::tests::basic::IsDefined + +import Exception; +import util::Math; +import List; + +// Strings +test bool isDefinedStr1() = ("abc"[0])?; + +test bool isDefinedStr2() = !("abc"[5])?; + +test bool isDefinedStr3() = ("abc"[-3])?; + +test bool isDefinedStr4() = !("abc"[-4])?; + +// Locations +test bool isDefinedLoc1() = |project://x/y.txt|(5, 4, <1, 5>, <1, 9>).begin?; +test bool isDefinedLoc2() = |project://x/y.txt|(5, 4, <1, 5>, <1, 9>).end?; +test bool isDefinedLoc3() + = !(|project://x/y.txt|(5, 4, <1, 5>, <1, 9>).host?); + +test bool isDefinedLoc4() = !(|std:///List.rsc|.ls?); +test bool isDefinedLoc5() = |std:///util|.ls?; + +// Lists +test bool isDefinedList1(list[int] L) = L == [] || L[arbInt(size(L))]?; + +test bool isDefinedList2(list[int] L) = !L[size(L) + 1]?; + +test bool isDefinedList3() = [0, 1, 2][-3]?; + +test bool isDefinedList4() = !([0, 1, 2][-4])?; + +test bool isDefinedList5() { + lst = [0, 1, 2]; + try { + lst[3]; + return false; + } + catch IndexOutOfBounds(_): { + return true; + } +} + +test bool isDefinedList6() { + lst = [0, 1, 2]; + return !lst[3]?; +} + +test bool isDefinedList7() { + int x = -1; + try { + x = [0, 1, 2, 3][2] ? 100; + } + catch IndexOutOfBounds(_): { + x = 200; + } + return x == 2; +} + +test bool isDefinedList8() { + int x = -1; + try { + x = [0, 1, 2, 3][5] ? 100; + } + catch IndexOutOfBounds(_): { + x = 200; + } + return x == 100; +} + +test bool isDefinedList9() { + list[int] lst = [0, 1, 2]; + lst[2] ? 0 += 1; + return lst[2] == 3; +} + +// Maps +test bool isDefinedMap1() = (0 : "0", + 1 : "1", + 2 : "2")[2]?; + +test bool isDefinedMap2() = !((0 : "0", + 1 : "1", + 2 : "2")[3])?; + +test bool isDefinedMap3() { + map[int, str] m = (0 : "0", + 1 : "1", + 2 : "2"); + try { + m[3]; + return false; + } + catch NoSuchKey(_): { + return true; + } +} + +test bool isDefinedMap4() { + map[int, str] m = (0 : "0", + 1 : "1", + 2 : "2"); + + return m[2]?; +} + +test bool isDefinedMap5() { + map[int, str] m = (0 : "0", + 1 : "1", + 2 : "2"); + + return !m[3]?; +} + +test bool isDefinedMap6() { + map[int, int] m = (0 : 10, + 1 : 20); + m[0] ? 0 += 1; + return m[0] == 11; +} + +test bool isDefinedMap7() { + map[int, int] m = (0 : 10, + 1 : 20); + m[3] ? 0 += 1; + return m[3] == 1; +} + +test bool isDefinedMap8() { + map[int, int] m = (0 : 10, + 1 : 20); + m[0] ?= 100; + return m[0] == 10; +} + +test bool isDefinedMap9() { + map[int, int] m = (0 : 10, + 1 : 20); + m[5] ?= 100; + return m[5] == 100; +} + +// Tuples +test bool isDefinedTuple1() { + return (<0, 1, 2>[1])?; +} + +test bool isDefinedTuple2() { + return (<0, 1, 2><1>)?; +} + +test bool isDefinedTuple3() { + tuple[int n, str s] tup = <0, "a">; + return tup.n?; +} + +@ignoreCompiler{Remove-after-transtion-to-compiler: Already detected by type checker: Field x does not exist on type tuple[int n, str s]} +@expected{UndeclaredField} +test bool isDefinedTuple4() { + tuple[int n, str s] tup = <0, "a">; + return !tup.x?; +} + +test bool hasTuple1() { + tuple[int n, str s] tup = <0, "a">; + return tup has n; +} + +test bool hasTuple2() { + tuple[int n, str s] tup = <0, "a">; + return !(tup has x); +} + +// Relation +test bool isDefinedRel1() { + return ({<1, "a" >, + <2, "b" >}[0])?; +} + +@ignoreCompiler{Remove-after-transtion-to-compiler: Already detected by type checker} +@expected{UnsupportedSubscriptArity} +test bool isDefinedRel2() { + return !({<1, "a" >, + <2, "b" >}[1, 2, 3])?; +} + +test bool hasRel1() { + rel[int n, str s] r = {<0, "a" >}; + return r has n; +} + +test bool hasRel2() { + rel[int n, str s] r = {<0, "a" >}; + return !(r has x); +} + +// ListRelation +test bool isDefinedLRel1() { + return ([<1, "a" >, + <2, "b" >][0])?; +} + +@ignoreCompiler{Remove-after-transtion-to-compiler: Already detected by type checker} +@expected{UnsupportedSubscriptArity} +test bool isDefinedLRel2() { + return !([<1, "a" >, + <2, "b" >][1, 2, 3])?; +} + +test bool hasLRel1() { + lrel[int n, str s] r = [<0, "a" >]; + return r has n; +} + +test bool hasLRel2() { + lrel[int n, str s] r = [<0, "a" >]; + return !(r has x); +} + +// ADT +data A = a(int x, int y, str s); + +test bool isDefinedADT1() { + return (a(1, 2, "abc")[0])?; +} + +test bool isDefinedADT2() { + return !(a(1, 2, "abc")[5])?; +} + +// node +test bool isDefinedNode1() { + return ("f"(0, 1, 2)[0])?; +} + +test bool isDefinedNode2() { + return !("f"(0, 1, 2)[5])?; +} + +test bool isDefinedNode3() = "aap"(noot = 1).noot?; + +test bool isDefinedNode4() = !("aap"(boot = 1).noot?); + +test bool hasNode1() = "aap"(noot = 1) has noot; + +test bool hasNode2() = !("aap"(boot = 1) has noot); + +test bool hasNode3() = !("aap"() has noot); + +@ignoreCompiler{INCOMPATIBILITY: ? has been restricted and works no longer on undefined variables} +test bool tst() { + int x = 10; + y = x ? 1; + return y == 10; +} + +// The status of unitialized variables is in transit +//test bool tst() { int x; y = x ? 1; return x == 1; } +// Annotations +data F + = f3() + | f3(int n) + | g(int n) + | deep(F f) + ; +anno + int + F + @ + pos +; + +test bool isDefinedAnno1() = (f3()[@pos = 1])@pos?; + +test bool isDefinedAnno2() = !(f3()@pos)?; + +test bool isDefinedAnno3() = ((f3()[@pos = 1])@pos ? 10) == 1; + +test bool isDefinedAnno4() = ((f3())@pos ? 10) == 10; + +test bool isDefinedAnno5() { + X = f3(); + X @ pos ? 0 += 1; + return X@pos == 1; +} + +test bool isDefinedAnno6() { + X = f3()[@pos = 1]; + X @ pos ? 0 += 1; + return X@pos == 2; +} + +test bool isDefinedAnno7() { + X = f3(); + X @ pos ?= 3; + return X@pos == 3; +} + +test bool isDefinedAnno8() { + X = f3()[@pos = 1]; + X @ pos ?= 3; + return X@pos == 1; +} + +test bool isDefinedAnno9() = f3()[@pos = 1] has pos; + +// TODO we can not tell this anymore since annotations are now simulated using keyword parameters. +// the keyword parameter "is" always there due to their semantics of having defaults.. +// test bool isDefinedAnno10() = !(f3() has pos); +// e has f : e is of an ADT type and its constructor has a positional or keyword field f. +// e[k]? : list or map contains given index k +// e.f? : e is a node or constructor that has a keyword field f with an explicitly set value +// e? : debatable whether we allow the general case +data F + = z( int l = 2) + | u() + ; + +test bool isDefined8() = !(u() has l); +test bool isDefined9() = !(u().l?); +test bool isDefined10() = !(z().l?); +test bool isDefined11() = z() has l; +test bool isDefined12() = z(l = 1).l?; + +test bool isDefined13() { + e = z(); + e.l ?= 3; + // set l to 3 if the field is not set, otherwise leave it + return e.l == 3; +} + +data D + = d1() + | d2(int n) + | d3(int n, str s = "abc") + ; + +@expected{NoSuchField} +test bool getADTField1() = d1().n == 0; + +@expected{NoSuchField} +test bool getADTField2() = d1().s == "abc"; + +test bool getADTField3() = d2(10).n == 10; + +@expected{NoSuchField} +test bool getADTField4() = d2(10).s == "abc"; + +test bool getADTField5() = d3(20).n == 20; +test bool getADTField6() = d3(20).s == "abc"; +test bool getADTField7() = d3(20, s = "def").n == 20; +test bool getADTField8() = d3(20, s = "def").s == "def"; +test bool getADTField9() = d3(20, s = "abc").n == 20; +test bool getADTField10() = d3(20, s = "abc").s == "abc"; + +test bool hasADT1() = !(d1() has n); +test bool hasADT2() = !(d1() has s); +test bool hasADT3() = d2(10) has n; +test bool hasADT4() = !(d2(10) has s); +test bool hasADT5() = d3(20) has n; +test bool hasADT6() = d3(20) has s; +test bool hasADT7() = d3(20, s = "def") has n; +test bool hasADT8() = d3(20, s = "def") has s; +test bool hasADT9() = d3(20, s = "abc") has n; +test bool hasADT10() = d3(20, s = "abc") has s; + +test bool isDefADTField1() = !d1().n?; +test bool isDefADTField2() = !d1().s?; +test bool isDefADTField3() = d2(10).n?; +test bool isDefADTField4() = !d2(10).s?; +test bool isDefADTField5() = d3(20).n?; +test bool isDefADTField6() = !d3(20).s?; +test bool isDefADTField7() = d3(20, s = "def").n?; +test bool isDefADTField8() = d3(20, s = "def").s?; +test bool isDefADTField9() = d3(20, s = "abc").n?; +test bool isDefADTField10() = d3(20, s = "abc").s?; + +test bool ifDefADTFieldOtherwise1() = 13 == (d1().n ? 13); +test bool ifDefADTFieldOtherwise2() = "xyz" == (d1().s ? "xyz"); +test bool ifDefADTFieldOtherwise3() = 10 == (d2(10).n ? 13); +test bool ifDefADTFieldOtherwise4() = "xyz" == (d2(10).s ? "xyz"); +test bool ifDefADTFieldOtherwise5() = 20 == (d3(20).n ? 13); +test bool ifDefADTFieldOtherwise6() = "xyz" == (d3(20).s ? "xyz"); +test bool ifDefADTFieldOtherwise7() = 20 == (d3(20, s = "def").n ? 13); +test bool ifDefADTFieldOtherwise8() = "def" == (d3(20, s = "def").s ? "xyz"); +test bool ifDefADTFieldOtherwise9() = 20 == (d3(20, s = "abc").n ? 13); +test bool ifDefADTFieldOtherwise10() = "abc" == (d3(20, s = "abc").s ? "xyz"); + +@ignoreCompiler{INCOMPATIBILITY: ? has been restricted and works no longer on undefined variables} +test bool undefinedVariable() = !undefined?; + +@ignoreCompiler{INCOMPATIBILITY: ? has been restricted and works no longer on undefined variables} +test bool definedVariable() { + int defined = 42; + return defined?; +} + +// Keyword parameters +bool fWithKwN1(int n = 1) = n?; + +test bool unsetKwParameterIsUndefined1() = !fWithKwN1(); +test bool setKwParameterIsDefined1() = fWithKwN1(n = 2); + +int fWithKwN2(int n = 1) = n? ? 10 : -10; + +test bool unsetKwParameterIsUndefined2() = fWithKwN2() == -10; +test bool setKwParameterIsDefined2() = fWithKwN2(n = 2) == 10; +// Potential generic rules to check: +// e has f => e.f is well defined +// !(e has f) => e.f. gives error +// e.f? => e has f +// !(e.f?) && !(e has f) => e.f gives error +// !(e.f?) && (e has field) => e.f. gives default value +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/ListRelations.rsc| +module lang::rascal::tests::basic::ListRelations + +import List; +import ListRelation; +import Type; + +// Operators +test bool product(list[&A] X, list[&B] Y) + = isEmpty(X) + ? isEmpty(X * Y) + : (isEmpty(Y) + ? isEmpty(X * Y) + : all(x <- X, x in domain(X * Y)) + && all(y <- Y, y in range(X * Y)) + && all( <- X * Y, x in X, y in Y)); + +test bool composition(lrel[int, str] X, lrel[str, int] Y) + = isEmpty(X) + ? isEmpty(X o Y) + : (isEmpty(Y) + ? isEmpty(X o Y) + : (isEmpty(X o Y) || all( <- X o Y, x in domain(X o Y), y in range(X o Y)))); + +test bool selection(lrel[&A fa, &B fb] X) + = domain(X) <= X.fa && range(X) <= X.fb && X.fa == X<0> && X.fb == X<1>; + +test bool \join(lrel[&A, &B] X, lrel[&B, &C, &D] Y) + = isEmpty(X) + ? isEmpty(X join Y) + : (isEmpty(Y) + ? isEmpty(X join Y) + : toSet((X join Y)<0,1>) == toSet(X) && toSet((X join Y)<2,3,4>) == toSet(Y)); + +// Note that all subscriptions are of the form X[{a}] to avoid that a is interpreted as an integer index. +test bool subscription1(lrel[&A, &B, &C] X) + = isEmpty(X) + || all(&A a <- domain(X), any(<&B b, &C c> <- X[{a}], in X)) + && all(<&A a, &B b, &C c> <- X, in X[{a}]); + +// Make sure that a single integer subscript is properly interpreted as a list index +test bool subscription2(lrel[int, str] X) = [X[i] | int i <- [0..size(X)]] == X; + +test bool tclosure(lrel[int, int] X) + = isEmpty(X) || X <= (X+) && squeeze((X+) + (X+) o X) == squeeze(X+); + +private list[&T] squeeze(list[&T] xs) = ( [] | (ix in it) ? it : it + [ix] | &T ix <- xs ); + +test bool rtclosure(lrel[int, int] X) + = isEmpty(X) + || X <= X* + && all(x <- (X*) o X, x in X*) + && all(x <- carrier(X), y <- carrier(X), in X*, in X*); + +// Library functions +private set[int] sample(lrel[int, int] X) { + c = carrier(X); + if (size(c) <= 2) + return {}; + = takeOneFrom(c); + = takeOneFrom(c); + return {r1, r2}; +} +test bool tst_carrier(lrel[int, int] X) + = isEmpty(X) || all( <- X, a in carrier(X), b in carrier(X)); + +test bool tst_carrierR(lrel[int, int] X) { + s = sample(X); + XR = carrierR(X, s); + return isEmpty(XR) || all( <- XR, a in s, b in s); +} + +test bool tst_carrierX(lrel[int, int] X) { + s = sample(X); + XR = carrierX(X, s); + return isEmpty(XR) || all( <- XR, a notin s, b notin s); +} + +test bool tst_complement(lrel[int, int] X) + = isEmpty(X) + || isEmpty(complement(X)) + || complement(X) <= domain(X) * range(X) + && all( <- complement(X), notin X); + +test bool tst_domain(lrel[int, int] X) + = isEmpty(X) + || all( <- X, a in domain(X)) + && all(c <- domain(X), any(<int x, int _> <- X, eq(x, c))); + +test bool tst_domainR(lrel[int, int] X) { + s = sample(X); + XR = domainR(X, s); + return isEmpty(XR) || all( <- XR, a in s); +} + +test bool tst_domainX(lrel[int, int] X) { + s = sample(X); + XR = domainX(X, s); + return isEmpty(XR) || all( <- XR, a notin s); +} + +test bool tst_ident(list[int] X) + = isEmpty(X) || all( <- ident(X), eq(a, b), a in X); + +test bool tst_invert(lrel[int, int] X) = invert(invert(X)) == X; + +test bool tst_range(lrel[int, int] X) + = isEmpty(X) + || all(<int _, int b> <- X, b in range(X)) + && all(int c <- range(X), any(<int _, int y> <- X, eq(y, c))); + +test bool tst_rangeR(lrel[int, int] X) { + s = sample(X); + XR = rangeR(X, s); + return isEmpty(XR) || all(<_, b> <- XR, b in s); +} + +test bool tst_rangeX(lrel[int, int] X) { + s = sample(X); + XR = rangeX(X, s); + return isEmpty(XR) || all(<_, b> <- XR, b notin s); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Lists.rsc| +module lang::rascal::tests::basic::Lists + +import List; +import ListRelation; +import Set; +import Boolean; +import util::Math; +import Type; +import String; +import ValueIO; + +// is A + B == C? +bool isConcat(list[&T] A, list[&T] B, list[&T] C) + = isEmpty(A) + ? C == B + : (isEmpty(B) + ? C == A + : (slice(C, 0, size(A)) == A && slice(C, size(A), size(C) - size(A)) == B)); + +test bool concat1(list[&T] A, list[&T] B) = isConcat(A, B, A + B); +test bool concat2(&T A, list[&T] B) = isConcat([A], B, [A] + B); +test bool concat3(list[&T] A, &T B) = isConcat(A, [B], A + [B]); + +bool isElemProperlyRemoved(&T x, list[&T] A, list[&T] B) + = x in A && (x notin B || freq(x, A) > freq(x, B)); + +// is A - B == C? +bool isDiff(list[&T] A, list[&T] B, list[&T] C) + = isEmpty(A) + ? isEmpty(C) + : (isEmpty(B) + ? C == A + : (isEmpty(C) + ? (isEmpty(B) || all(x <- B, freq(x, A) <= freq(x, B))) + : all(x <- C, isElemProperlyRemoved(x, A, B)))); + +test bool diff(list[&T] A, list[&T] B) = isDiff(A, B, A - B); + +// A == B? +/*TODO: reducer inside Boolean expression does not work in interpreter*/bool isEqual(list[&T] A, list[&T] B) { + if (size(A) == size(B)) { + for (int i <- [0..size(A)]) { + if (!eq(A[i], B[i])) + return false; + } + return true; + } + return false; +} + +//bool isEqual(list[&T] A, list[&T] B) = +// (size(A) == size(B)) ? (true | (it && (elementAt(A,i) == elementAt(B,i))) | int i <- index(A)) : false; +//bool isEqual(list[&T] A, list[&T] B) = +// size(A) == size(B) && (true | (it && (A[i] == B[i])) | int i <- index(A)); +test bool equal1(list[&T] A) = A == A; +test bool equal2(list[&T] A, list[&T] B) + = (A == B) ? isEqual(A, B) : !isEqual(A, B); + +test bool notEqual1(list[&T] A) = !(A != A); +test bool notEqual2(list[&T] A, list[&T] B) + = (A != B) ? !isEqual(A, B) : isEqual(A, B); + +// x in L? +bool isIn(&T x, list[&T] L) = ( false | it || eq(x, e) | e <- L ); + +// Frequency of x in L +int freq(&T x, list[&T] L) = ( 0 | eq(e, x) ? it + 1 : it | e <- L ); + +// Merge two lists, keeping their order +public list[&T] mergeOrdered(list[&T] A, list[&T] B) { + int i = 0; + int j = 0; + list[&T] res = []; + while(i < size(A) || j < size(B)) { + if (i < size(A) && arbBool()) { + res = res + [elementAt(A, i)]; + i += 1; + } + else + if (j < size(B)) { + res = res + [elementAt(B, j)]; + j += 1; + } + ; + } + ; + return res; +} + +// Merge two lists without keeping their order. +public list[&T] mergeUnOrdered(list[&T] A, list[&T] B) { + list[&T] res = []; + while(!(isEmpty(A) && isEmpty(B))) { + if (arbBool() && size(A) > 0) { + = takeOneFrom(A); + res = res + [x]; + } + else + if (size(B) > 0) { + = takeOneFrom(B); + res = res + [x]; + } + ; + } + return res; +} + +test bool inList(list[&T] A, list[&T] B) { + C = mergeUnOrdered(A, B); + return ( true | it && b in C | b <- B ); +} + +test bool notInList(list[&T] A, &T x) + = x in A ==> isIn(x, A) || x notin A ==> !isIn(x, A); + +test bool intersection(list[&T] A, list[&T] B) + = isEmpty(A & B) || all(x <- A & B, x in A, x in B); + +test bool lesseq(list[int] A, list[int] B) = A <= mergeOrdered(A, B); +test bool less(list[int] A, list[int] B) = isEmpty(B) || A < mergeOrdered(A, B); + +test bool greatereq(list[int] A, list[int] B) = mergeOrdered(A, B) >= A; +test bool greater(list[int] A, list[int] B) + = isEmpty(B) || mergeOrdered(A, B) > A; + +test bool splicing(list[&T] A, list[&T] B) + = [*A, *B] == A + B && [A, *B] == [A] + B && [*A, B] == A + [B]; + +test bool subscription(list[int] L) { + R = L; + for (int i <- index(L)) { + if (head(R) != L[i]) + return false; + R = tail(R); + } + return true; +} + +test bool subscriptionWrapped(list[int] L) { + for (int i <- index(L)) { + if (L[i] != L[i - size(L)]) { + return false; + } + } + return true; +} + +test bool sliceFirst(list[int] L) { + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + e = f + arbInt(size(L) - f); + S = L[f..e]; + return S == makeSlice(L, f, f + 1, e); +} + +test bool sliceFirst2(list[&T] L) { + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + S = L[f..]; + return S == [elementAt(L, i) | i <- [f..size(L)]]; +} + +// In an ideal world, this should work, but we have to adapt ranges first ... +//public list[int] makeSlice(list[int] L, int b, int s, int e){ +// return +// for(int i <- [b, s .. e]) +// append L[i]; +//} +public list[int] makeSlice(list[int] L, int f, int s, int e) { + res = []; + int i = f; + int delta = s - f; + if (delta == 0 || f == e) + return res; + if (f <= e) { + while(i >= 0 && i < size(L) && i < e) { + res = res + [elementAt(L, i)]; + i += delta; + } + } + else { + while(i >= 0 && i < size(L) && i > e) { + res = res + [elementAt(L, i)]; + i += delta; + } + } + return res; +} + +test bool sliceFirstSecond(list[int] L) { + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + incr = 2; + return L[f, f + incr..] == makeSlice(L, f, f + incr, size(L)); +} + +test bool sliceEmpty(int from, int to) = [][from % 32768..to % 32768] == []; + +test bool sliceOverEnd() = [0][1..] == []; + +test bool sliceEnd(list[int] L) { + if (isEmpty(L)) + return true; + e = arbInt(size(L)); + return L[..e] == makeSlice(L, 0, 1, e); +} + +test bool sliceSecondEnd(list[int] L) { + if (isEmpty(L)) + return true; + e = arbInt(size(L)); + incr = 2; + first = incr > e ? size(L) - 1 : 0; + return L[, incr..e] == makeSlice(L, first, incr, e); +} + +public tuple[int, int] arbFirstEnd(list[int] L) { + if (isEmpty(L)) + throw "No beging/end indices possible"; + if (size(L) == 1) + return <0, 0>; + f = arbInt(size(L)); + e = f + arbInt(size(L) - f); + return ; +} +test bool sliceFirstSecondEnd(list[int] L) { + if (isEmpty(L)) + return true; + = arbFirstEnd(L); + incr = 2; + return L[f, f + incr..e] == makeSlice(L, f, f + incr, e); +} + +test bool sliceFirstNegative(list[int] L) { + if (isEmpty(L)) + return true; + f = 1; + return L[-f..] == makeSlice(L, size(L) - f, size(L) - f + 1, size(L)); +} + +test bool sliceEndNegative(list[int] L) { + if (isEmpty(L)) + return true; + e = arbInt(size(L)); + return L[..-e] == makeSlice(L, 0, 1, e == 0 ? e : size(L) - e); +} + +test bool sliceFirstNegativeSecondNegative(list[int] L) { + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + incr = 2; + if (f == 0) + return L[0, -incr..] == makeSlice(L, 0, size(L) - incr, size(L)); + else + return L[-f, -(f + incr)..] == makeSlice(L, size(L) - f, size(L) - (f + incr), -1); +} + +test bool sliceSecondNegative(list[int] L) { + if (isEmpty(L)) + return true; + incr = 2; + S = L[, -incr..]; + return S == makeSlice(L, 0, size(L) - incr, size(L)); +} + +test bool sliceOutOfBoundIndex1() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + return L[..] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +} +test bool sliceOutOfBoundIndex2() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + return L[10..] == []; +} +test bool sliceOutOfBoundIndex3() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + return L[10..10] == []; +} +test bool sliceOutOfBoundIndex4() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + return L[10..-11] == [9, 8, 7, 6, 5, 4, 3, 2, 1]; +} +test bool sliceOutOfBoundIndex5() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + return L[-15..-11] == []; +} +test bool sliceOutOfBoundIndex6() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + return L[10..-5] == [9, 8, 7, 6]; +} + +test bool assignSlice1() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + .. + ] = [10, 20]; + return L == [10, 20, 10, 20, 10, 20, 10, 20, 10, 20]; +} +test bool assignSlice2() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + 2 + .. + ] = [10, 20]; + return L == [0, 1, 10, 20, 10, 20, 10, 20, 10, 20]; +} +test bool assignSlice3() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[2..6] = [10, 20]; + return L == [0, 1, 10, 20, 10, 20, 6, 7, 8, 9]; +} +test bool assignSlice4() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[8..3] = [10, 20]; + return L == [0, 1, 2, 3, 10, 20, 10, 20, 10, 9]; +} + +test bool assignStep1() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + , + 2 + .. + ] = [10]; + return L == [10, 1, 10, 3, 10, 5, 10, 7, 10, 9]; +} +test bool assignStep2() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + , + 2 + .. + ] = [10, 20]; + return L == [10, 1, 20, 3, 10, 5, 20, 7, 10, 9]; +} +test bool assignStep3() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + , + 2 + .. + ] = [10]; + return L == [10, 1, 10, 3, 10, 5, 10, 7, 10, 9]; +} +test bool assignStep4() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + , + 2 + .. + ] = [10, 20]; + return L == [10, 1, 20, 3, 10, 5, 20, 7, 10, 9]; +} +test bool assignStep5() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + , + 2 + .. + ] = [10, 20, 30]; + return L == [10, 1, 20, 3, 30, 5, 10, 7, 20, 9]; +} +test bool assignStep6() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + , + 2 + .. + ] = [10, 20, 30, 40, 50, 60, 70]; + return L == [10, 1, 20, 3, 30, 5, 40, 7, 50, 9, 60, 70]; +} + +test bool assignStep7() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + 2 + , + 4 + .. + ] = [10]; + return L == [0, 1, 10, 3, 10, 5, 10, 7, 10, 9]; +} +test bool assignStep8() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[2, 4..6] = [10]; + return L == [0, 1, 10, 3, 10, 5, 6, 7, 8, 9]; +} + +test bool assignStep9() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + , + 6 + .. + 1 + ] = [10]; + return L == [0, 1, 2, 10, 4, 5, 10, 7, 8, 10]; +} +test bool assignStep10() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + 8 + , + 6 + .. + ] = [10]; + return L == [10, 1, 10, 3, 10, 5, 10, 7, 10, 9]; +} +test bool assignStep11() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[8, 6..3] = [10]; + return L == [0, 1, 2, 3, 10, 5, 10, 7, 10, 9]; +} + +test bool assignStep12() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + -1 + , + -2 + .. + ] = [10, 20, 30, 40, 50]; + return L == [50, 40, 30, 20, 10, 50, 40, 30, 20, 10]; +} +test bool assignStep13() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + -1 + , + -3 + .. + ] = [10, 20, 30, 40, 50]; + return L == [0, 50, 2, 40, 4, 30, 6, 20, 8, 10]; +} + +@ignoreInterpreter{} +test bool assignAdd1() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + .. + ] += [10]; + return L == [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]; +} +@ignoreInterpreter{} +test bool assignAdd2() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + 2 + .. + ] += [10]; + return L == [0, 1, 12, 13, 14, 15, 16, 17, 18, 19]; +} +@ignoreInterpreter{} +test bool assignAdd3() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[2..6] += [10]; + return L == [0, 1, 12, 13, 14, 15, 6, 7, 8, 9]; +} +@ignoreInterpreter{} +test bool assignAdd4() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[8..3] += [10]; + return L == [0, 1, 2, 3, 14, 15, 16, 17, 18, 9]; +} + +@ignoreInterpreter{} +test bool assignSub1() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + .. + ] -= [10]; + return L == [-10, 1 - 10, 2 - 10, 3 - 10, 4 - 10, 5 - 10, 6 - 10, 7 - 10, 8 - 10, 9 - 10]; +} +@ignoreInterpreter{} +test bool assignSub2() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + 2 + .. + ] -= [10]; + return L == [0, 1, 2 - 10, 3 - 10, 4 - 10, 5 - 10, 6 - 10, 7 - 10, 8 - 10, 9 - 10]; +} +@ignoreInterpreter{} +test bool assignSub3() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[2..6] -= [10]; + return L == [0, 1, 2 - 10, 3 - 10, 4 - 10, 5 - 10, 6, 7, 8, 9]; +} +@ignoreInterpreter{} +test bool assignSub4() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[8..3] -= [10]; + return L == [0, 1, 2, 3, 4 - 10, 5 - 10, 6 - 10, 7 - 10, 8 - 10, 9]; +} + +@ignoreInterpreter{} +test bool assignProd1() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + .. + ] *= [10]; + return L == [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]; +} +@ignoreInterpreter{} +test bool assignProd2() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L [ + 2 + .. + ] *= [10]; + return L == [0, 1, 20, 30, 40, 50, 60, 70, 80, 90]; +} +@ignoreInterpreter{} +test bool assignProd3() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[2..6] *= [10]; + return L == [0, 1, 20, 30, 40, 50, 6, 7, 8, 9]; +} +@ignoreInterpreter{} +test bool assignProd4() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[8..3] *= [10]; + return L == [0, 1, 2, 3, 40, 50, 60, 70, 80, 9]; +} + +// TODO: add tests for /= and &= +test bool AssignFromEnd1() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[-1] = 90; + return L == [0, 1, 2, 3, 4, 5, 6, 7, 8, 90]; +} +test bool AssignFromEnd2() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[-2] = 80; + return L == [0, 1, 2, 3, 4, 5, 6, 7, 80, 9]; +} +test bool AssignFromEnd3() { + L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + L[-10] = 10; + return L == [10, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +} + +// Nested Lists +test bool AssignNestedList1() { + L = [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]; + L[1][2] = 50; + return L == [[0, 1, 2], [3, 4, 50], [6, 7, 8, 9]]; +} +test bool AccessNestedList1() { + L = [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]; + return L[1][2] == 5; +} +test bool AssignNestedList2() { + L = [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]; + L[-2][-1] = 50; + return L == [[0, 1, 2], [3, 4, 50], [6, 7, 8, 9]]; +} +test bool AccessNestedList2() { + L = [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]; + return L[-2][-1] == 5; +} + +// Library functions +test bool tstDelete(list[&T] L) { + if (size(L) > 1) { + n = arbInt(size(L)); + return delete(L, n) == L[..n] + ((n == size(L) - 1) ? [] : L[n + 1..]); + } + return true; +} + +// TODO: distribution +/* domain has been removed from List +test bool tstDomain(list[&T] L) = domain(L) == toSet([0..size(L)]); +*/test bool tstDrop(list[&T] L) { + if (size(L) > 1) { + n = arbInt(size(L)); + return drop(n, L) == (n < size(L) ? L[n..] : []); + } + return true; +} + +test bool tstDup(list[&T] L) { + // L = [{{{[<-121590445r651299473>]}},{},{{[]},{}}},{}]; + seen = {}; + d = for (e <- L) { + if (e notin seen) { + seen = seen + {e}; + append e; + } + } + ; + return d == dup(d); +} + +test bool tstGetOneFrom(list[&T] L) = isEmpty(L) || getOneFrom(L) in L; + +test bool tstHead(list[&T] L) = isEmpty(L) || eq(head(L), elementAt(L, 0)); + +test bool tstHeadN(list[&T] L) { + if (size(L) > 1) { + n = arbInt(size(L)); + return eq(head(L, n), L[..n]); + } + return true; +} + +test bool tstHeadTail(list[&T] L) + = isEmpty(L) || headTail(L) == ; + +test bool tstIndex(list[int] L) = (index(L) == [0..size(L)]); + +test bool tstIndexOf(list[int] L) { + int n = -1; + e = isEmpty(L) ? 0 : getOneFrom(L); + for (int i <- index(L)) { + if (eq(elementAt(L, i), e)) { + n = i; + break; + } + } + return eq(indexOf(L, e), n); +} + +test bool tstInsertAt(list[&T] L, &T e) { + if (isEmpty(L)) + return insertAt(L, 0, e) == [e]; + n = arbInt(size(L)); + return insertAt(L, n, e) == L[..n] + [e] + L[n..]; +} + +@ignoreCompiler{FIXME: breaks on the negative match} +test bool simplerIntercalateWithNegativeMatch() { + str ic(str sep: !"", list[value] l) + = "<for (e <- l) {><}>"[..-size(sep)]; + + return ic ( + ",", + [1, 2, 3] + ) == "1,2,3"; +} + +test bool tstIntercalate(str sep, list[value] L) { + if (sep == "" || L == []) + return true; + return + intercalate(sep, L) == "<for (int i <- [1..size(L)]) {><}>"; +} + +test bool tstIsEmpty(list[&T] L) = isEmpty(L) ? size(L) == 0 : size(L) > 0; + +test bool tstLast(list[&T] L) = isEmpty(L) || eq(last(L), elementAt(L, -1)); + +test bool tstLastIndexOf(list[int] L) { + int n = -1; + e = isEmpty(L) ? 0 : getOneFrom(L); + for (int i <- reverse(index(L))) { + if (eq(elementAt(L, i), e)) { + n = i; + break; + } + } + return lastIndexOf(L, e) == n; +} + +test bool tstMapper(list[int] L) { + int incr(int x) { + return x + 1; + } + ; + return mapper(L, incr) == [x + 1 | x <- L]; +} + +test bool tstMax(list[int] L) = isEmpty(L) || all(x <- L, max(L) >= x); + +test bool tstMerge(list[int] L, list[int] R) + = isSorted(merge(sort(L), sort(R))); + +test bool tstMin(list[int] L) = isEmpty(L) || all(x <- L, min(L) <= x); + +test bool tstMix(list[&T] L, list[&U] R) { + if (isEmpty(L)) + return mix(L, R) == R; + if (isEmpty(R)) + return mix(L, R) == L; + n = min(size(L), size(R)); + res + = [elementAt(L, i), elementAt(R, i) | i <- [0..n]] + + (size(L) > n ? L[n..] : []) + + (size(R) > n ? R[n..] : []); + return mix(L, R) == res; +} + +public int factorial(int n) = (n <= 0) ? 1 : n * factorial(n - 1); + +test bool tstPermutations(list[int] L) + = (size(L) == size({*L}) && size(L) < 8) + ==> (size(permutations(L)) <= factorial(size(L)) + && + all(P <- permutations(L), size(P) == size(L), isEmpty(L - P), isEmpty(P - L))); + +test bool tstPop(list[value] L) + = isEmpty(L) || pop(L) == ; + +test bool tstPrefix(list[value] L) = prefix(L) == (isEmpty(L) ? [] : L[..-1]); + +test bool tstPush(value elem, list[value] L) = push(elem, L) == [elem, *L]; + +test bool tstReverse(list[&T] L) = reverse(reverse(L)) == L; + +test bool tstSize(list[&T] L) = size(L) == ( 0 | it + 1 | _ <- L ); + +// TODO: slice +test bool tstSort(list[int] L) = isSorted(sort(L)); + +test bool tstSplit(list[&T] L) { + = split(L); + return L1 + L2 == L; +} + +test bool tstSum(list[int] L) = isEmpty(L) || sum(L) == ( 0 | it + x | x <- L ); + +test bool tstTail(list[&T] L) + = isEmpty(L) || (tail(L) == (size(L) == 1 ? [] : L[1..])); + +test bool tstTailN(list[&T] L) { + if (isEmpty(L)) + return true; + n = arbInt(size(L)); + return tail(L, n) == (n > 0 ? L[-n..] : []); +} + +test bool tstTake(list[&T] L) { + if (size(L) > 1) { + n = arbInt(size(L)); + return take(n, L) == L[..n]; + } + return true; +} + +test bool tstTakeOneFrom(list[int] L) { + if (size(L) > 1) { + = takeOneFrom(L); + return elem in L && (size(R) == size(L) - 1) && (toSet(L) == toSet(R) + elem); + } + return true; +} + +bool isEven(int a) = a mod 2 == 0; + +list[int] takeEven(list[int] L) + = (isEmpty(L) || !isEven(elementAt(L, 0))) + ? [] + : elementAt(L, 0) + takeEven(size(L) == 1 ? [] : L[1..]); + +test bool tstTakeWhile(list[int] L) { + return takeWhile(L, isEven) == takeEven(L); +} + +test bool tstToMapUnique(list[tuple[&A, &B]] L) + = (size(L<0>) == size(toSet(domain(L)))) + ==> (toMapUnique(L) == toMapUnique(toSet(L))); + +test bool tstTop(list[&T] L) = isEmpty(L) || eq(top(L), elementAt(L, 0)); + +test bool tstToRel(list[&T] L) + = isEmpty(L) + || toRel(L) == {| i <- [0..size(L) - 1]}; + +test bool tstToSet(list[&T] L) = toSet(L) == {x| x <- L}; + +@ignore{not all values can be read-back after writing to string} +test bool tstToString(list[value] L) + = (readTextValueString(#list[value], toString(L)) == L); + +test bool tstUnzip2(list[tuple[&A, &B]] L) + = unzip2(L) == <[a | <- L], [b | <_, b> <- L]>; + +test bool tstUnzip3(list[tuple[&A, &B, &C]] L) + = isEmpty(L) + || unzip3(L) == <[a | <- L], [b | <_, b, _> <- L], [c | <_, _, c> <- L]>; + +test bool tstUpTill(int n) = n < 0 || n > 10000 || upTill(n) == [0..n]; + +test bool tstZip(list[&T] L) = zip2(L, L) == [ | x <- L]; + +// Tests that check the correctness of the dynamic types of lists produced by the library functions; +// incorrect dynamic types make pattern matching fail; +test bool dtstSlice(list[&T] lst) { + if (isEmpty(lst)) + return true; + int b = 0; + if (size(lst) != 1) + b = arbInt(size(lst) - 1); + int len = arbInt(size(lst) - b); + if (len == 0) + return true; + lhs = slice(lst, b, len); + rhs = lst[b..(len + b)]; + return lhs == rhs && typeOf(lhs) == typeOf(rhs); +} + +test bool dtstDelete(list[&T] lst) { + if (isEmpty(lst)) + return true; + int index = 0; + if (size(lst) != 1) + index = arbInt(size(lst) - 1); + lhs = delete(lst, index); + rhs = [lst[i] | int i <- [0..size(lst)], i != index]; + return lhs == rhs && typeOf(lhs) == typeOf(rhs); +} + +test bool dtstDrop(list[&T] lst) { + if (isEmpty(lst)) + return true; + int n = 0; + if (size(lst) != 1) + n = arbInt(size(lst) - 1); + lhs = drop(n, lst); + rhs = [lst[i] | int i <- [n..size(lst)]]; + return lhs == rhs && typeOf(lhs) == typeOf(rhs); +} + +test bool dtstHead(list[&T] lst) { + if (isEmpty(lst)) + return true; + int n = 0; + if (size(lst) != 1) + n = arbInt(size(lst) - 1); + if (n == 0) + return true; + lhs = head(lst, n); + rhs1 = [lst[i] | int i <- [0..n]]; + rhs2 = take(n, lst); + return + lhs == rhs1 + && lhs == rhs2 + && typeOf(lhs) == typeOf(rhs1) + && typeOf(lhs) == typeOf(rhs2); +} + +test bool dtstTail(list[&T] lst) { + if (isEmpty(lst)) + return true; + int n = 0; + if (size(lst) != 1) + n = arbInt(size(lst) - 1); + if (n == 0) + return true; + lhs = tail(lst, n); + rhs = [lst[i] | int i <- [(size(lst) - n)..size(lst)]]; + return lhs == rhs && typeOf(lhs) == typeOf(rhs); +} + +test bool dtstPrefix(list[&T] lst) { + if (isEmpty(lst) || size(lst) == 1) + return true; + lhs = prefix(lst); + rhs = [lst[i] | int i <- [0..(size(lst) - 1)]]; + return lhs == rhs && typeOf(lhs) == typeOf(rhs); +} + +test bool dtstDifference(list[&T] lst) { + if (isEmpty(lst)) + return true; + for (&T elem <- lst) { + bool deleted = false; + lhs = lst - [elem]; + + // TODO: the following expression cannot be coorectly translated by the compiler + //rhs = [ *( (eq(elem,el) && !deleted) ? { deleted = true; []; } : [ el ]) | &T el <- lst ]; + rhs = for (&T el <- lst) { + if (eq(elem, el) && !deleted) { + deleted = true; + } + else { + append el; + } + } + if (!eq(lhs, rhs) || typeOf(lhs) != typeOf(rhs)) { + throw "Removed from resulted in instead of "; + } + } + return true; +} + +test bool dtstIntersection(list[&T] lst) { + if (isEmpty(lst)) + return true; + bool check = true; + for ([*l1, *l2] := lst) { + lhs1 = lst & l1; + rhs1 = [el | &T el <- lst, el in l1]; + lhs2 = lst & l2; + rhs2 = [el | &T el <- lst, el in l2]; + check + = check + && lhs1 == rhs1 + && typeOf(lhs1) == typeOf(rhs1) + && lhs2 == rhs2 + && typeOf(lhs2) == typeOf(rhs2); + } + return check; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Locations.rsc| +module lang::rascal::tests::basic::Locations + +import String; +import List; +import Set; +import Relation; +import ListRelation; +import IO; +import util::Math; +import Location; +import util::FileSystem; + +int singleChar(str s) = charAt(s, 0); + +list[int] makeValidSchemeChars() + = [singleChar("a")..singleChar("z")] + + [singleChar("A")..singleChar("Z")] + + [singleChar("0")..singleChar("9")] + + [singleChar("+"), singleChar("-"), singleChar(".")]; + +list[int] validSchemeChars + = [singleChar("a")..singleChar("z")] + + [singleChar("A")..singleChar("Z")] + + [singleChar("0")..singleChar("9")] + + [singleChar("+"), singleChar("-"), singleChar(".")] ; + +str createValidScheme(str s) { + if (s == "") + return "a"; + return + ( "a" + | it + stringChar(validSchemeChars[c % size(validSchemeChars)]) + | c <- chars(s) + ); +} + +@expected{MalFormedURI} +test bool noOpaqueURI2() = loc _ := |home:://this:is:opaque|; + +test bool canChangeScheme1(loc l, str s) + = (l[scheme = createValidScheme(s)]).scheme == createValidScheme(s); +test bool canChangeScheme2(loc l, str s) { + l.scheme = createValidScheme(s); + return l.scheme == createValidScheme(s); +} + +test bool canChangeAuthority1(loc l, str s) = (l[authority = s]).authority == s; +test bool canChangeAuthority2(loc l, str s) { + l.authority = s; + return l.authority == s; +} + +str fixPath(str s) = visit(s) { + case /\/\/+/ => "/" + }; + +test bool canChangePath1(loc l, str s) + = (l[path = s]).path == fixPath(startsWith(s, "/") ? s : "/" + s); +test bool canChangePath2(loc l, str s) { + l.path = s; + return l.path == fixPath(startsWith(s, "/") ? s : "/" + s); +} + +test bool canChangeQuery1(loc l, str s) = (l[query = s]).query == s; +test bool canChangeQuery2(loc l, str s) { + l.query = s; + return l.query == s; +} + +test bool canChangeFragment1(loc l, str s) = (l[fragment = s]).fragment == s; +test bool canChangeFragment2(loc l, str s) { + l.fragment = s; + return l.fragment == s; +} + +list[int] validHostChars + = (validSchemeChars - [singleChar("+"), singleChar(".")]) ; +str createValidHost(str s) { + if (s == "") + return "a"; + return + ( "a.a" + | it + stringChar(validHostChars[c % size(validHostChars)]) + | c <- chars(s) + ) + + "a.com"; +} + +test bool canChangeHost1(loc l, str s) + = (l[scheme = "http"][authority = "a"][host = createValidHost(s)]).host == createValidHost(s); +test bool canChangeHost2(loc l, str s) { + l.scheme = "http"; + l.authority = "a"; + l.host = createValidHost(s); + return l.host == createValidHost(s); +} + +test bool canChangeUser1(loc l, str s) + = contains(s, "@") + || (l[scheme = "http"][authority = "a@a.com"][user = s]).user == s; +test bool canChangeUser2(loc l, str s) { + if (contains(s, "@")) + return true; + l.scheme = "http"; + l.authority = "a@a.com"; + l.user = s; + if (l.user == s) { + return true; + } + else { + println(" != "); + return false; + } +} + +test bool validURIAuthority(loc l, str s) = l[authority = s].uri != ""; +test bool validURIPath(loc l, str s) = l[path = s].uri != ""; +test bool validURIQuery(loc l, str s) = l[query = s].uri != ""; +test bool validURIFragment(loc l, str s) = l[fragment = s].uri != ""; + +str fixPathAddition(str s) = replaceAll(s, "/", ""); + +test bool pathAdditions1(list[str] ss) + = ( |tmp:///ba| | it + t | s <- ss, t := fixPathAddition(s), t != "" ).path == ( "/ba" | it + "/" + t | s <- ss, t := fixPathAddition(s), t != "" ); + +test bool pathAdditions2(loc l, str s) + = s == "" + || (l + fixPathAddition(s)).path == ((endsWith(l.path, "/") ? l.path : l.path + "/") + fixPathAddition(s)); + +test bool testParent(loc l, str s) + = s == "" || ((l + replaceAll(s, "/", "_")).parent + "/") == (l[path = l.path] + "/"); +test bool testWindowsParent(str s) + = s == "" || (|file:///c:/| + replaceAll(s, "/", "_")).parent == |file:///c:/|; +test bool testFile(loc l, str s) { + s = replaceAll(s, "/", "_"); + if (s == "") + return true; + return (l + s).file == s; +} + +test bool supportSquareBraces(loc l) { + newAuth = l.authority + "]"; + newL = l[authority = newAuth]; + stringable = ""; + return newL.authority == newAuth; +} + +test bool noFile() = |tmp://X|.file == ""; +test bool rootPath() = |tmp://X|.path == "/"; +test bool rootPath3() = |tmp:///|.path == "/"; +test bool rootPath4() = |tmp://X/|.path == "/"; + +test bool top0(loc x) = x.top == x.top.top; +test bool top1(loc x) = "" == "|" + x.uri + "|"; +test bool top2(loc x) = toLocation(x.uri) == x.top; + +@ignore +test bool splicePathEncoded() + = str x := " " && |tmp:///< x >.rsc| == |tmp:///| + ".rsc"; +@ignore +test bool spliceArbPathEncoded(str x) + = |tmp:///< x >.rsc| == |tmp:///| + ".rsc"; + +test bool enclosingTest1() + = |tmp:///x.src|(5, 10, <0, 0>, <0, 0>) < |tmp:///x.src|(2, 20, <0, 0>, <0, 0>); +test bool enclosingTest2() + = |tmp:///x.src|(5, 10, <0, 0>, <0, 0>) <= |tmp:///x.src|(2, 20, <0, 0>, <0, 0>); +test bool enclosingTest3() + = |tmp:///x.src|(5, 10, <0, 0>, <0, 0>) <= |tmp:///x.src|(5, 10, <0, 0>, <0, 0>); +test bool enclosingTest4() + = |tmp:///x.src|(5, 10, <1, 2>, <1, 12>) <= |tmp:///x.src|(5, 10, <0, 0>, <0, 0>); +test bool enclosingTest5() + = |tmp:///x.src|(5, 10, <0, 0>, <0, 0>) <= |tmp:///x.src|(5, 10, <1, 2>, <1, 12>); +test bool enclosingTest6() + = !(|tmp:///x.src|(4, 11, <0, 0>, <0, 0>) <= |tmp:///x.src|(5, 10, <0, 0>, <0, 0>)); +test bool enclosingTest7() + = !(|tmp:///x.src|(4, 11, <0, 0>, <0, 0>) <= |tmp:///x.src|(5, 11, <0, 0>, <0, 0>)); +test bool enclosingTest8() + = !(|tmp:///x.src|(4, 11, <0, 0>, <0, 0>) <= |tmp:///x.src|(4, 10, <0, 0>, <0, 0>)); +test bool enclosingTest9() + = !(|tmp:///x.src|(4, 11, <0, 0>, <0, 0>) <= |tmp:///x.src|(4, 10, <0, 0>, <0, 0>)); + +test bool offSetLengthEnclosing( + int aOffset, int aLength, int bOffset, int bLength +) + = (abs(aOffset) < toInt(pow(2, 31)) + && + abs(aOffset) + abs(aLength) < toInt(pow(2, 31)) + && + abs(bOffset) < toInt(pow(2, 31)) + && + abs(bOffset) + abs(bLength) < toInt(pow(2, 31)) + && + abs(aOffset) >= abs(bOffset) + && + abs(aOffset) <= abs(bOffset) + abs(bLength) + && + abs(aOffset) + abs(aLength) <= abs(bOffset) + abs(bLength)) + ==> |tmp:///x.rsc|(abs(aOffset), abs(aLength), <0, 0>, <0, 0>) <= |tmp:///x.rsc|(abs(bOffset), abs(bLength), <0, 0>, <0, 0>); + +// Simulate a list of 1000 lines each of length < 1000; +public list[int] lineSizes = [arbInt(1000) | int _ <- [1..1000]] ; + +public int maxIndex = ( 0 | it + lineSizes[i] | int i <- index(lineSizes) ) ; + +// Turn an index in the above list into a line/column pair +tuple[int line, int column] getLineAndColumn(int idx) { + int pos = 0; + + for (int i <- index(lineSizes)) { + if (pos + lineSizes[i] >= idx) + return ; + pos += lineSizes[i]; + } + throw "getLineAndColumn: out of range [0 .. ]"; +} + +// Build a location for an area from index f to index t. +loc buildLoc(int f, int t, str base = "base.src") { + return + |test:///< base >|(f, t - f, getLineAndColumn(f), getLineAndColumn(t)); +} + +// Restrict i to legal index values +int restrict(int i) { + if (i < 0) + i = -i; + if (i > maxIndex) + return arbInt(maxIndex); + return i; +} + +// Get a location from index f to index t. +loc getLoc(int f, int t, str base = "base.src") { + f = restrict(f); + t = restrict(t); + return (f <= t) ? buildLoc(f, t, base = base) : buildLoc(t, f, base = base); +} + +// Test the comparison operators +test bool less1(int f1, int t1, int f2, int t2) { + l1 = getLoc(f1, t1); + l2 = getLoc(f2, t2); + return + l1.offset > l2.offset + && + l1.offset + l1.length <= l2.offset + l2.length + || + l1.offset >= l2.offset + && + l1.offset + l1.length < l2.offset + l2.length + ? l1 < l2 + : !(l1 < l2); +} + +test bool less2(int f, int t) { + f = restrict(f); + t = restrict(t); + l1 = getLoc(f, t, base = "base1.src"); + l2 = getLoc(f, t, base = "base2.src"); + return l1 < l2; +// path is lexicographically less, other attributes are equal +} + +test bool lessequal1(int f1, int t1, int f2, int t2) { + l1 = getLoc(f1, t1); + l2 = getLoc(f2, t2); + return + l1 == l2 + || + l1.offset > l2.offset + && + l1.offset + l1.length <= l2.offset + l2.length + || + l1.offset >= l2.offset + && + l1.offset + l1.length < l2.offset + l2.length + ? l1 <= l2 + : !(l1 <= l2); +} + +test bool lessequal2(int f, int t) { + f = restrict(f); + t = restrict(t); + l1 = getLoc(f, t, base = "base1.src"); + l2 = getLoc(f, t, base = "base2.src"); + return l1 <= l2; +// path is lexicographically less, other attributes are equal +} + +@ignoreCompiler{FIXME} +test bool greater1(int f1, int t1, int f2, int t2) { + l1 = getLoc(f1, t1); + l2 = getLoc(f2, t2); + return + l1.offset < l2.offset + && + l1.offset + l1.length >= l2.offset + l2.length + || + l1.offset <= l2.offset + && + l1.offset + l1.length > l2.offset + l2.length + ? l1 > l2 + : !(l1 > l2); +} + +test bool greater2(int f, int t) { + f = restrict(f); + t = restrict(t); + l1 = getLoc(f, t, base = "base1.src"); + l2 = getLoc(f, t, base = "base2.src"); + return !(l1 > l2); +} + +@ignoreCompiler{FIXME} +test bool greaterequal1(int f1, int t1, int f2, int t2) { + l1 = getLoc(f1, t1); + l2 = getLoc(f2, t2); + return + l1 == l2 + || + l1.offset < l2.offset + && + l1.offset + l1.length >= l2.offset + l2.length + || + l1.offset <= l2.offset + && + l1.offset + l1.length > l2.offset + l2.length + ? l1 >= l2 + : !(l1 >= l2); +} + +test bool greaterequal2(int f, int t) { + f = restrict(f); + t = restrict(t); + l1 = getLoc(f, t, base = "base1.src"); + l2 = getLoc(f, t, base = "base2.src"); + return !(l1 >= l2); +} + +test bool equal1(int f, int t) { + f = restrict(f); + t = restrict(t); + l1 = getLoc(f, t); + l2 = getLoc(f, t); + return l1 == l2; +} + +test bool equal2(int f, int t) { + f = restrict(f); + t = restrict(t); + l1 = getLoc(f, t, base = "base1.src"); + l2 = getLoc(f, t, base = "base2.src"); + return !(l1 == l2); +} + +// Create a list of n different locations +list[loc] getLocs(int n) { + locs = [getLoc(arbInt(maxIndex), arbInt(maxIndex)) | int _ <- [0..n]]; + return + [locs[i] | int i <- [0..n], !any(int j <- [0..n], i != j, locs[i] == locs[j])]; +} + +// Use loc in a set +test bool locInSet() { + locs = getLocs(100); + return size(locs) == size(toSet(locs)); +} + +// Use loc in a map +test bool locInMap() { + locs = getLocs(100); + m = (locs[i]: locs[i].offset| int i <- index(locs)); + return all(k <- m, m[k] == k.offset); +} + +// Use loc in a relation +test bool locInRel() { + locs = getLocs(100); + m = {| int i <- index(locs)}; + return all(k <- domain(m), m[k] == {k.offset}); +} + +// Use loc in a list relation +test bool locInLRel() { + locs = getLocs(100); + m = [ | int i <- index(locs)]; + return all(k <- domain(m), m[k] == [k.offset]); +} + +// Locations library +// Make two locations with a gap inbetween. When gap is negative they will overlap. +// Always return a pair of locations where l1 starts before l2. +tuple[loc, loc] makeLocsWithGap(int gap) { + sign = gap > 0 ? 1 : -1; + absgap = min(abs(gap), maxIndex / 2); + m1 = 1 + arbInt(maxIndex - absgap - 2); + // 1 <= m1 <= maxIndex - 2 + m2 = m1 + sign * absgap; + + llen = arbInt(m1); + l = getLoc(m1 - llen, m1); + + rlen = m2 == maxIndex ? 0 : arbInt(maxIndex - m2); + r = getLoc(m2, m2 + rlen); + + if (l.offset == r.offset && r.length == 0) { + return ; + } + else if (l.offset >= r.offset) { + return ; + } + else { + return ; + } +} + +bool report(loc l1, loc l2, bool expected) { + if (!expected) { + println("Not expected: , "); + return false; + } + return true; +} + +// isLexicallyLess +bool isLexicallyLess1(int f, int t) { + l1 = getLoc(f, t, base = "base1.src"); + l2 = getLoc(f, t, base = "base2.src"); + return report(l1, l2, isLexicallyLess(l1, l2)); +} + +bool isLexicallyLess1(int _) { + = makeLocsWithGap(10); + return report(l1, l2, isLexicallyLess(l1, l2)); +} + +test bool isSameFile1() { + l = |C:///a|; + r = |C:///a#1|; + return isSameFile(l, r); +} + +test bool isSameFile2() { + l = |C:///a|; + r = |C:///b#1|; + return !isSameFile(l, r); +} + +test bool isSameFile3(loc l) { + return isSameFile(l, l); +} + +test bool isSameFile4(loc l) { + return !isSameFile(l[scheme = "A"], l[scheme = "B"]); +} + +test bool isSameFile5(loc l) { + return !isSameFile(l[authority = "A"], l[authority = "B"]); +} + +// isContainedIn +test bool isContainedIn1(int f, int len) { + f1 = restrict(f); + t1 = restrict(f1 + len); + len1 = t1 - f1; + delta = (t1 - f1) / 2; + l1 = getLoc(f1, t1); + l2 = getLoc(f1 + delta, t1 - delta); + return report(l1, l2, delta > 0 ==> isContainedIn(l2, l1)); +} + +test bool isContainedIn2(int f, int len) { + f1 = restrict(f); + t1 = restrict(f1 + len); + len1 = t1 - f1; + delta = (t1 - f1) / 2; + l1 = getLoc(f1, t1, base = "base1.src"); + l2 = getLoc(f1 + delta, t1 - delta, base = "base2.src"); + return report(l1, l2, !isContainedIn(l2, l1)); +} + +// isStrictlyContainedIn +test bool isStrictlyContainedIn1(int f, int len) { + f1 = restrict(f); + t1 = restrict(f1 + len); + len1 = t1 - f1; + delta = (t1 - f1) / 2; + l1 = getLoc(f1, t1); + l2 = getLoc(f1 + delta, t1 - delta); + return report(l1, l2, delta > 0 ==> isStrictlyContainedIn(l2, l1)); +} + +test bool isStrictlyContainedIn2(int f, int len) { + f1 = restrict(f); + t1 = restrict(f1 + len); + len1 = t1 - f1; + delta = (t1 - f1) / 2; + l1 = getLoc(f1, t1); + l2 = getLoc(f1, t1 - delta); + return report(l1, l2, delta > 0 ==> isStrictlyContainedIn(l2, l1)); +} + +test bool isStrictlyContainedIn3(int f, int len) { + f1 = restrict(f); + t1 = restrict(f1 + len); + l1 = getLoc(f1, t1); + return report(l1, l1, !isStrictlyContainedIn(l1, l1)); +} + +// beginsBefore +@ignore{unknown} +test bool beginsBefore1(int _) { + = makeLocsWithGap(-10); + return report(l1, l2, beginsBefore(l1, l2)); +} + +test bool beginsBefore2(int _) { + = makeLocsWithGap(10); + return report(l1, l2, !beginsBefore(l2, l1)); +} + +// isBefore +test bool isBefore1(int _) { + = makeLocsWithGap(10); + return report(l1, l2, isBefore(l1, l2)); +} + +test bool isBefore2(int _) { + = makeLocsWithGap(10); + return report(l1, l2, !isBefore(l2, l1)); +} + +// isImmediatelyBefore +@ignore{Fails intermittently} +test bool isImmediatelyBefore1(int _) { + = makeLocsWithGap(0); + return report(l1, l2, isImmediatelyBefore(l1, l2)); +} + +@ignore{Fails intermittently} +test bool isImmediatelyBefore2(int _) { + = makeLocsWithGap(0); + return report(l1, l2, !isImmediatelyBefore(l2, l1)); +} + +// beginsAfter +@ignore{Until #1693 has been solved} +test bool beginsAfter1(int _) { + = makeLocsWithGap(-10); + return report(l1, l2, beginsAfter(l2, l1)); +} + +// isAfter +test bool isAfter1(int _) { + = makeLocsWithGap(10); + return report(l1, l2, isAfter(l2, l1)); +} + +// isImmediatelyAfter +@ignore{Fails intermittently} +test bool isImmediatelyAfter1(int _) { + = makeLocsWithGap(0); + return report(l1, l2, isImmediatelyAfter(l2, l1)); +} + +// isOverlapping +test bool isOverlapping1(int _) { + = makeLocsWithGap(-1); + return report(l1, l2, isOverlapping(l1, l2)); +} + +test bool isOverlapping2(int _) { + = makeLocsWithGap(10); + return !isOverlapping(l1, l2); +} + +test bool isOverlapping3() + = isOverlapping(|unknown:///|(0, 2), |unknown:///|(0, 2)); +test bool isOverlapping4() + = isOverlapping(|unknown:///|(0, 2), |unknown:///|(1, 2)); +test bool isOverlapping5() + = !isOverlapping(|unknown:///|(0, 2), |unknown:///|(2, 2)); +test bool isOverlapping6() + = isOverlapping(|unknown:///|(1, 2), |unknown:///|(0, 2)); +test bool isOverlapping7() + = isOverlapping(|unknown:///|(1, 2), |unknown:///|(1, 2)); +test bool isOverlapping8() + = isOverlapping(|unknown:///|(1, 2), |unknown:///|(2, 2)); +test bool isOverlapping9() + = !isOverlapping(|unknown:///|(2, 2), |unknown:///|(0, 2)); +test bool isOverlapping10() + = isOverlapping(|unknown:///|(2, 2), |unknown:///|(1, 2)); +test bool isOverlapping11() + = isOverlapping(|unknown:///|(2, 2), |unknown:///|(2, 2)); + +// cover +test bool isCover1(int _) { + = makeLocsWithGap(10); + u = cover([l1, l2]); + return report(l1, l2, isContainedIn(l1, u) && isContainedIn(l2, u)); +} + +test bool isCover2(int _) { + = makeLocsWithGap(-10); + u = cover([l1, l2]); + return report(l1, l2, isContainedIn(l1, u) && isContainedIn(l2, u)); +} + +test bool isCover3(int f, int t) { + f = restrict(f); + t = restrict(t); + l = getLoc(f, t); + u = cover([l, l, l, l]); + return report(l, l, l == u); +} + +test bool trailingSlashFile1() { + withSlash = |project://rascal/src/org/rascalmpl/library/|; + withoutSlash = |project://rascal/src/org/rascalmpl/library|; + + return withSlash.file == withoutSlash.file; +} + +test bool trailingSlashFile2() { + withSlash = |project://rascal/src/org/rascalmpl/library/|; + withoutSlash = |project://rascal/src/org/rascalmpl/library|; + + withoutSlash.file = "libs"; + withSlash.file = "libs"; + + return + withSlash.file == withoutSlash.file + && withSlash.parent == withoutSlash.parent; +} + +test bool testRelativize() + = relativize(|file:///a/b|, |file:///a/b/c.txt|) == |relative:///c.txt|; + +test bool testFailedRelativize() + = relativize(|file:///b/b|, |file:///a/b/c.txt|) == |file:///a/b/c.txt|; + +test bool trailingSlashRelativize1() + = relativize(|file:///library/|, |file:///library|) == relativize(|file:///library/|, |file:///library/|); + +test bool trailingSlashRelativize2() + = relativize(|file:///library|, |file:///library/|) == relativize(|file:///library|, |file:///library|); + +test bool extensionSetWithMoreDots1() + = |file:///a.txt/b|[extension = "aap"] == |file:///a.txt/b.aap|; + +test bool extensionSetWithMoreDots2() + = |file:///a.txt/b.noot|[extension = "aap"] == |file:///a.txt/b.aap|; + +test bool extensionSetWithSlash() + = |file:///a/b.noot/|[extension = "aap"] == |file:///a/b.aap/|; + +test bool extensionSetWithSlashAndMoreDots() + = |file:///a.txt/b.noot/|[extension = "aap"] == |file:///a.txt/b.aap/|; + +test bool extensionGetWithMoreDot1() = |file:///a.txt/b|.extension == ""; + +test bool extensionGetWithMoreDots2() + = |file:///a.txt/b.noot|.extension == "noot"; + +test bool extensionGetWithSlash() + = |file:///a/b.noot/|.extension == "noot"; + +test bool extensionGetSimple() = |file:///a/b.noot|.extension == "noot"; + +test bool extensionGetRoot() = |file:///b.noot|.extension == "noot"; + +test bool extensionGetNoRoot() = |file:///b|.extension == ""; + +test bool extensionNoPath() = |file:///|.extension == ""; + +test bool extensionSetRoot() + = |file:///b.noot|[extension = "aap"] == |file:///b.aap|; + +test bool extensionSetSimple() + = |file:///a/b.noot|[extension = "aap"] == |file:///a/b.aap|; + +// we don't want backslashes in windows +test bool correctTempPathResolverOnWindows() + = /\\/ !:= resolveLocation(|tmp:///|).path; + +private data MavenLocalRepositoryPath + = path(str groupId, str artifactId, str version) + | error(str cause) + ; + +private MavenLocalRepositoryPath parseMavenLocalRepositoryPath(loc jar) { + if (jar.extension != "jar") { + return error("jar should have jar extension"); + } + jar = relativize(|home:///.m2/repository|, jar); + + str groupId = replaceAll(jar.parent.parent.parent.path[1..], "/", "."); + str artifactId = jar.parent.parent.file; + str version = jar.parent.file; + str file = jar.file; + + if (file != "-.jar") { + return + error( + "This is not a repository release jar; filename should be ArtifactId-Version.jar: " + ); + } + if (/!/ := artifactId) { + return error("ArtifactId contains exclamation mark: "); + } + return path(groupId, artifactId, version); +} + +test bool mvnSchemeTest() { + debug = false; + jarFiles = find(|home:///.m2/repository|, "jar"); + + // check whether the implementation of the scheme holds the contract specified in the assert + for (jar <- jarFiles, path(groupId, artifactId, version) := parseMavenLocalRepositoryPath(jar)) { + // this is the contract: + loc mvnLoc = |mvn://< groupId >--< artifactId >--< version >|; + + if (!exists(mvnLoc)) { + println(" does not exist."); + return false; + } + } + + // report on all the failed attempts + for (debug, jar <- jarFiles, error(msg) := parseMavenLocalRepositoryPath(jar)) { + println(msg); + } + + return true; +} + +test bool fileWorks() = testLocWorksRoot(resolveLocation(|home:///|)); + +test bool memoryWorks() { + loc memTest = |memory://locations-loc-test|; + writeFile(memTest + "/a/a.txt", "Hoi"); + return testLocWorksRoot(memTest); +} + +test bool jarWorks() { + for (f := findFile(|home:///.m2/repository|, ext = "jar")) { + return + testLocWorksRoot( + f[scheme = "jar+"][path = "!/"], isWritable = false + ); + } + return false; +} + +test bool mavenWorks() { + for (jar := findFile(|home:///.m2/repository|, ext = "jar"), path(groupId, artifactId, version) := parseMavenLocalRepositoryPath(jar)) { + return + testLocWorksRoot( + |mvn://< groupId >--< artifactId >--< version >|, isWritable = false + ); + } + return false; +} + +private bool testLocWorksRoot(loc existing, bool isWritable = true) { + println("Start file path: "); + assert exists(existing) : "Start loc should exist"; + testLocWorks(existing, isWritable); + + loc subPath = findFile(existing); + println("Sub path: "); + assert exists(subPath) : "Subpath should exist"; + testLocWorks(subPath, isWritable); + + subPath = findDirectory(existing); + println("Sub path: "); + assert exists(subPath) : "Subpath should exist"; + testLocWorks(subPath, isWritable); + + loc nonExisting = existing + "not$__$there"; + println("Not existing: "); + assert !exists(nonExisting) : "Subpath should exist"; + testLocWorks(nonExisting, isWritable); + + return true; +} + +private loc findFile(loc l, str ext = "") { + for (f <- l.ls, isFile(f) && (ext == "" || f.extension == ext)) { + return f; + } + for (f <- l.ls, isDirectory(f)) { + result = findFile(f, ext = ext); + if (result.scheme != "invalid") { + return result; + } + } + return |invalid:///file|; +} + +private loc findDirectory(loc l) { + for (f <- l.ls, isDirectory(f)) { + return f; + } + throw "There should be at least a single directory inside of it"; +} + +private void testLocWorks(loc l, bool shouldWrite) { + println("\texists: "); + println("\tisFile: "); + println("\tisDirectory: "); + if (exists(l)) { + println("\tlastModified: "); + if (isDirectory(l)) { + println("\tcontents: "); + println("\tcontents: "); + } + else { + println("\tisWriteable: "); + println("\tisReadable: "); + } + } + else + if (shouldWrite) { + try { + remove(l); + } + catch IO(_): + throw "Removing file that does not exist should not cause an exception"; + if (isFile(l)) { + copyLoc = l.parent + "nested"; + copy(l.parent, copyLoc, recursive = true, overwrite = true); + sameContents = readFile(l) == readFile(copyLoc + l.file); + println("\tcopy: "); + assert sameContents : "It should be possible to copy a file"; + } + } + if (!exists(l) || !isDirectory(l)) { + try { + println("\tcontents: "); + assert + false : "ls on a non-directory should have thrown an IO exception"; + } + catch IO(_): + true; + } +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Maps.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Vadim Zaytsev - vadim@grammarware.net - UvA} +module lang::rascal::tests::basic::Maps + +import Map; +import Set; +import List; +import util::Math; +import Type; +import Node; +import Exception; +import IO; + +private map[&K, &V] emptyMap(type[map[&K, &V]] _) { + map[&K, &V] e = (); + return e; +} +private list[&T] emptyList(type[&T] _) { + list[&T] e = []; + return e; +} +private map[value, value] up(map[&K, &V] m) = m; + +// composition +test bool composition1(map[&K, &V] M) = M o emptyMap(#map[value, value]) == (); +test bool composition2(map[&K, &V] M) = () o M == (); +test bool composition3(map[&K, &V] M) = M o (v: v| &K k <- M, &V v := M[k]) == M; +test bool composition4(map[&K, &V] M) = (k: k| &K k <- M) o M == M; + +// comprehension +test bool comprehension1() = (k: k * k| k <- emptyList(#num)) == (); +test bool comprehension2() = (k: k * k| k <- [1..5]) == (1 : 1, + 2 : 4, + 3 : 9, + 4 : 16); +test bool comprehension3(set[&K] xs) + = size((k: k| &K k <- delAnnotationsRec(xs))) == size(delAnnotationsRec(xs)); + +// difference +test bool difference1(map[&K, &V] M) = M - () == M; +test bool difference2(map[&K, &V] M) = () - M == (); +test bool difference3() = (1 : 10) - (2 : 20) == (1 : 10); +test bool difference4() = (1 : 10) - (1 : 10) == (); +test bool difference5() = (1 : 10) - (1 : 20) == (); + +// equal +test bool equal1() = () == (); +test bool equal2(map[&K, &V] M) = M == M; +test bool equal3(map[&K, &V] M1, map[&K, &V] M2) + = (M1 == M2) + ==> (domain(M1) == domain(M2) + && + range(M1) == range(M2) + && + (isEmpty(M1) || all(x <- M1, eq(M1[x], M2[x])))); + +test bool equal4() { + map[int, int] iie = (); + map[str, str] sse = (); + map[value, value] iiev = iie; + map[value, value] ssev = sse; + return iiev == ssev; +} +test bool equal5() = (2 : 20, + 1 : 10) == (1 : 10, + 2 : 20); +test bool equal6() { + map[int, int] iit = (1 : 10); + map[num, value] nvt = (1 : 10); + return iit == nvt; +} + +// in +test bool in1(map[&K, &V] M) = isEmpty(M) || all(&K k <- M, k in M); +test bool in2(&K k) = k in (k : k); +test bool in3(&K k, &V v, map[&K, &V] M) = k in (M + (k : v)); +test bool in4(&K k, &V v, map[&K, &V] M) = eq(v, (M + (k : v))[k]); +test bool in5(&K k, &V v, map[&K, &V] M) = k in ((k : v) + M); +test bool in6(&K k, &V v, map[&K, &V] M) = k in M || eq(v, ((k : v) + M)[k]); + +// intersection +test bool intersection1() = (1 : 10, + 2 : 20) & (3 : 30) == (); +test bool intersection2() = (1 : 10, + 2 : 20) & (2 : 30) == (); +test bool intersection3() = (1 : 10, + 2 : 20) & (1 : 20) == (); +test bool intersection4() = (1 : 10, + 2 : 20) & (1 : 10) == (1 : 10); +test bool intersection5() = (1 : 10, + 2 : 20) & (2 : 20) == (2 : 20); +test bool intersection6(map[&K, &V] M) = M & () == (); +test bool intersection7(map[&K, &V] M) = () & M == (); + +// notequal +test bool notequal1() = !(() != ()); +test bool notequal2(map[&K, &V] M) = !(M != M); +test bool notequal3(map[&K, &V] M1, map[&K, &V] M2) + = (M1 != M2) + ==> (domain(M1) + != + domain(M2) + || + range(M1) + != + range(M2) + || + isEmpty(M1) + || + any(x <- M1, !eq(M1[x], M2[x]))); + +test bool notequal4() { + map[value, value] iie = (1 : 1) - (1 : 1); + map[value, value] sse = ("x" : "x") - ("x" : "x"); + return !(iie != sse); +} + +test bool notequal5() = !((2 : 20, + 1 : 10) != (1 : 10, + 2 : 20)); +test bool notequal6() { + map[int, int] iit = (1 : 10); + map[num, value] nvt = (1 : 10); + return !(iit != nvt); +} +test bool notequal7() = up((1 : 10)) != up((1.0 : 10)); +test bool notequal8(map[&K, &V] M) = isEmpty(M) || M != (); + +// notin +test bool notin1(&K k) = k notin (); +test bool notin2(&K k, map[&K, &V] M) = k notin (M - (k : k)); + +// pattern matching +test bool pm1() { + value n = 1; + return map[int, int] _ := (n : n); +} +test bool pm2() { + value n = 1; + value s = "string"; + return map[str, int] _ := (s : n); +} +test bool pm3() { + value n = 1; + value s = "string"; + return map[int, str] _ := (n : s); +} +test bool pm4() { + value s = "string"; + return map[str, str] _ := (s : s); +} + +// strictsubmap +test bool strictsubmap1(map[&K, &V] M) = isEmpty(M) || () < M; +test bool strictsubmap2(map[&K, &V] M) + = isEmpty(M) || delete(M, getOneFrom(M)) < M; + +// strictsupermap +test bool strictsupermap1(map[&K, &V] M) = isEmpty(M) || M > (); +test bool strictsupermap2(map[&K, &V] M) + = isEmpty(M) || M > delete(M, getOneFrom(M)); + +// submap +test bool submap1(map[&K, &V] M) = () <= M; +test bool submap2(map[&K, &V] M) = M <= M; +test bool submap3(map[&K, &V] M) = isEmpty(M) || delete(M, getOneFrom(M)) <= M; +test bool submap4(map[&K, &V] M1, map[&K, &V] M2) = M1 < M2 ==> M1 <= M2; + +// subscription +@expected{NoSuchKey} +test bool subscription1(&K k) { + map[&K, bool] M = (); + return M[k]; +} +@expected{NoSuchKey} +test bool subscription2() = (1 : false)[2]; +test bool subscription3() = (1 : 10)[1] == 10; +test bool subscription4() = (1 : 10, + 2 : 20)[1] == 10; + +// supermap +test bool supermap1(map[&K, &V] M) = M >= (); +test bool supermap2(map[&K, &V] M) = M >= M; +test bool supermap3(map[&K, &V] M) + = isEmpty(M) || M >= delete(M, getOneFrom(M)); +test bool supermap4(map[&K, &V] M1, map[&K, &V] M2) = M1 > M2 ==> M1 >= M2; + +// union +test bool union1(map[&K, &V] M1, map[&K, &V] M2) = size(M1 + M2) >= size(M1); +test bool union2(map[&K, &V] M) = () + M == M; +test bool union3(map[&K, &V] M) = M + () == M; +test bool union4() = (1 : 10) + (2 : 20) == (1 : 10, + 2 : 20); + +// += +test bool increment1() { + map[int, rel[int, int]] M = (0 : {<1, 10 >}); + M[0] += <10, 20>; + return M == (0 : {<1, 10 >, + <10, 20 >}); +} + +//////////////////////////////////////////////////////////////////////////////// +// legacy +bool keyIsInRange(&K x, map[&K, &V] A, map[&K, &V] B, map[&K, &V] C) + = (x in domain(B) && eq(C[x], B[x])) || (x in domain(A) && eq(C[x], A[x])); + +bool rightValIsUsedForKey(&K x, map[&K, &V] A, map[&K, &V] B, map[&K, &V] C) + = (x in domain(B) && x in domain(A)) ==> eq(C[x], B[x]); + +// is A + B == C? +bool isUnion(map[&K, &V] A, map[&K, &V] B, map[&K, &V] C) + = isEmpty(A) + ? C == B + : (isEmpty(B) + ? C == A + : (domain(A) + domain(B) == domain(C) + && + range(C) <= range(A) + range(B) + && + all(x <- C, keyIsInRange(x, A, B, C)) + && + all(x <- C, rightValIsUsedForKey(x, A, B, C)))); + +test bool union(map[&K, &V] A, map[&K, &V] B) = isUnion(A, B, A + B); + +// is A - B == C? +bool isDiff(map[&K, &V] A, map[&K, &V] B, map[&K, &V] C) + = isEmpty(A) + ? isEmpty(C) + : (isEmpty(B) + ? C == A + : (isEmpty(C) + ? domain(A) <= domain(B) + : all(x <- C, x in domain(A) && x notin domain(B)))); + +test bool diff(map[&K, &V] A, map[&K, &V] B) = isDiff(A, B, A - B); + +test bool intersection(map[&K, &V] A, map[&K, &V] B) + = isEmpty(A & B) || all(x <- A & B, x in A, x in B, eq(A[x], B[x])); + +test bool lesseq(map[&K, &V] A, map[&K, &V] B) = A <= (B + A); + +// right overwrites left +test bool less(map[&K, &V] A, map[&K, &V] B) = (A != B + A) ==> A < (B + A); + +test bool greatereq(map[&K, &V] A, map[&K, &V] B) = (B + A) >= A; +test bool greater(map[int, str] A, map[int, str] B) + = A<0> & B<0> == {} ==> B <= A || (B + A) > A; + +test bool intKeyHandling1() { + N = 10000; + m = (i: i| int i <- [0..N]); + return + size(m) == N + && size(domain(m)) == N + && size(range(m)) == N + && domain(m) == range(m); +} + +test bool intKeyHandling2() { + N = 10000; + m = (i: i| int i <- [0..N]); + return all(int i <- [0..N], i in m, m[i] == i); +} + +test bool intKeyHandling3() { + N = 10000; + m = (i: i| int i <- [0..N]); + for (int i <- [0..N]) { + m[i] = 2 * i; + } + return + size(m) == N + && size(domain(m)) == N + && size(range(m)) == N + && all(int i <- [0..N], i in m, m[i] == 2 * i); +} + +test bool intKeyHandling4() { + N = 10000; + m = ("XY": "XY"| int i <- [0..N]); + return + size(m) == N + && size(domain(m)) == N + && size(range(m)) == N + && domain(m) == range(m); +} + +test bool intKeyHandling5() { + N = 10000; + m = ("XY": "XY"| int i <- [0..N]); + return all(int i <- [0..N], "XY" in m, m["XY"] == "XY"); +} + +test bool intKeyHandling6() { + N = 10000; + m = ("XY": "XY"| int i <- [0..N]); + for (int i <- [0..N]) { + m["XY"] = "XXYY"; + } + return + size(m) == N + && size(domain(m)) == N + && size(range(m)) == N + && all(int i <- [0..N], "XY" in m, m["XY"] == "XXYY"); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Matching.rsc| +module lang::rascal::tests::basic::Matching + +data T1 + = \int() + | \void() + | string(str s) + ; +data T2 + = \int() + | \void() + | string(str s) + ; + +@ignoreCompiler{FIXME} +test bool incomparableTypesButNonEmptyIntersectionCanMatch() { + tuple[int, num] a = <1, 1>; + return tuple[num, int] _ := a; +} + +test bool tstQNameInPatternInt() { + T1 t1 = T1::\int(); + T2 t2 = T2::\int(); + return T1::\int() := t1 && T2::\int() := t2; +} + +@expect{ +UnexpectedType +} +test bool prefixShouldHaveEffect() { + value term = T2::string("x"); + + return T1::string(_) !:= term; +} + +test bool tstQNameInPatternVoid() { + T1 t1 = T1::\void(); + T2 t2 = T2::\void(); + return T1::\void() := t1 && T2::\void() := t2; +} + +test bool tstQNameInSwitchInt() { + T1 t1 = T1::\int(); + T2 t2 = T2::\int(); + bool tstSwitch = true; + switch(t1) { + case T1::\int(): + ; + default: + tstSwitch = false; + } + switch(t2) { + case T2::\int(): + ; + default: + tstSwitch = false; + } + return tstSwitch; +} + +test bool tstQNameInSwitchVoid() { + T1 t1 = T1::\void(); + T2 t2 = T2::\void(); + bool tstSwitch = true; + switch(t1) { + case T1::\void(): + ; + default: + tstSwitch = false; + } + switch(t2) { + case T2::\void(): + ; + default: + tstSwitch = false; + } + return tstSwitch; +} + +test bool tstQNameInSwitchString() { + T1 t1 = T1::string("t1"); + T2 t2 = T2::string("t2"); + bool tstSwitch = true; + + switch(t1) { + case T1::string(str _): + ; + default: + tstSwitch = false; + } + switch(t2) { + case T2::string(str _): + ; + default: + tstSwitch = false; + } + return tstSwitch; +} + +bool fT1(T1::\int()) = true; +bool fT1(T1::\void()) = true; +bool fT1(T1::string(str _)) = true; +default bool fT1(value _) = false; + +bool fT2(T2::\int()) = true; +bool fT2(T2::\void()) = true; +bool fT2(T2::string(str _)) = true; +default bool fT2(value _) = false; + +test bool tstQNameinFun1Int() { + T1 t1 = T1::\int(); + return fT1(t1); +} + +test bool tstQNameinFun2Int() { + T2 t2 = T2::\int(); + return fT2(t2); +} + +test bool tstQNameinFun1Void() { + T1 t1 = T1::\void(); + return fT1(t1); +} + +test bool tstQNameinFun2Void() { + T2 t2 = T2::\void(); + return fT2(t2); +} + +test bool tstQNameinFun1String() { + T1 t1 = T1::string("t1"); + return fT1(t1); +} + +test bool tstQNameinFun2String() { + T2 t2 = T2::string("t2"); + return fT2(t2); +} + +test bool deepMatchKeywordParameter() = /int _ := "f"("f"(x = [1])); + +data IG = ig( int x = 1); + +test bool ignoreKeywordParameter1() = ig() := ig(x = 1); +test bool ignoreKeywordParameter2() = ig( + x = 1 + ) := ig(); +test bool ignoreKeywordParameter3() = "bla"() := "bla"(y = 1); +test bool ignoreKeywordParameter4() = {ig()} := {ig(x = 1)}; +test bool ignoreKeywordParameter5() = {{ig()}} := {{ig(x = 1)}}; +test bool ignoreKeywordParameter6() = := ; +test bool ignoreKeywordParameter7() = [ig(), _] := [ig(x = 1), 2]; +test bool ignoreKeywordParameter8() = "fiets"(ig()) := "fiets"(ig(x = 1)); + +@ignore{Not yet operational} +test bool ignoreKeywordParameter9() { + A = ig(x = 1); + return A := ig(); +} + +@ignore{Not yet operational} +test bool ignoreKeywordParameter10() { + L = [ig(x = 1)]; + return L := [ig()]; +} + +@ignore{Not yet operational} +test bool ignoreKeywordParameter11() { + S = {ig(x = 1)}; + return S := {ig()}; +} + +@ignore{Not yet operational} +test bool ignoreKeywordParameter12() { + M = (ig(x = 1) : ig(x = 1)); + return M := (ig() : ig()); +} + +@ignore{Not yet operational} +test bool ignoreKeywordParameter13() { + M = ([ig(x = 1)] : ig(x = 1)); + return M := ([ig()] : ig()); +} + +@ignore{Not yet operational} +test bool ignoreKeywordParameter14() { + T = ; + return T := ; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Memoization.rsc| +@synopsis{This module test the memoization feature} +module lang::rascal::tests::basic::Memoization + +import util::Memo; +import Set; + +// over the test we can have duplicate random values, so prefix the test run +private int testCount = 0 ; + +private int callCount ; + +@memo +private void call(value _) { + callCount += 1; +} + +test bool memoCalledCorrectly(set[value] x) { + callCount = 0; + for (v <- x) { + call(); + } + testCount += 1; + return callCount == size(x); +} + +test bool memoCalledCorrectly2(set[value] x) { + callCount = 0; + for (_ <- [0..10]) { + for (v <- x) { + call(); + } + } + testCount += 1; + return callCount == size(x); +} + +test bool memoExpire() { + int callCount2 = 0; + + @memo=maximumSize(10) + int call2(int i) { + callCount2 += 1; + return callCount2; + } + + for (i <- [0..5]) { + call2(i); + } + + if (call2(0) != 1) { + return false; + } + for (i <- [10..10000]) { + // this should take long as to at least hit the cache limit cleanup + call2(i); + } + + // run it a second time to really help the memo cache to clear + for (i <- [10..10000]) { + // this should take long as to at least hit the cache limit cleanup + call2(i); + } + + @javaClass{org.rascalmpl.library.Prelude} + java void sleep(int seconds); + + // sleep a bit more + sleep(10); + + for (i <- [1..5]) { + if (call2(i) == i + 1) { + // should be dropped from cache by now + // note w do not hit the call(0) as that one might be considered hot + return false; + } + } + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Nodes.rsc| +module lang::rascal::tests::basic::Nodes + +import Node; +import List; +import util::Math; +import Exception; + +// Operators +// In an ideal world, this should work, but we have to adapt ranges first ... +//public list[int] makeSlice(list[int] L, int b, int s, int e){ +// return +// for(int i <- [b, s .. e]) +// append L[i]; +//} +test bool visitNode() { + switch("x"()) { + case "x"(): + return true; + } + return false; +} + +test bool subscription(node N) { + R = getChildren(N); + for (int i <- [0..arity(N)]) { + if (head(R) != N[i]) + return false; + R = tail(R); + } + return true; +} + +test bool subscriptionWrapped(node N) { + for (int i <- [0..arity(N)]) { + if (N[i] != N[i - arity(N)]) { + return false; + } + } + return true; +} + +public list[value] makeSlice(list[value] L, int f, int s, int e) { + res = []; + int i = f; + int delta = s - f; + if (delta == 0 || f == e) + return res; + if (f <= e) { + while(i >= 0 && i < size(L) && i < e) { + res += L[i]; + i += delta; + } + } + else { + while(i >= 0 && i < size(L) && i > e) { + res += L[i]; + i += delta; + } + } + return res; +} + +test bool sliceFirst1(node N) { + L = getChildren(N); + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + e = f + arbInt(size(L) - f); + return N[f..e] == makeSlice(L, f, f + 1, e); +} + +test bool sliceFirst2(node N) { + L = getChildren(N); + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + return N[f..] == makeSlice(L, f, f + 1, size(L)); +} + +test bool sliceFirstSecond(node N) { + L = getChildren(N); + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + incr = 2; + return N[f, f + incr..] == makeSlice(L, f, f + incr, size(L)); +} + +test bool sliceEnd(node N) { + L = getChildren(N); + if (isEmpty(L)) + return true; + e = arbInt(size(L)); + return N[..e] == makeSlice(L, 0, 1, e); +} + +test bool sliceSecondEnd(node N) { + L = getChildren(N); + if (isEmpty(L)) + return true; + e = arbInt(size(L)); + incr = 2; + first = incr > e ? size(L) - 1 : 0; + return N[, incr..e] == makeSlice(L, first, incr, e); +} + +public tuple[int, int] arbFirstEnd(list[value] L) { + if (isEmpty(L)) + throw "No beging/end indices possible"; + if (size(L) == 1) + return <0, 0>; + f = arbInt(size(L)); + e = f + arbInt(size(L) - f); + return ; +} +test bool sliceFirstSecondEnd(node N) { + L = getChildren(N); + if (isEmpty(L)) + return true; + = arbFirstEnd(L); + incr = 2; + return N[f, f + incr..e] == makeSlice(L, f, f + incr, e); +} + +test bool sliceFirstNegative(node N) { + L = getChildren(N); + if (isEmpty(L)) + return true; + f = 1; + return N[-f..] == makeSlice(L, size(L) - f, size(L) - f + 1, size(L)); +} + +test bool sliceEndNegative(node N) { + L = getChildren(N); + if (isEmpty(L)) + return true; + e = arbInt(size(L)); + return N[..-e] == makeSlice(L, 0, 1, e == 0 ? e : size(L) - e); +} + +test bool sliceFirstNegativeSecondNegative(node N) { + L = getChildren(N); + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + incr = 2; + if (f == 0) + return N[0, -incr..] == makeSlice(L, 0, size(L) - incr, size(L)); + else + return N[-f, -(f + incr)..] == makeSlice(L, size(L) - f, size(L) - (f + incr), -1); +} + +test bool sliceSecondNegative(node N) { + L = getChildren(N); + if (isEmpty(L)) + return true; + incr = 2; + return N[, -incr..] == makeSlice(L, 0, size(L) - incr, size(L)); +} + +test bool assignSlice1() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + .. + ] = [10, 20]; + return L == "f"(10, 20, 10, 20, 10, 20, 10, 20, 10, 20); +} +test bool assignSlice2() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + 2 + .. + ] = [10, 20]; + return L == "f"(0, 1, 10, 20, 10, 20, 10, 20, 10, 20); +} +test bool assignSlice3() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L[2..6] = [10, 20]; + return L == "f"(0, 1, 10, 20, 10, 20, 6, 7, 8, 9); +} +test bool assignSlice4() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L[8..3] = [10, 20]; + return L == "f"(0, 1, 2, 3, 10, 20, 10, 20, 10, 9); +} + +test bool assignStep1() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + , + 2 + .. + ] = [10]; + return L == "f"(10, 1, 10, 3, 10, 5, 10, 7, 10, 9); +} +test bool assignStep2() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + , + 2 + .. + ] = [10, 20]; + return L == "f"(10, 1, 20, 3, 10, 5, 20, 7, 10, 9); +} +test bool assignStep3() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + , + 2 + .. + ] = [10]; + return L == "f"(10, 1, 10, 3, 10, 5, 10, 7, 10, 9); +} +test bool assignStep4() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + , + 2 + .. + ] = [10, 20]; + return L == "f"(10, 1, 20, 3, 10, 5, 20, 7, 10, 9); +} +test bool assignStep5() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + , + 2 + .. + ] = [10, 20, 30]; + return L == "f"(10, 1, 20, 3, 30, 5, 10, 7, 20, 9); +} +test bool assignStep6() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + , + 2 + .. + ] = [10, 20, 30, 40, 50, 60, 70]; + return L == "f"(10, 1, 20, 3, 30, 5, 40, 7, 50, 9, 60, 70); +} + +test bool assignStep7() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + 2 + , + 4 + .. + ] = [10]; + return L == "f"(0, 1, 10, 3, 10, 5, 10, 7, 10, 9); +} +test bool assignStep8() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L[2, 4..6] = [10]; + return L == "f"(0, 1, 10, 3, 10, 5, 6, 7, 8, 9); +} + +test bool assignStep9() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + , + 6 + .. + 1 + ] = [10]; + return L == "f"(0, 1, 2, 10, 4, 5, 10, 7, 8, 10); +} +test bool assignStep10() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + 8 + , + 6 + .. + ] = [10]; + return L == "f"(10, 1, 10, 3, 10, 5, 10, 7, 10, 9); +} +test bool assignStep11() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L[8, 6..3] = [10]; + return L == "f"(0, 1, 2, 3, 10, 5, 10, 7, 10, 9); +} + +test bool assignStep12() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + -1 + , + -2 + .. + ] = [10, 20, 30, 40, 50]; + return L == "f"(50, 40, 30, 20, 10, 50, 40, 30, 20, 10); +} +test bool assignStep13() { + L = "f"(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + L [ + -1 + , + -3 + .. + ] = [10, 20, 30, 40, 50]; + return L == "f"(0, 50, 2, 40, 4, 30, 6, 20, 8, 10); +} + +// Library functions +test bool tstNode1(node N) + = N == makeNode( + getName(N), getChildren(N), keywordParameters = getKeywordParameters(N) + ); + +test bool tstNode2(str name, list[value] children) + = arity(makeNode(name, children)) == size(children) + && getName(makeNode(name, children)) == name + && getChildren(makeNode(name, children)) == children; + +data D = d(int i, int j = 0); +node n0 = d(1, j = 2) ; +node n1 = d(3) ; + +test bool testPositionalFieldOnNode() = n0.i == 1; +//generate warning in static checker: "field access on node type may fail at run-time" +test bool testKeywordParameterOnNode() = n0.j == 2; + +@expected{NoSuchField} +test bool testUnsetKeywordParameterOnNode() { + n1.j; + fail; +} +@expected{NoSuchField} +test bool testNonExistingFieldOnNode() { + n0.k; + fail; +} + +test bool testNodeHasPositionalParameter() = n0 has i; +test bool testNodeHasKeywordParameter() = n0 has j; +test bool testNodeHasDefaultKeywordParameter() = !(n1 has j); +test bool testNodeIsPositionalParameterDefined() = n0.i?; +test bool testNodeIsKeywordParameterDefined() = n0.j?; +test bool testNodeDefaultKeywordParameterIsNotDefined() = !n1.j?; + +test bool testUnset(node n) = getKeywordParameters(unset(n)) == (); + +test bool testUnsetRecNode(node v) = testUnsetRecValue(v); + +test bool testUnsetRecValue(value v) { + visit(unsetRec(v)) { + case node n: { + if (getKeywordParameters(n) != ()) { + return false; + } + } + } + return true; +} + +test bool testUnsetRecInsideTuple() = testUnsetRecValue(<1, "a"(x = 3)>); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Overloading.rsc| +module lang::rascal::tests::basic::Overloading + +import List; +import Set; + +test bool overloading1() { + int f(0) = -1; + default int f(int n) = n; + + int f("0") = -2; + default int f(str s) = -3; + + int f(int n, str s) = -4; + + x = f(0); + y = f(5); + k = f("0"); + l = f("5"); + z = f(0, "1"); + return x == -1 && y == 5 && k == -2 && l == -3 && z == -4; +} + +test bool overloading2() { + default int f(int n) = n; + default int f(str s) = -3; + + int f(0) = -1; + int f("0") = -2; + + int f(int n, str s) = -4; + + x = f(0); + y = f(5); + k = f("0"); + l = f("5"); + z = f(0, "1"); + return x == -1 && y == 5 && k == -2 && l == -3 && z == -4; +} + +data D + = d(str s) + | d(int n) + | d() + ; + +@synopsis{triggers issue #1234} +@ignoreCompiler{d(x) is now flagged as type error} +test bool constructorDynamicMatch() { + value x = 1; + + // Due to issue #1234, `d(x)` would throw a MatchFailed() here */ + return d(int i) := d(x) && i == 1; +} + +data D3 + = d3(str s) + | d3(int n) + | d3() + ; + +D3 d3(0) = d3(-1); +D3 d3("0") = d3("-1"); + +test bool overloading3a() { + x = d3(0); + y = d3("0"); + k = d3(1); + z = d3("1"); + return == ; +} + +@ignoreCompiler{INCOMPATIBILITY: This test is deprecated for compiler -- No more overloading across scopes} +test bool overloading3b() { + public D d(0) = d(-1); + public D d("0") = d("-1"); + + x = d(0); + y = d("0"); + k = d(1); + z = d("1"); + return == ; +} + +test bool overloadingDynamicCall() { + int f(0) = -1; + default int f(int i) = 100 + i; + + str f("0") = "- 1"; + default str f(str s) = "100 + "; + + x = f; + y = x("arg"); + z = x(1); + return == <"100 + arg", 101>; +} + +data D4 + = d4(str s) + | d4(int n) + | d4() + ; + +default D4 d4(str s) = d4(); + +D4 d4(0) = d4(-1); +D4 d4("0") = d4("-1"); + +test bool overloadingMatcha() { + int n = 0; + if (D4::d4(int v) := d4(0)) { + n = v; + } + return n == -1; +} + +@ignoreCompiler{INCOMPATIBILITY: This test is deprecated for compiler -- No more overloading across scopes} +test bool overloadingMatchb() { + default D d(str s) = d(); + + D d(0) = d(-1); + D d("0") = d("-1"); + + int n = 0; + if (D::d(int v) := d(0)) { + n = v; + } + return n == -1; +} + +test bool overloadingPlusBacktracking1() { + int f([*int x, *int y]) { + if (size(x) == size(y)) { + return -1000; + } + fail; + } + default int f(list[int] l) = 0; + + int g([1, 2, 3, 4, 5, 6]) { + return -2000; + } + default int g(list[int] l) = -3000; + + int h(list[int] _) { + fail; + } + default int h(list[int] l) = -3000; + + return + == <-1000, -2000, -3000, -3000>; +} + +test bool overloadingPlusBacktracking2() { + list[int] f([*int x, *int y]) { + if (size(x) == size(y)) + return x; + fail; + } + default list[int] f(list[int] l) = l; + + return f([1, 2, 3, 4]) == [1, 2]; +} + +test bool overloadingPlusPolymorphism1() { + int inc(int n) { + return n + 1; + } + + return mapper([1, 2, 3], inc) == [2, 3, 4]; +} + +test bool overloadingPlusPolymorphism2() { + bool similar(int a, int b) = a % 5 == b % 5; + + return group({1, 2, 3}, similar) == {{1}, {2}, {3}}; +} + +@ignoreCompiler{FIXME: Not yet supported by compiler} +test bool overloadingPlusVarArgs() { + str f(500) = "500"; + str f(500, "0") = "5000"; + default str f(int n, str strs...) = " + "; + + return + f(500) + + + "; " + + + f(0) + + + "; " + + + f(500, "0") + + + "; " + + + f(0, "0", "0") + + + "; " + + + f(600) + + + "; " + + + f(600, "0") == "500; 0 + []; 5000; 0 + [\"0\",\"0\"]; 600 + []; 600 + [\"0\"]"; +} + +test bool overloadingPlusVarArgsSpecialCase() { + list[str] f(str strs...) = strs; + + return f(["0", "0"]) + f("1", "1") == ["0", "0", "1", "1"]; +} + +@IgnoreCompiler{ +Map patterns not supported +} +private bool singletonSetWithMap({()}) = true; + +@IgnoreCompiler{ +Map patterns not supported +} +private default bool singletonSetWithMap(value _) = false; + +@IgnoreInterpreter +@IgnoreCompiler +test bool callSingletonSetWithMap() = singletonSetWithMap({()}); + +data F + = fff(str s, int n) + | fff(int n, str s) + ; + +int getN1(fff(str s, n)) = n; + +int getN2(fff(n, str s)) = n; + +test bool overloadedCons1(str s, int n) = getN1(fff(s, n)) == n; +test bool overloadedCons2(str s, int n) = getN2(fff(n, s)) == n; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Relations.rsc| +module lang::rascal::tests::basic::Relations + +import Set; +import Relation; + +// Operators +test bool product(set[&A] X, set[&B] Y) + = isEmpty(X) + ? isEmpty(X * Y) + : (isEmpty(Y) + ? isEmpty(X * Y) + : all(x <- X, x in domain(X * Y)) + && all(y <- Y, y in range(X * Y)) + && all( <- X * Y, x in X, y in Y)); + +test bool composition(rel[int, str] X, rel[str, int] Y) + = isEmpty(X) + ? isEmpty(X o Y) + : (isEmpty(Y) + ? isEmpty(X o Y) + : (isEmpty(X o Y) || all( <- X o Y, x in domain(X o Y), y in range(X o Y)))); + +test bool selection(rel[&A fa, &B fb] X) + = X.fa == domain(X) && X.fb == range(X) && X.fa == X<0> && X.fb == X<1>; + +test bool \join(rel[&A, &B] X, rel[&B, &C, &D] Y) + = isEmpty(X) + ? isEmpty(X join Y) + : (isEmpty(Y) ? isEmpty(X join Y) : (X join Y)<0,1> == X && (X join Y)<2,3,4> == Y); + +test bool subscription(rel[&A, &B, &C] X) + = isEmpty(X) + || all(&A a <- domain(X), any(<&B b, &C c> <- X[a], in X)) + && all(<&A a, &B b, &C c> <- X, in X[a]); + +/*TODO: + &A => set[list[list[loc]]] + rel[&A,&A] =>{<{[],[[]]},{[],[[]]}>,<{[],[[],[]]},{}>} +*/test bool tclosure(rel[&A, &A] X) = isEmpty(X) || X <= (X+) && (X+) + (X+) o X == (X+); + +test bool rtclosure(rel[int, int] X) + = isEmpty(X) + || X <= X* + && (X*) + (X*) o X == X* + && all(x <- carrier(X), y <- carrier(X), in X*, in X*); + +// Library functions +private set[int] sample(rel[int, int] X) { + c = carrier(X); + if (size(c) <= 2) + return {}; + = takeOneFrom(c); + = takeOneFrom(c); + return {r1, r2}; +} +test bool tst_carrier(rel[int, int] X) + = isEmpty(X) || all( <- X, a in carrier(X), b in carrier(X)); + +test bool tst_carrierR(rel[int, int] X) { + s = sample(X); + XR = carrierR(X, s); + return isEmpty(XR) || all( <- XR, a in s, b in s); +} + +test bool tst_carrierX(rel[int, int] X) { + s = sample(X); + XR = carrierX(X, s); + return isEmpty(XR) || all( <- XR, a notin s, b notin s); +} + +test bool tst_complement(rel[int, int] X) + = isEmpty(complement(X)) + || complement(X) <= domain(X) * range(X) + && all( <- complement(X), notin X); + +test bool tst_domain(rel[int, int] X) + = isEmpty(X) + || all(<num a, num _> <- X, a in domain(X)) + && all(num c <- domain(X), any(<num x, num _> <- X, x == c)); + +test bool tst_domainR(rel[int, int] X) { + s = sample(X); + XR = domainR(X, s); + return isEmpty(XR) || all( <- XR, a in s); +} + +test bool tst_domainX(rel[int, int] X) { + s = sample(X); + XR = domainX(X, s); + return isEmpty(XR) || all( <- XR, a notin s); +} + +test bool tst_ident(set[int] X) + = isEmpty(X) || all( <- ident(X), a == b, a in X); + +test bool tst_invert(rel[int, int] X) = invert(invert(X)) == X; + +test bool tst_range(rel[int, int] X) + = isEmpty(X) + || all(<num _, num b> <- X, b in range(X)) + && all(num c <- range(X), any(<num _, num y> <- X, y == c)); + +test bool tst_rangeR(rel[int, int] X) { + s = sample(X); + XR = rangeR(X, s); + return isEmpty(XR) || all(<_, b> <- XR, b in s); +} + +test bool tst_rangeX(rel[int, int] X) { + s = sample(X); + XR = rangeX(X, s); + return isEmpty(XR) || all(<_, b> <- XR, b notin s); +} + +@expected{UndeclaredField} +@ignoreCompiler{Remove-after-transtion-to-compiler: Already detected by type checker} +test bool fieldSelectionNoFields() { + x = {}; + x; + // throws UndeclaredField + return false; +} + +test bool fieldSelectionEmpty() { + rel[int a, int b] x = {}; + return x == {}; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/RepositionTree.rsc| +module lang::rascal::tests::basic::RepositionTree + +import List; +import ParseTree; +import lang::pico::\syntax::Main; +import String; + +loc facPico + = |project://rascal/src/org/rascalmpl/library/lang/pico/examples/fac.pico| ; + +private list[loc] collect(Tree t) = [s@\loc | /Tree s := t, s@\loc?]; + +test bool repositionSimulatesReparse() { + t1 = parse(#start[Program], facPico); + t2 = reposition(t1); + // defaults set + assert t1 := t2; + // but that skips keyword parameters and layout + return collect(t1) == collect(t2); +} + +test bool removeAllAnnotations() { + t1 = parse(#start[Program], facPico); + t2 + = reposition( + t1, + markSyntax = false, + markLexical = false, + markSubLexical = false, + markAmb = false, + markChar = false, + markLayout = false, + markLit = false, + markStart = false, + markSubLit = false, + markSubLayout = false, + markRegular = false + ); + assert t1 := t2; + // but that skips keyword parameters and layout + return collect(t2) == []; +} + +test bool charsFromLeftToRight() { + // removing \r from this test to avoid problems. + t1 = parse(#start[Program], facPico); + t2 = reposition(t1, markChar = true); + allChars = [ch | /ch: char(_) := t2]; + sortedChars + = sort(allChars, bool (Tree c1, Tree c2) { + return c1@\loc.offset < c2@\loc.offset; + }); + + return allChars == sortedChars; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Sets.rsc| +@license{ + Copyright (c) 2009-2020 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module lang::rascal::tests::basic::Sets + +import Exception; +import Set; +import List; +import Relation; +import Map; +import IO; +import Type; + +test bool eqSet(value a, value b) = eq(a, b) <==> size({a, b}) == 1; + +// Set operators +bool elemInAorB(&T x, set[&T] A, set[&T] B) = (x in A) || (x in B); + +// is A + B == C? +bool isUnion(set[&T] A, set[&T] B, set[&T] C) + = isEmpty(A) ? C == B : (isEmpty(B) ? C == A : all(x <- C, elemInAorB(x, A, B))); + +test bool union1(set[&T] A, set[&T] B) = isUnion(A, B, A + B); +test bool union2(&T A, set[&T] B) = isUnion({A}, B, {A} + B); +test bool union3(set[&T] A, &T B) = isUnion(A, {B}, A + {B}); + +bool elemInAandNotInB(&T x, set[&T] A, set[&T] B) = (x in A) && (x notin B); + +// is A - B == C? +bool isDiff(set[&T] A, set[&T] B, set[&T] C) + = isEmpty(A) + ? isEmpty(C) + : (isEmpty(B) + ? C == A + : (isEmpty(C) ? all(x <- A, x in B) : all(x <- C, elemInAandNotInB(x, A, B)))); + +test bool diff(set[&T] A, set[&T] B) = isDiff(A, B, A - B); + +// A == B? +public +bool isEqual(set[&T] A, set[&T] B) + = size(A) == size(B) ? (size(A) == 0 || all(x <- A, x in B) && all(x <- B, x in A)) : false; + +test bool equal1(set[&T] A) = A == A; +test bool equal2(set[int] A, set[int] B) + = (A == B) ? isEqual(A, B) : !isEqual(A, B); + +test bool notEqual1(set[&T] A) = !(A != A); +test bool notEqual2(set[int] A, set[int] B) + = (A != B) ? !isEqual(A, B) : isEqual(A, B); + +test bool intersection(set[&T] A, set[&T] B) + = isEmpty(A & B) || all(x <- A & B, x in A, x in B); + +test bool lesseq(set[int] A, set[int] B) = A <= (A + B); +test bool less(set[int] A, set[int] B) = (A & B == {} && !isEmpty(B)) ==> A < (A + B); + +test bool greatereq(set[int] A, set[int] B) = (A + B) >= A; +test bool greater(set[int] A, set[int] B) + = (A & B == {} && !isEmpty(B)) ==> (A + B) > A; + +test bool tst_in(int A, set[int] B) = A in (A + B) && A in (B + A); +test bool tst_notin(int A, set[int] B) = A notin (B - A); + +test bool splicing(set[&T] A, set[&T] B) + = {*A, *B} == A + B && {A, *B} == {A} + B && {*A, B} == A + {B}; + +// Library functions +private bool similar(int a, int b) = a % 5 == b % 5; +private int getClass(int a) = a % 5; + +test bool tst_classify(set[int] S) { + if (isEmpty(S)) { + return true; + } + classes = classify(S, getClass); + + if ({*classes[c]| c <- classes} != S) { + println("failed for : "); + return false; + } + if (c <- classes, e <- classes[c], getClass(e) notin classes) { + println( + "failed due to: classes: , class: , elem: , getClass(): " + ); + return false; + } + return true; +} + +test bool tst_getOneFrom(set[&A] S) = isEmpty(S) || getOneFrom(S) in S; + +test bool tst_group1(set[int] S) + = isEmpty(S) || {*g| g <- group(S, similar)} == S; +test bool tst_group2(set[int] S) + = isEmpty(S) + || all(g <- group(S, similar), all(int x <- g, int y <- g, similar(x, y))); + +test bool tst_index1(set[int] S) = isEmpty(S) || domain(index(S)) == S; +test bool tst_index2(set[int] S) = isEmpty(S) || min(range(index(S))) == 0; +test bool tst_index3(set[int] S) + = isEmpty(S) || max(range(index(S))) == size(S) - 1; + +// mapper +test bool tst_max(set[int] S) = isEmpty(S) || all(x <- S, x <= max(S)); + +test bool tst_min(set[int] S) = isEmpty(S) || all(x <- S, x >= min(S)); + +// power, power1, reducer +test bool tst_size(set[int] S) = size(S) == ( 0 | it + 1 | _ <- S ); + +test bool tst_sort(set[int] S) + = isEmpty(S) + || all(x <- sort(S), x in S) + && size(S) == size(sort(S)) + && isSorted(sort(S)); + +test bool tst_sum(set[int] S) = isEmpty(S) || sum(S) == ( 0 | it + x | x <- S ); + +test bool tst_takeOneFrom(set[int] S) { + if (isEmpty(S)) + return true; + = takeOneFrom(S); + return x in S && x notin S2 && size(S2) == size(S) - 1 && S2 < S; +} + +test bool tst_getSingleFrom(set[int] S) { + if ({e} := S) { + return getSingleFrom(S) == e; + } + return true; +} + +test bool tst_getSingleFromExample(str input) { + return getSingleFrom({input}) == input; +} + +@expected{CallFailed} +test bool tst_getSingleFromMore(str input, int i) { + getSingleFrom({input, i}); + return false; +} + +test bool tst_toList(set[int] S) + = isEmpty(S) || size(S) == size(toList(S)) && all(x <- S, x in toList(S)); + +test bool tst_toMap(rel[int, int] S) + = isEmpty(S) + || domain(S) == domain(toMap(S)) + && range(S) == {*toMap(S)[k]| k <- toMap(S)}; + +data X = y(int y); +anno + int + X + @ + z +; + +test bool tst_toMapUnique(set[int] D, set[int] R) { + if (isEmpty(D) || isEmpty(R)) + return true; + S = {| k <- D}; + return + domain(S) == domain(toMapUnique(S)) + && range(S) == {toMapUnique(S)[k]| k <- toMapUnique(S)}; +} + +// toString +// Tests that check the correctness of the dynamic types of sets produced by the library functions; +// incorrect dynamic types make pattern matching fail; +@ignoreAnnotations +test bool dtstDifference(set[&T] s) { + if (isEmpty(s)) + return true; + bool check = true; + for (int _ <- [0..size(s)]) { + &T elem = getOneFrom(s); + lhs = s - {elem}; + rhs = {el| &T el <- s, !eq(el, elem)}; + check = check && eq(lhs, rhs) && typeOf(lhs) == typeOf(rhs); + } + return check; +} + +@ignoreAnnotations +test bool dtstIntersection(set[&T] s) { + if (isEmpty(s)) + return true; + set[set[&T]] subs = power(s); + bool check = true; + for (set[&T] sub <- subs) { + lhs = s & sub; + rhs = {el| &T el <- s, el in sub}; + check = check && eq(lhs, rhs) && typeOf(lhs) == typeOf(rhs); + } + return check; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/SolvedIssues.rsc| +@bootstrapParser +@synopsis{In this test module we collect test cases that are associated with bugs from the past. +This is just to make sure the bugs are not re-introduced accidentally.} +module lang::rascal::tests::basic::SolvedIssues + +test bool emptySetEquals1(set[value] x, set[value] y) = x - x == y - y; + +test bool emptySetEquals2(map[value, value] x, map[value, value] y) + = x - x == y - y; + +data X = n(set[node] nn); + +test bool infiniteMatcher() { + bool firstTime = true; + for (n({"a"(), *_}) <- {n({"a"()})}) { + if (!firstTime) { + return false; + } + firstTime = false; + } + return true; +} + +data Exp = a(int x, int y = 5); + +test bool inferKWparamType() { + // a regression test + if (a(_,y = q) := a(0, y = 3)) { + int z = q; + // used to throw an exception "Expected int, but got value true;" + return true; + } + else { + return false; + } +} + +// https://github.com/cwi-swat/rascal/issues/885 +test bool checkComparableOnElementsBreak() + = [<[], _, _ >, + _ ] := [<[], false, 1 >, + <[3], false, 3 >]; + +// https://github.com/cwi-swat/rascal/issues/930 +test bool nodeSetMatch() = {"a"(1)} := {"a"(1)}; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Strings1.rsc| +module lang::rascal::tests::basic::Strings1 + +import String; +import List; +import util::Math; + +test bool subscription(str S) { + R = ""; + for (int i <- [0..size(S)]) { + R += S[i]; + } + return R == S; +} + +test bool sliceEmpty() = ""[0..5] == ""; +test bool sliceOverEnd() = "/"[1..] == ""; +test bool sliceOverEnd2() = "/"[2..] == ""; + +test bool subscriptionWrapped(str S) { + for (int i <- [0..size(S)]) { + if (S[i] != S[i - size(S)]) { + return false; + } + } + return true; +} + +test bool sliceFirst1(str L) { + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + e = f + arbInt(size(L) - f); + S = L[f..e]; + return S == makeSlice(L, f, f + 1, e); +} + +test bool sliceFirst2(str L) { + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + S = L[f..]; + return S == makeSlice(L, f, f + 1, size(L)); +} + +// In an ideal world, this should work, but we have to adapt ranges first ... +//public list[int] makeSlice(list[int] L, int b, int s, int e){ +// return +// for(int i <- [b, s .. e]) +// append L[i]; +//} +public str makeSlice(str L, int f, int s, int e) { + res = ""; + int i = f; + int delta = s - f; + if (delta == 0 || f == e) + return res; + if (f <= e) { + while(i >= 0 && i < size(L) && i < e) { + res += L[i]; + i += delta; + } + } + else { + while(i >= 0 && i < size(L) && i > e) { + res += L[i]; + i += delta; + } + } + return res; +} + +test bool sliceFirstSecond(str L) { + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + incr = 2; + return L[f, f + incr..] == makeSlice(L, f, f + incr, size(L)); +} + +test bool sliceEnd(str L) { + if (isEmpty(L)) + return true; + e = arbInt(size(L)); + return L[..e] == makeSlice(L, 0, 1, e); +} + +test bool sliceSecondEnd(str L) { + if (isEmpty(L)) + return true; + e = arbInt(size(L)); + incr = 2; + first = incr > e ? size(L) - 1 : 0; + return L[, incr..e] == makeSlice(L, first, incr, e); +} + +public tuple[int, int] arbFirstEnd(str L) { + if (isEmpty(L)) + throw "No beging/end indices possible"; + if (size(L) == 1) + return <0, 0>; + f = arbInt(size(L)); + e = f + arbInt(size(L) - f); + return ; +} +test bool sliceFirstSecondEnd(str L) { + if (isEmpty(L)) + return true; + = arbFirstEnd(L); + incr = 2; + return L[f, f + incr..e] == makeSlice(L, f, f + incr, e); +} + +test bool sliceFirstNegative(str L) { + if (isEmpty(L)) + return true; + f = 1; + return L[-f..] == makeSlice(L, size(L) - f, size(L) - f + 1, size(L)); +} + +test bool sliceEndNegative(str L) { + if (isEmpty(L)) + return true; + e = arbInt(size(L)); + return L[..-e] == makeSlice(L, 0, 1, e == 0 ? e : size(L) - e); +} + +test bool sliceFirstNegativeSecondNegative(str L) { + if (isEmpty(L)) + return true; + f = arbInt(size(L)); + incr = 2; + if (f == 0) + return L[0, -incr..] == makeSlice(L, 0, size(L) - incr, size(L)); + else + return L[-f, -(f + incr)..] == makeSlice(L, size(L) - f, size(L) - (f + incr), -1); +} + +test bool sliceSecondNegative(str L) { + if (isEmpty(L)) + return true; + incr = 2; + S = L[, -incr..]; + return S == makeSlice(L, 0, size(L) - incr, size(L)); +} + +test bool assignSlice1() { + L = "abcdefghij"; + L [ + .. + ] = "XY"; + return L == "XYXYXYXYXY"; +} +test bool assignSlice2() { + L = "abcdefghij"; + L [ + 2 + .. + ] = "XY"; + return L == "abXYXYXYXY"; +} +test bool assignSlice3() { + L = "abcdefghij"; + L[2..6] = "XY"; + return L == "abXYXYghij"; +} +test bool assignSlice4() { + L = "abcdefghij"; + L[8..3] = "XY"; + return L == "abcdXYXYXj"; +} + +test bool assignStep1() { + L = "abcdefghij"; + L [ + , + 2 + .. + ] = "X"; + return L == "XbXdXfXhXj"; +} +test bool assignStep2() { + L = "abcdefghij"; + L [ + , + 2 + .. + ] = "XY"; + return L == "XbYdXfYhXj"; +} +test bool assignStep3() { + L = "abcdefghij"; + L [ + , + 2 + .. + ] = "X"; + return L == "XbXdXfXhXj"; +} +test bool assignStep4() { + L = "abcdefghij"; + L [ + , + 2 + .. + ] = "XY"; + return L == "XbYdXfYhXj"; +} +test bool assignStep5() { + L = "abcdefghij"; + L [ + , + 2 + .. + ] = "XYZ"; + return L == "XbYdZfXhYj"; +} +test bool assignStep6() { + L = "abcdefghij"; + L [ + , + 2 + .. + ] = "XYZPQRS"; + return L == "XbYdZfPhQjRS"; +} + +test bool assignStep7() { + L = "abcdefghij"; + L [ + 2 + , + 4 + .. + ] = "X"; + return L == "abXdXfXhXj"; +} +test bool assignStep8() { + L = "abcdefghij"; + L[2, 4..6] = "X"; + return L == "abXdXfghij"; +} + +test bool assignStep9() { + L = "abcdefghij"; + L [ + , + 6 + .. + 1 + ] = "X"; + return L == "abcXefXhiX"; +} +test bool assignStep10() { + L = "abcdefghij"; + L [ + 8 + , + 6 + .. + ] = "X"; + return L == "XbXdXfXhXj"; +} +test bool assignStep11() { + L = "abcdefghij"; + L[8, 6..3] = "X"; + return L == "abcdXfXhXj"; +} + +test bool assignStep12() { + L = "abcdefghij"; + L [ + -1 + , + -2 + .. + ] = "XYZPQ"; + return L == "QPZYXQPZYX"; +} +test bool assignStep13() { + L = "abcdefghij"; + L [ + -1 + , + -3 + .. + ] = "XYZPQ"; + return L == "aQcPeZgYiX"; +} + +// Library functions +test bool tstCenter1(str S) { + c = center(S, size(S) + 5); + return contains(c, S) && startsWith(c, " ") && endsWith(c, " "); +} +test bool tstCenter2(str S) { + c = center(S, size(S) + 5, "x"); + return contains(c, S) && startsWith(c, "x") && endsWith(c, "x"); +} + +test bool tstCharAt(str S) { + for (i <- [0..size(S)]) + if (charAt(S, i) != chars(S[i])[0]) + return false; + return true; +} + +test bool tstChars(str S) = S == stringChars(chars(S)); + +test bool tstContains(str S1, str S2, str S3) + = contains(S1 + S2 + S3, S1) && contains(S1 + S2 + S3, S2) && contains(S1 + S2 + S3, S3); + +test bool tstEndsWith(str S1, str S2) = endsWith(S1 + S2, S2); + +test bool tstEscape(str S, str K1, str R1, str K2, str R2) { + if (isEmpty(K1) + || isEmpty(K2) + || K1[0] == K2[0] + || contains(S, K1[0]) + || contains(S, K2[0])) + return true; + T = K1[0] + S + K2[0] + S + K2[0] + S + K1[0]; + return escape(T, (K1[0] : R1, + K2[0] : R2)) == R1 + S + R2 + S + R2 + S + R1; +} + +test bool tstFindAll(str S1, str S2) { + S = S2 + S1 + S2 + S1 + S2; + for (i <- findAll(S, S2)) + if (!startsWith((i < size(S) ? S[i..] : ""), S2)) + return false; + return true; +} + +test bool tstFindFirst(str S1, str S2) { + S = S1 + S2 + S1 + S2; + i = findFirst(S, S2); + return i >= 0 && startsWith((i < size(S) ? S[i..] : ""), S2); +} + +test bool tstFindLast(str S1, str S2) { + S = S1 + S2 + S1 + S2 + S1; + i = findLast(S, S2); + return i >= 0 && startsWith((i < size(S) ? S[i..] : ""), S2); +} + +test bool tstIsEmpty(str S) = isEmpty(S) ? size(S) == 0 : size(S) > 0; + +test bool tstStringChar(str S) { + for (i <- [0..size(S)]) + if (stringChar(chars(S)[i]) != S[i]) + return false; + return true; +} + +test bool tstIsValidCharacter(str S) + = isEmpty(S) || all(i <- [0..size(S)], isValidCharacter(chars(S[i])[0])); + +test bool tstLeft1(str S) { + l = left(S, size(S) + 5); + return startsWith(l, S) && endsWith(l, " "); +} +test bool tstLeft2(str S) { + l = left(S, size(S) + 5, "x"); + return startsWith(l, S) && endsWith(l, "x"); +} +test bool tstLeft1_s(str S) { + l = left(S, size(S) + 1); + return startsWith(l, S) && endsWith(l, " "); +} +test bool tstLeft2_s(str S) { + l = left(S, size(S) + 1, "x"); + return startsWith(l, S) && endsWith(l, "x"); +} + +bool areOverlapping(str s1, str s2) + = s1 == s2 + || findAll(s1 + s2, s2) + != [size(s1)] + || findAll(s2 + s1, s1) + != [size(s2)] + || ((size(s1) > 0 && size(s2) > 0) && s1[-1] == s2[0] || s1[0] == s2[-1]); + +test bool tstReplaceAll(str S1, str S2, str S3) { + if (areOverlapping(S1, S2) || areOverlapping(S1 + S2, S2 + S1)) + return true; + S = S1 + S2 + S1 + S2 + S1; + return replaceAll(S, S2, S3) == S1 + S3 + S1 + S3 + S1; +} + +test bool tstReplaceFirst(str S1, str S2, str S3) { + if (areOverlapping(S1, S2) || areOverlapping(S1 + S2, S2 + S1)) + return true; + S = S1 + S2 + S1 + S2 + S1; + return replaceFirst(S, S2, S3) == S1 + S3 + S1 + S2 + S1; +} + +test bool tstReplaceLast(str S1, str S2, str S3) { + if (areOverlapping(S1, S2) || areOverlapping(S1 + S2, S2 + S1)) + return true; + S = S1 + S2 + S1 + S2 + S1; + return replaceLast(S, S2, S3) == S1 + S2 + S1 + S3 + S1; +} + +test bool tstReverse(str S) = S == reverse(reverse(S)); + +// rexpMatch +test bool tstRight1(str S) { + r = right(S, size(S) + 5); + return endsWith(r, S) && startsWith(r, " "); +} +test bool tstRight2(str S) { + r = right(S, size(S) + 5, "x"); + return endsWith(r, S) && startsWith(r, "x"); +} + +test bool tstSize(str S) = size(S) == size(chars(S)); + +test bool tstSplit(str S1, str S2) + = areOverlapping(S1, S2) + || isEmpty(S1) + || isEmpty(S2) + || contains(S2, S1) + || S1[-1] == S2[0] + || S1[0] == S2[-1] + || split(S1, S2 + S1 + S2 + S1) == [S2, S2]; + +// squeeze +test bool tstSqueeze1(str S) = // !:= squeeze(S, "a-zA-Z"); +test bool tstSqueeze2(str S) = squeeze(S, "") == S; +test bool tstSqueeze3(str S) { + if (// := S) { + return // := squeeze(S, "0-9"); + } + return true; +} + +test bool tstSqueezeUnicode() = squeeze("Hi 🍝🍝World", "🍝") == "Hi 🍝World"; +test bool tstSqueezeCase1() = squeeze("abc", "a-c") == "abc"; +test bool tstSqueezeCase2() = squeeze("aabc", "a-c") == "abc"; +test bool tstSqueezeCase3() = squeeze("aabcc", "a-c") == "abc"; +test bool tstSqueezeCase4() = squeeze("aabbcc", "a-c") == "abc"; +test bool tstSqueezeCase5() = squeeze("aaabc", "a-c") == "abc"; + +// second squeeze +test bool tstSqueeze1CC(str S) = // !:= squeeze(S, #[a-zA-Z]); +test bool tstSqueeze2CC(str S) = squeeze(S, #[]) == S; +test bool tstSqueeze3CC(str S) { + if (// := S) { + return // := squeeze(S, #[0-9]); + } + return true; +} + +test bool tstSqueezeUnicodeCC() + = squeeze("Hi 🍝🍝World", #[🍝]) == "Hi 🍝World"; +test bool tstSqueezeCase1CC() = squeeze("abc", #[a-c]) == "abc"; +test bool tstSqueezeCase2CC() = squeeze("aabc", #[a-c]) == "abc"; +test bool tstSqueezeCase3CC() = squeeze("aabcc", #[a-c]) == "abc"; +test bool tstSqueezeCase4CC() = squeeze("aabbcc", #[a-c]) == "abc"; +test bool tstSqueezeCase5CC() = squeeze("aaabc", #[a-c]) == "abc"; + +test bool tstStartsWith(str S1, str S2) = startsWith(S1 + S2, S1); + +test bool tstSubstring1(str S) { + for (i <- [0..size(S)]) + if (substring(S, i) != (i < size(S) ? S[i..] : "")) + return false; + return true; +} + +test bool tstSubstring2(str S) { + for (i <- [0..size(S)]) + for (j <- [i..size(S)]) + if (substring(S, i, j) != (i < size(S) ? S[i..j] : "")) + return false; + return true; +} + +test bool toInt(int N) = N == toInt(""); + +test bool tstToLowerCase(str S) = /[A-Z]/ !:= toLowerCase(S); + +test bool toReal(real R) = R == toReal(""); + +test bool tstToUpperCase(str S) = /[a-z]/ !:= toUpperCase(S); + +test bool tstTrim(str S) = trim(S) == trim(" \t\n" + S + "\r\b\t "); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Strings2.rsc| +module lang::rascal::tests::basic::Strings2 + +import String; +import List; +import util::Math; +import util::Reflective; + +test bool tstWrap(str S1, str S2) { + S1 = trim(S1); + S2 = trim(S2); + if (contains(S1, "\n") + || contains(S2, "\n") + || contains(S1, " ") + || contains(S2, " ")) + return true; + if (S1 == "" && S2 == "") + return true; + S = S1 + " " + S2 + " " + S1 + " " + S2; + n = max(size(S1), size(S2)) + 2; + return trim(S) == trim(replaceAll(wrap(S, n), getLineSeparator(), " ")); +} + +private set[str] UNICODE_WS + = {"\u0009", + "\u000A", + "\u000B", + "\u000C", + "\u000D", + "\u0020", + "\u0085", + "\u00A0", + "\u1680", + "\u2000", + "\u2001", + "\u2002", + "\u2003", + "\u2004", + "\u2005", + "\u2006", + "\u2007", + "\u2008", + "\u2009", + "\u200A", + "\u2028", + "\u2029", + "\u202F", + "\u205F", + "\u3000"} ; + +test bool tstRemoveWhitespace1(str S1) + = removeWhitespace(S1) == "<for (int i <- [0..size(S1)]) {><}>"; +test bool tstRemoveWhitespace2(str S1) + = size(removeWhitespace(S1)) <= size(S1); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/TestsForTests.rsc| +module lang::rascal::tests::basic::TestsForTests + +import Exception; + +@expected{IO} +test bool testExpected() { + throw IO("this should be expected"); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Tuples.rsc| +module lang::rascal::tests::basic::Tuples + +test bool subscription1(tuple[&A] T) = == T; +test bool subscription2(tuple[&A, &B] T) = == T; +test bool subscription3(tuple[&A, &B, &C] T) = == T; + +test bool subscriptionWrapped1(tuple[&A] T) = == T; +test bool subscriptionWrapped2(tuple[&A, &B] T) = == T; +test bool subscriptionWrapped3(tuple[&A, &B, &C] T) + = == T; + +test bool tupleExpressions() { + value n = 1; + value s = "string"; + return + tuple[int, int] _ := + && tuple[str, str] _ := + && tuple[int, str] _ := ; +} + +test bool dropLabelsConcat1(tuple[int x, str s] a, tuple[real] b) + = !((a + b) has x); + +test bool dropLabelsConcat2(tuple[int x, str s] a, tuple[real] b) + = !((b + a) has x); + +test bool dropLabelsConcatDuplicate(tuple[int x, str s] a, tuple[real x] b) + = !((a + b) has x); + +test bool keepLabelsConcat(tuple[int x, str s] a, tuple[real r] b) + = ((a + b) has x); + +// tuples with escaped field names +test bool escape1() { + tuple[int \n, str \type] T = <1, "a">; + return T.\n == 1; +} +test bool escape2() { + tuple[int \n, str \type] T = <1, "a">; + return T.\type == "a"; +} + +test bool escape3() { + list[tuple[int \n, str \type]] L = [<1, "a" >, + <2, "b" >]; + return L[0].\n == 1; +} +test bool escape4() { + list[tuple[int \n, str \type]] L = [<1, "a" >, + <2, "b" >]; + return L[0].\type == "a"; +} +test bool escape5() { + list[tuple[int \n, str \type]] L = [<1, "a" >, + <2, "b" >]; + return L.\n == [1, 2]; +} +test bool escape6() { + list[tuple[int \n, str \type]] L = [<1, "a" >, + <2, "b" >]; + return L.\type == ["a", "b"]; +} +test bool escape7() { + list[tuple[int \n, str \type]] L = [<1, "a" >, + <2, "b" >]; + return L<\n> == [1, 2]; +} +test bool escape8() { + list[tuple[int \n, str \type]] L = [<1, "a" >, + <2, "b" >]; + return L<\type> == ["a", "b"]; +} + +test bool escape9() { + set[tuple[int \n, str \type]] S = {<1, "a" >, + <2, "b" >}; + return S.\n == {1, 2}; +} +test bool escape10() { + set[tuple[int \n, str \type]] S = {<1, "a" >, + <2, "b" >}; + return S.\type == {"a", "b"}; +} +test bool escape11() { + set[tuple[int \n, str \type]] S = {<1, "a" >, + <2, "b" >}; + return S<\n> == {1, 2}; +} +test bool escape12() { + set[tuple[int \n, str \type]] S = {<1, "a" >, + <2, "b" >}; + return S<\type> == {"a", "b"}; +} + +// tuples with escaped field names via alias +alias TUP = tuple[int \n, str \type]; + +test bool escapeAndAlias() { + TUP T = <1, "a">; + return T.\n == 1; +} +test bool escapeAndAlias2() { + TUP T = <1, "a">; + return T.\type == "a"; +} + +test bool escapeAndAlias3() { + list[TUP] L = [<1, "a" >, + <2, "b" >]; + return L[0].\n == 1; +} +test bool escapeAndAlias4() { + list[TUP] L = [<1, "a" >, + <2, "b" >]; + return L[0].\type == "a"; +} +test bool escapeAndAlias5() { + list[TUP] L = [<1, "a" >, + <2, "b" >]; + return L.\n == [1, 2]; +} +test bool escapeAndAlias6() { + list[TUP] L = [<1, "a" >, + <2, "b" >]; + return L.\type == ["a", "b"]; +} +test bool escapeAndAlias7() { + list[TUP] L = [<1, "a" >, + <2, "b" >]; + return L<\n> == [1, 2]; +} +test bool escapeAndAlias8() { + list[TUP] L = [<1, "a" >, + <2, "b" >]; + return L<\type> == ["a", "b"]; +} + +test bool escapeAndAlias9() { + set[TUP] S = {<1, "a" >, + <2, "b" >}; + return S.\n == {1, 2}; +} +test bool escapeAndAlias10() { + set[TUP] S = {<1, "a" >, + <2, "b" >}; + return S.\type == {"a", "b"}; +} +test bool escapeAndAlias11() { + set[TUP] S = {<1, "a" >, + <2, "b" >}; + return S<\n> == {1, 2}; +} +test bool escapeAndAlias12() { + set[TUP] S = {<1, "a" >, + <2, "b" >}; + return S<\type> == {"a", "b"}; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/basic/Types.rsc| +module lang::rascal::tests::basic::Types + +import Type; + +// equals is an equivalence relation +test bool typeEqualsReflexive(type[value] t) = t == t; + +test bool typeEqualsTransitive(type[value] s, type[value] t, type[value] u) + = s == t && t == u ==> s == u; + +test bool typeEqualsSymmetric(type[value] s, type[value] t) = t == s <==> s == t; + +// subtype is a partial order +test bool subtypeReflexive(type[value] s) = subtype(s, s); + +test bool subtypeTransitive(type[value] s, type[value] t, type[value] u) + = subtype(s, t) && subtype(t, u) ==> subtype(s, u); + +test bool subtypeAntiSymmetric(type[value] s, type[value] t) + = subtype(s, t) && subtype(t, s) ==> s == t; + +// lattice laws +test bool lubIsIdempotent(type[value] s) = lub(s, s) == s; + +test bool glbIsIdempotent(type[value] s) = glb(s, s) == s; + +test bool valueIsMax(type[value] s, type[value] t) + = subtype(lub(s, t), #value); + +test bool voidIsMin(type[value] s, type[value] t) = subtype(#void, glb(s, t)); + +test bool voidIsMin2(type[value] s) = subtype(#void, s); + +test bool valueIsMax2(type[value] s) = subtype(s, #value); + +test bool absorption1(type[value] a, type[value] b) = lub(a, glb(a, b)) == a; + +test bool absorption2(type[value] a, type[value] b) = glb(a, lub(a, b)) == a; + +test bool lubReflectsSubtype(type[value] a, type[value] b) + = subtype(a, lub(a, b)) && subtype(b, lub(a, b)); + +test bool glbReflectsSubtype(type[value] a, type[value] b) + = subtype(glb(a, b), a) && subtype(glb(a, b), b); + +test bool monotonicity( + type[value] a1, type[value] a2, type[value] b1, type[value] b2 +) + = subtype(a1, a2) + && subtype(b1, b2) + ==> subtype(glb(a1, b2), glb(a2, b2)) + && subtype(glb(a1, b1), glb(a2, b2)); + +// labels +test bool subtypeLabelsAreAliasesLeft(type[value] s, str lbl) + = subtype(s, type(\label(lbl, s.symbol), ())); +test bool subtypeLabelsAreAliasesRight(type[value] s, str lbl) + = subtype(type(\label(lbl, s.symbol), ()), s); + +// ADT'S +data Exp = exp(); +test bool subtypeNode() = subtype(#Exp, #node); + +// numerics +test bool subtype_int_num() = subtype(\int(), \num()); +test bool subtype_real_num() = subtype(\real(), \num()); +test bool subtype_rat_num() = subtype(\rat(), \num()); +test bool subtypeNum(type[num] t) = subtype(t, #num); +test bool lubNum(type[num] t, type[num] u) = lub(t, u) == #num; + +// smoke test int != str +test bool subtype_int_str() = !subtype(\int(), \str()); + +// co-variance +test bool subtype_list(type[value] s) + = subtype(\list(s.symbol), \list(\value())); +test bool subtype_set(type[value] s) + = subtype(\set(s.symbol), \set(\value())); +test bool subtype_map(type[value] k, type[value] v) + = subtype(\map(k.symbol, v.symbol), \map(\value(), \value())); + +test bool subtype_tuple1(type[value] s, type[value] t) + = subtype(\tuple([s.symbol, t.symbol]), \tuple([\value(), \value()])); +test bool subtype_tuple2(type[value] s, type[value] t, type[value] u) + = subtype( + \tuple([s.symbol, t.symbol, u.symbol]), + \tuple([\value(), \value(), \value()]) + ); + +test bool subtype_rel1(type[value] s, type[value] t) + = subtype(\rel([s.symbol, t.symbol]), \rel([\value(), \value()])); +test bool subtype_rel2(type[value] s, type[value] t, type[value] u) + = subtype( + \rel([s.symbol, t.symbol, u.symbol]), \rel([\value(), \value(), \value()]) + ); + +test bool subtype_lrel1(type[value] s, type[value] t) + = subtype(\lrel([s.symbol, t.symbol]), \lrel([\value(), \value()])); +test bool subtype_lrel2(type[value] s, type[value] t, type[value] u) + = subtype( + \lrel([s.symbol, t.symbol, u.symbol]), + \lrel([\value(), \value(), \value()]) + ); + +// tuples +// there can exist no tuples that take a void parameter, hence types with those void parameters represent an _empty set_ of values, hence such tuples types are equivalent to `void` +test bool tuplesWithVoidParametersDoNotExist1() + = equivalent(type(\tuple([\void()]), ()), #void); +test bool tuplesWithVoidParametersDoNotExist2() + = equivalent(type(\tuple([\void(), \void()]), ()), #void); + +// functions +// there can exist no functions that take a void parameter, hence types with those void parameters represent an _empty set_ of values, hence such function types are equivalent to `void` +test bool functionsWithVoidParametersDoNotExist1() = #int(void) == #void; +test bool functionsWithVoidParametersDoNotExist2() + = #int(void, void) == #void; +test bool functionsWithVoidParametersDoNotExist3() + = #int(void a, void b) == #void; + +test bool functionParametersAreCoVariant() + = subtype(#int(int), #int(num)); +test bool functionParametersAreContraVariant() + = subtype(#int(num), #int(int)); +test bool functionsParametersLubUp() + = lub(#int(int), #int(str)) == #int(value); + +test bool functionReturnsAreCoVariant() = subtype(#int(int), #value(int)); + +// lub +test bool lub_value(type[value] s) + = lub(s, #value) == #value && lub(#value, s) == #value; +test bool lub_void1(type[value] s) = lub(s, #void) == s; +test bool lub_void2(type[value] s) = lub(#void, s) == s; + +test bool lub_int_real() = lub(\int(), \real()) == \num(); +test bool lub_int_rat() = lub(\int(), \rat()) == \num(); +test bool lub_real_rat() = lub(\real(), \rat()) == \num(); + +test bool lub_list_set(type[value] s) + = lub(\list(s.symbol), \set(s.symbol)) == \value(); + +test bool lub_intstr() = lub(\int(), \str()) == \value(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/AtColumnRequirement.rsc| +module lang::rascal::tests::concrete::AtColumnRequirement + +import ParseTree; +import Exception; + +lexical C + = C C C + | "a" @ 2 + | "b" + ; + +bool testParse(str input, bool shouldParse) { + try { + parse(#C, input); + } + catch ParseError(_): { + return !shouldParse; + } + + return shouldParse; +} + +test bool testA() = testParse("a", false); + +test bool testBab() = testParse("bab", false); + +test bool testBba() = testParse("bba", true); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Call.rsc| +module lang::rascal::tests::concrete::Call + +lexical ABC + = "a" + | "b" + | "c" + ; + +syntax XYZ + = "x" + | "y" + | "z" + ; + +//keyword KEY = "k" | "e"| | "y"; +test bool dispatchTestLexical() { + int f((ABC) `a`) = 1; + int f((ABC) `b`) = 2; + int f((ABC) `c`) = 3; + + return [f((ABC)`a`), f((ABC)`b`), f((ABC)`c`)] == [1, 2, 3]; +} + +test bool dispatchTestSyntax() { + int f((XYZ) `x`) = 1; + int f((XYZ) `y`) = 2; + int f((XYZ) `z`) = 3; + + return [f((XYZ)`x`), f((XYZ)`y`), f((XYZ)`z`)] == [1, 2, 3]; +} +//@ignoreCompiler{To be analyzed} +//test bool dispatchTestKeyword() { +// int f((KEY) `k`) = 1; +// int f((KEY) `e`) = 2; +// int f((KEY) `y`) = 3; +// +// return [f((KEY)`k`),f((KEY)`e`),f((KEY)`y`)] == [1,2,3]; +//} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/CaseInsensitiveMatching.rsc| +module lang::rascal::tests::concrete::CaseInsensitiveMatching + +lexical CI = 'if' 'fi'; + +test bool matchAnyway1() = (CI) `IFFI` := (CI) `iffi`; +test bool matchAnyway2() = (CI) `IffI` := (CI) `iFFi`; +test bool matchAnyway3() = (CI) `iffi` := (CI) `IFFI`; +test bool matchAnyway4() = (CI) `IFFI` := (CI) `IFFI`; +test bool matchAnyway5() = (CI) `iffi` := (CI) `iffi`; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Character.rsc| +module lang::rascal::tests::concrete::Character + +import ParseTree; +import String; + +lexical Example = ([A-Z] head [a-z]* tail)+ words; + +test bool fieldsFromLexicals() + = ["C", "C", "B", "F"] == ["" | w <- t.words] + when Example t := [Example] "CamelCaseBaseFeest"; + +test bool fieldsFromLexicals2() + = ["amel", "ase", "ase", "eest"] == ["" | w <- t.words] + when Example t := [Example] "CamelCaseBaseFeest"; + +// even though the dynamic type would be list[[CCBF]] == list[[BCF]], the static type is list[[A-Z]]: +test bool staticFieldProjectType() = list[[A-Z]] _ := [w.head | w <- t.words] + when Example t := [Example] "CamelCaseBaseFeest"; + +private bool check(type[&T] _, value x) = &T _ := x; + +test bool singleA() = check(#[A], char(65)); +test bool singleB() = check(#[B], char(66)); +test bool notSingleB() = !check(#[A], char(66)); +test bool singleAB1() = check(#[A-B], char(65)); +test bool singleAB2() = check(#[A-B], char(66)); +test bool notSingleAB() = !check(#[A-B], char(67)); + +test bool charclassLUB() + = set[[A-D]] _ := {char(65), char(66), char(67), char(68)}; +test bool charclassLUB2() = set[[a-z]] _ := {char(i)| i <- [97..122]}; + +list[Tree] characters(str x) = [char(i) | i <- chars(x)]; + +list[![]] produceCharClass() = [w.head | w <- t.words] + when Example t := [Example] "CamelCaseBaseFeest"; + +test bool characterClassSubType() { + [A-Z] head = char(65); + [A-Za-z] tmp = head; + + // assignment into bigger class: always allowed + if ([A-Z] _ := tmp) { + // binding to smaller class should match if it fits + return true; + } + return false; +} + +test bool shortestRangesArePrinted() = "<#![]>" == "![]"; +test bool complementOfNothingIsEverything() + = (#![]).symbol == \char-class([range(1, 0x10FFFF)]); +test bool charClassOrderedRanges() + = (#[a-zA-Z]).symbol == \char-class([range(65, 90), range(97, 122)]); +test bool charClassMergedRanges() + = (#[A-ZF-G]).symbol == \char-class([range(65, 90)]); +test bool charClassExtendedRanges() + = (#[A-MN-Z]).symbol == \char-class([range(65, 90)]); +// ambiguity in this syntax must be resolved first +//test bool differenceCC() = (#[a-zA-Z] - [A-Z]).symbol == (#[a-z]).symbol; +//test bool unionCC() = (#[a-z] || [A-Z]).symbol == (#[A-Za-z]).symbol; +//test bool intersectCC() = (#[A-Za-z] && [A-Z]).symbol == (#[A-Z]).symbol; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/DanglingElse.rsc| +module lang::rascal::tests::concrete::DanglingElse + +import ParseTree; + +layout Layout = [\ \t\n]* !>> [\ \t\n]; + +start syntax Stmt + = "if" "(" "x" ")" Stmt "else" Stmt + | "if" "(" "x" ")" Stmt () !>> "else" + | "{" "}" + ; + +bool expectSuccess(str src) { + try { + parse(#Stmt, src); + return true; + } + catch value _: { + return false; + } +} + +test bool noElse1() = expectSuccess("if (x) {}"); + +test bool noElse2() = expectSuccess("if (x) if (x) {}"); + +test bool noElse3() = expectSuccess("if (x) if (x) if (x) {}"); + +test bool noElse1Trailing() = expectSuccess("if (x) {} "); + +test bool noElse2Trailing() = expectSuccess("if (x) if (x) {} "); + +test bool noElse3Trailing() = expectSuccess("if (x) if (x) if (x) {} "); + +test bool ifElse1() = expectSuccess("if (x) {} else {}"); + +test bool ifElse2() = expectSuccess("if (x) if (x) {} else {}"); + +test bool ifElse3() = expectSuccess("if (x) if (x) if (x) {} else {}"); + +test bool ifElseElse1() = expectSuccess("if (x) {} else {}"); + +test bool ifElseElse2() + = expectSuccess("if (x) if (x) {} else {} else {}"); + +test bool ifElseElse3() + = expectSuccess("if (x) {} else if (x) {} else if (x) {} else {}"); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Field.rsc| +module lang::rascal::tests::concrete::Field + +syntax C = "{" !<< B* bs; +syntax B = "b"; + +test bool stripLabels() { + C ex2 = [C] "bbb"; + return B _ <- ex2.bs; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/FieldProjectionBug.rsc| +module lang::rascal::tests::concrete::FieldProjectionBug + +import ParseTree; + +layout Layout = [\ \r\n]+ !>> [\ \r\n]; +start syntax Expression = (Identifier i | BuiltIn b) function; +lexical Identifier = [a-zA-Z0-9]+ !>> [a-zA-Z0-9] \ Keywords; +lexical BuiltIn = "hoi"; +keyword Keywords = "hoi"; + +test bool prodFieldProjectionOnAnAlternative() { + Tree T = (Expression) `hoi`.function; + return regular(alt(_)) := T.prod; +} + +test bool labeledAlternativeProjection() { + T = (Expression) `hoi`.function; + return BuiltIn _ := T.b; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/FirstAmbiguity.rsc| +module lang::rascal::tests::concrete::FirstAmbiguity + +syntax P = E; +syntax E + = "e" + | E "+" E + ; + +import ParseTree; + +@issue{1868} +test bool firstAmbDoesNotThrowStaticErrors() { + return amb({E _, E _}) := firstAmbiguity(#P, "e+e+e"); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Function.rsc| +module lang::rascal::tests::concrete::Function + +layout L = [\ ]*; +syntax A = "a"; +syntax As = {A ","}+; +syntax G = "g" As; + +int useExtraFormalInConcreteListPattern(int n, (G) `g <{A ","}+ as>`) { + int g() { + x = as; + return n; + } + return g(); +} + +test bool useExtraFormalInConcreteListPattern1() + = useExtraFormalInConcreteListPattern(42, [G] "g a,a,a") == 42; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/IsDefined.rsc| +module lang::rascal::tests::concrete::IsDefined + +syntax As = "a"* as; + +syntax Bs = {"b" ","}* bs; + +// Concrete lists +test bool isDefinedConcrete1() = (([As] "aaa")[0])?; + +test bool isDefinedConcrete2() = !(([As] "aaa")[5])?; + +test bool isDefinedConcrete3() = (([Bs] "b,b,b")[0])?; + +test bool isDefinedConcrete4() = !(([Bs] "b,b,b")[5])?; + +test bool hasConcrete1() = ([As] "aaa") has as; + +test bool hasConcrete2() = !(([As] "aaa") has bs); + +test bool hasConcrete3() = ([Bs] "b,b,b") has bs; + +test bool hasConcrete4() = !(([Bs] "b,b,b") has as); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Issue1913.rsc| +module lang::rascal::tests::concrete::Issue1913 + +extend lang::std::Layout; +import IO; + +syntax As = "begin" A* as "end"; + +syntax A + = "a" + | "b" + ; + +test bool issue1913() { + A* bs = (As)`begin b a b end`.as; + + As prog = (As)`begin b a a end`; + + prog = visit(prog) { + case (As)`begin b end` + => (As)`begin end` + } + + println(prog); + + // don't loose a space + return "" == "begin b a b a a end"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Issue2147.rsc| +module lang::rascal::tests::concrete::Issue2147 + +lexical C = "c"+; + +import ParseTree; + +test bool issue2147_deepMatch() { + // these examples are expected + assert [C] "c" !:= [C] "cc"; + assert /[C] "c" !:= [C] "cc"; + + // now we upcast to `Tree` + Tree c = [C] "c"; + + // now we get something unusual. This should not match... + return /c !:= [C] "cc"; +} + +test bool issue2147_visit() { + // now we upcast to `Tree` + Tree c = [C] "c"; + + visit([C] "cc") { + case c: + return false; + } + + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Layout.rsc| +module lang::rascal::tests::concrete::Layout + +// DO NOT ADD CODE THAT TRIGGERS A PARSER TO THIS MODULE +// WE NEED A MODULE WHICH CAN RUN THE GRAMMAR NORMALIZER LIKE THIS WITHOUT TRIGGERING THE GENERATOR +import ParseTree; + +layout Manual = @manual "***"; +layout Auto = [\ \t\n\r]*; + +syntax A = "a" "b" "c"; +syntax B = "a" Manual "b" "c"; +syntax C = {"a" ","}*; +syntax D = {"a" (Manual "," Manual)}*; +syntax E = A a "x" B b; + +test bool layoutA() + = /prod( + sort("A"), + [lit("a"), layouts("Auto"), lit("b"), layouts("Auto"), lit("c")], _ + ) := #A.definitions[sort("A")]; +test bool layoutB() + = /prod( + sort("B"), + [lit("a"), layouts("Manual"), lit("b"), layouts("Auto"), lit("c")], _ + ) := #B.definitions[sort("B")]; +test bool layoutC() + = /prod( + sort("C"), + [\iter-star-seps(lit("a"), [layouts("Auto"), lit(","), layouts("Auto")])], + _ + ) := #C.definitions[sort("C")]; +test bool layoutD() + = /prod( + sort("D"), + [\iter-star-seps( + lit("a"), [seq([layouts("Manual"), lit(","), layouts("Manual")])] + )], _ + ) := #D.definitions[sort("D")]; +test bool layoutE() + = /prod( + sort("E"), + [label("a", sort("A")), layouts("Auto"), lit("x"), + layouts("Auto"), label("b", sort("B"))], _ + ) := #E.definitions[sort("E")]; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Locations.rsc| +module lang::rascal::tests::concrete::Locations + +import ParseTree; +import IO; + +syntax A = "a"; +syntax E + = A + | "(" E "+" E ")" + | "(" E "*" E ")" + ; + +value main() = [A] "a"; + +test bool concreteExpressionsHaveSourceLocations1a() = (A) `a`.src?; + +test bool parsedExpressionsHaveSourceLocations1a() = ([A] "a").src?; + +test bool concreteExpressionsHaveSourceLocations1b() = (E) `(a+a)`.src?; + +test bool parsedExpressionsHaveSourceLocations1b() = ([E] "(a+a)").src?; + +test bool concreteExpressionsHaveSourceLocations1c() + = (E) `(a+(a*a))`.src?; + +test bool parsedExpressionsHaveSourceLocations1c() + = ([E] "(a+(a*a))").src?; + +test bool concreteExpressionsHaveSourceLocations2a() + = (A) `a`.src.length == 1; + +test bool parsedExpressionsHaveSourceLocations2a() + = ([A] "a").src.length == 1; + +test bool concreteExpressionsHaveSourceLocations2b() + = (E) `(a+a)`.src.length == 5; + +test bool parsedExpressionsHaveSourceLocations2b() + = ([E] "(a+a)").src.length == 5; + +test bool concreteExpressionsHaveSourceLocations2c() + = (E) `(a+(a*a))`.src.length == 9; + +test bool parsedExpressionsHaveSourceLocations2c() + = ([E] "(a+(a*a))").src.length == 9; + +test bool concreteExpressionsHaveSourceLocationsLegacy1a() + = (A) `a`@\loc?; + +test bool parsedExpressionsHaveSourceLocationsLegacy1a() + = ([A] "a")@\loc?; + +test bool concreteExpressionsHaveSourceLocationsLegacy1b() + = (E) `(a+a)`@\loc?; + +test bool parsedExpressionsHaveSourceLocationsLegacy1b() + = ([E] "(a+a)")@\loc?; + +test bool concreteExpressionsHaveSourceLocationsLegacy1c() + = (E) `(a+(a*a))`@\loc?; + +test bool parsedExpressionsHaveSourceLocationsLegacy1c() + = ([E] "(a+(a*a))")@\loc?; + +test bool concreteExpressionsSourceLocationsLengthLegacy1a() + = (A) `a`@\loc.length == 1; + +test bool parsedExpressionsSourceLocationsLengthLegacy1a() + = ([A] "a")@\loc.length == 1; + +test bool concreteExpressionsSourceLocationsLength2a() + = (E) `(a+a)`.src.length == 5; + +test bool parsedExpressionsSourceLocationsLength1a() + = ([E] "(a+a)").src.length == 5; + +test bool concreteExpressionsSourceLocationsLength1a() + = (E) `(a+(a*a))`.src.length == 9; + +test bool parsedExpressionsSourceLocationsLength2a() + = ([E] "(a+(a*a))").src.length == 9; + +test bool concreteExpressionsSourceLocationsLength2b() + = (E) `(a+(a*a))`.src.length == 9; + +test bool concreteExpressionsSourceLocationsCorrect1() + = readFile((A) `a`.src) == "a"; + +test bool concreteExpressionsSourceLocationsCorrect2() + = readFile((E) `(a+a)`.src) == "(a+a)"; + +test bool concreteExpressionsSourceLocationsCorrect3() + = readFile((E) `(a+(a*a))`.src) == "(a+(a*a))"; + +test bool concreteExpressionsAssignSourceLocation1() { + x = (A) `a`; + x.src.length = 100; + return x.src.length == 100; +} + +test bool concreteExpressionsAssignSourceLocationLegacy1() { + x = (A) `a`; + y = x@\loc[length = 100]; + return y.length == 100; +} + +test bool concreteExpressionsHaveSourceLocationsAfterVisitWithMatch() { + // assert /int _ := (A) `a`; // Removed assert since not relevant for the test itself + t = visit((A) `a`) { + case int i => i + 1 + } + + return t@\loc?; +} + +test bool concreteExpressionsHaveSourceLocationsAfterVisitWithNoMatch() { + t = visit((A) `a`) { + case 1239461234912634 => 123498761234896123 + } + + return t@\loc?; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Matching.rsc| +module lang::rascal::tests::concrete::Matching + +import List; +import Node; +import ParseTree; + +syntax A = a: "a"; + +syntax As = as: A+ alist; + +syntax C = c: A a "x" As as; + +syntax D + = d: "d" + | e: "e" D d + ; + +syntax Ds = ds: {D ","}+ dlist; + +syntax X + = "plus" {A ","}+ l1 + | "star" {A ","}* l2 + ; + +layout W = [\ ]*; + +int f({A ","}* l) = size([x | A x <- l]); + +test bool plusToStar() = f(([X] "plus a,a").l1) == 2; + +test bool plusToStarIndirect() { + {A ","}+ x = ([X] "plus a,a").l1; + + return f(x) == 2; +} + +test bool testIs() { + pt = parse(#A, "a"); + return a() := pt && pt is a; +} + +test bool testAs() { + pt = parse(#As, "aaa"); + return as(al) := pt && pt is as && pt.alist == al; +} + +test bool testMatchC() { + pt = parse(#C, "axaaa"); + return c(A _, As _) := pt; +} + +test bool testFieldSelectC() { + pt = parse(#C, "axaaa"); + return c(A a, As _) := pt && pt.a == a; +} +test bool testFieldSelectC2() { + pt = parse(#C, "axaaa"); + return c(A _, As as) := pt && pt.as == as; +} + +@ignoreInterpreter{Feature is not implemented} +test bool testConcreteListC1() { + pt = parse(#C, "axaaa"); + return c(A _, As as) := pt && unsetRec(as.alist[0]) == unsetRec([A] "a"); +// unset src keywords +} + +test bool testConcreteListC2() { + pt = parse(#C, "axaaa"); + return c(A _, As as) := pt && size([x | x <- as.alist]) == 3; +} + +@ignoreInterpreter{Feature is not implemented} +test bool testConcreteListD1() { + pt = parse(#Ds, "d,d"); + return Ds ds := pt && (unsetRec(ds.dlist[0]) == unsetRec([D] "d")); +// unset src keywords +} + +test bool testConcreteListD2() { + pt = parse(#Ds, "d,d"); + return Ds ds := pt && size([x | x <- ds.dlist]) == 2; +} + +bool dispatch(e(D _)) = true; +bool dispatch(d()) = false; + +test bool dispatchTest() = dispatch((D) `ed`) && !dispatch((D) `d`); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/ModulesWithStoredParsers.rsc| +module lang::rascal::tests::concrete::ModulesWithStoredParsers + +import util::Reflective; +import IO; +import ParseTree; +import util::Math; +import lang::rascal::grammar::storage::ModuleParserStorage; + +lexical W = [\ ]; +layout L = W*; +lexical A = [A]; +syntax As = A+; + +test bool storeParserNonModule() { + storeParsers(#As, |memory://test-tmp/parsersA.jar|); + p = loadParsers(|memory://test-tmp/parsersA.jar|); + + return + p(type(sort("As"), ()), "A A", |origin:///|) == parse(#As, "A A", |origin:///|); +} + +loc constructExampleProject() { + root = |memory://test-tmp/example-prj-< "" >|; + newRascalProject(root); + return root; +} + +// fix for target scheme not working for "non-existing" projects +PathConfig getTestPathConfig(loc root) { + pcfg = getProjectPathConfig(root); + pcfg.bin = root + "target/classes"; + + // remove std to avoid generating parsers for all modules in the library that contain syntax definitions + pcfg.srcs -= [|std:///|]; + return pcfg; +} + +test bool storeModuleParsersWorkedSimpleGrammar() { + root = constructExampleProject(); + writeFile( + root + "src/main/rascal/A.rsc", "module A + 'lexical A = [A]; + " + ); + + storeParsersForModules(getTestPathConfig(root)); + + return exists(root + "target/classes/A.parsers"); +} + +test bool storeModuleParsersWorkedForBiggerGrammar() { + root = constructExampleProject(); + writeFile( + root + "src/main/rascal/A.rsc", + "module A + 'lexical W = [\\ ]; + 'layout L = W*; + 'lexical A = [A]; + 'syntax As = A+; + " + ); + + storeParsersForModules(getTestPathConfig(root)); + + return exists(root + "target/classes/A.parsers"); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/OtherSyntax.rsc| +module lang::rascal::tests::concrete::OtherSyntax + +syntax Remote = "remote"; + +type[Remote] getRemoteGrammar() = #Remote; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/ParameterizedNonTerminals.rsc| +module lang::rascal::tests::concrete::ParameterizedNonTerminals + +layout Whitespace = [\ \t\n]*; +syntax A = "a"; +syntax B = "b"; + +syntax P[&T] = "{" &T ppar "}"; + +syntax Q[&T] = "[" P[&T] qpar "]"; + +syntax R[&T] = "(" Q[&T] rpar ")"; + +syntax PA = P[A] papar; + +syntax QA = Q[A] qapar; + +syntax RA = R[A] rapar; + +test bool PA1() = "<(PA) `{a}`>" == "{a}"; + +test bool PA2() = "<[PA] "{a}">" == "{a}"; + +test bool PA3() { + PA pa = (PA) `{a}`; + return "" == "{a}"; +} + +test bool PA4() { + PA pa1 = (PA) `{a}`; + PA pa2 = [PA] "{a}"; + return "" == ""; +} + +test bool PA5() { + PA pa1 = (PA) `{a}`; + PA pa2 = [PA] "{a}"; + return pa1 := pa2 && pa2 := pa1; +} + +test bool PA5a() = "<((PA) `{a}`).papar.ppar>" == "a"; + +test bool PA6() = "<(P[A]) `{a}`>" == "{a}"; + +test bool PA7() = "<(P[A]) `{a}`>" == "<(PA) `{a}`>"; + +test bool PA8() = "<(P[A]) `{a}`>" == "<[PA] "{a}">"; + +@ignoreInterpreter{Gives: Undeclared type: P} +test bool PA9() = "<(P[A]) `{a}`>" == "<[P[A]] "{a}">"; + +@ignoreInterpreter{Gives: Syntax error: concrete syntax fragment} +test bool PB1() = "<(P[B]) `{b}`>" == "{b}"; + +@ignoreInterpreter{Gives: Syntax error: concrete syntax fragment} +test bool PB2() = "<[P[B]] "{b}">" == "{b}"; + +test bool QA1() = "<(QA) `[{a}]`>" == "[{a}]"; +test bool QA2() = "<[QA] "[{a}]">" == "[{a}]"; +test bool QA3() = "<((QA) `[{a}]`).qapar.qpar.ppar>" == "a"; + +test bool RA1() = "<(RA) `([{a}])`>" == "([{a}])"; +test bool RA2() = "<[RA] "([{a}])">" == "([{a}])"; +test bool RA3() = "<((RA) `([{a}])`).rapar.rpar.qpar.ppar>" == "a"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/ParseTreeEquality.rsc| +module lang::rascal::tests::concrete::ParseTreeEquality + +import ParseTree; + +bool testCycleEquality() { + Tree cycle1 = cycle(sort("X"), 3); + Tree cycle2 = cycle(sort("X"), 3); + + return cycle1 == cycle2; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Parsing.rsc| +module lang::rascal::tests::concrete::Parsing + +import ParseTree; +import Exception; +import IO; +import lang::rascal::tests::concrete::OtherSyntax; + +start syntax A = "a"; +layout WS = [\ \t\n\r]*; + +start syntax B + = "b" + | [a-z] + ; + +test bool strExpr() = (A) `a` := parse(#A, "a"); + +test bool allowAmb() = /amb(_) := parse(#B, "b", allowAmbiguity = true); + +test bool disallowAmb() { + try { + parse(#B, "b"); + return false; + // should have thrown Ambiguity exception + } + catch Ambiguity(_, _, _): + return true; +} + +test bool disallowAmb2() { + try { + parse(#B, "b", allowAmbiguity = false); + return false; + // should have thrown Ambiguity exception + } + catch Ambiguity(_, _, _): + return true; +} + +@ignoreCompiler{FIX: TC does not yet allow [A] loc} +test bool locExpr() { + writeFile(|memory://test-tmp/locExpr.txt|, "a"); + return + [A] |memory://test-tmp/locExpr.txt| == parse(#A, |memory://test-tmp/locExpr.txt|); +} + +test bool parsingWithADynamicGrammar() + = B _ := parse(visit(#B) { + case "b" => "bbb" + }, "bbb"); + +test bool parsingWithAGrammarFromADifferentModule() + = Remote _ := parse(getRemoteGrammar(), "remote"); + +test bool parsingWithAParameterGrammar() { + Tree p(type[&T <: Tree] gr, str s) = parse(gr, s); + + return Tree _ := p(#B, "a"); +} + +test bool parsingWithARemoteParameterGrammar() { + Tree p(type[&T <: Tree] gr, str s) = parse(gr, s); + + return Tree _ := p(getRemoteGrammar(), "remote"); +} + +test bool parsingWithAManualGrammar() + = type[Tree] gr := + type(sort("MySort"), ( + sort("MySort") : choice(sort("MySort"), {prod(sort("MySort"), [lit("hello")], {})}) + )) + && Tree t := parse(gr, "hello") + && "" == "hello"; + +@ignoreCompiler{Fails because the type Symbol is assigned to type(...), bust assigning value() breaks other code} +test bool saveAndRestoreParser() { + storeParsers(#start[A], |memory://test-tmp/parsers.jar|); + p = loadParsers(|memory://test-tmp/parsers.jar|); + + return + p(type(\start(sort("A")), ()), "a", |origin:///|) == parse(#start[A], "a", |origin:///|); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Patterns1.rsc| +module lang::rascal::tests::concrete::Patterns1 + +import ParseTree; + +syntax A = "a"; +syntax As0 = A* as0; +syntax As1 = A+ as1; + +syntax B = "b"; +syntax Bs = B* bs0; + +syntax AB = (A | B)*; +syntax ABs = (As0 | Bs)*; +syntax A2 = [A]+ !>> [A]; +syntax B2 = [B]+ !>> [B]; +syntax AB2 = (A2 | B2)*; + +syntax C = "c"; +syntax Cs0 = {C ","}* cs0; +syntax Cs1 = {C ","}+ cs1; + +syntax AroundCs0 = "OPENCs0" {C ","}* "CLOSE"; +syntax AroundCs1 = "OPENCs1" {C ","}+ "CLOSE"; + +syntax D = "d"; +syntax Ds = {D ","}* ds; + +lexical E = "e"; +lexical Es = {E ","}* es; + +syntax DE = D d E e; + +lexical F = "f"; +lexical Fs = F* fs; + +lexical EF = (E | F)*; +lexical EFs = (Es | Fs)*; + +start syntax XorY + = x: "x" + | y: "y" + ; + +lexical Layout = [.;]; +layout L = Layout* !>> [.;]; + +lexical MyName = ([A-Za-z_] !<< [A-Z_a-z] [0-9A-Z_a-z]* !>> [0-9A-Z_a-z]); +lexical Mies = ([ab] [cd]); +syntax Noot = (("a" | "b") ("c" | "d")); + +syntax M[&T] = "[[" &T "]]"; + +syntax MA = M[A]; +syntax MB = M[B]; + +syntax N[&T, &U] = "\<\<" &T "," &U "\>\>"; + +syntax NAB = N[A, B]; + +test bool concreteMatchA1() = (A) `a` := [A] "a"; +test bool concreteMatchA2() = (A) `` := [A] "a"; + +test bool concreteMatchDE1() = (DE) `de` := [DE] "de"; +test bool concreteMatchDE2() = (DE) `d.e` := [DE] "de"; +test bool concreteMatchDE3() = (DE) `d.e` := [DE] "d.e"; +test bool concreteMatchDE4() = (DE) `de` := [DE] "d.e"; +test bool concreteMatchDE5() = (DE) `e` := [DE] "d.e"; + +test bool concreteMatchAs101() = (As1) `a` := [As1] "a"; +test bool concreteMatchAs102() = (As1) `aa` := [As1] "aa"; +test bool concreteMatchAs103() = (As1) `a.a` := [As1] "aa"; +test bool concreteMatchAs104() = (As1) `a.a` := [As1] "a.a"; +test bool concreteMatchAs105() = (As1) `aa` := [As1] "a.a"; +test bool concreteMatchAs106() = (As1) `` := [As1] "a.a"; + +test bool concreteMatchAs107() = (As1) `` := [As1] "a" && "" == "a"; +test bool concreteMatchAs108() + = (As1) `` := [As1] "aa" && "" == "aa"; +test bool concreteMatchAs109() + = (As1) `` := [As1] "aaa" && "" == "aaa"; +test bool concreteMatchAs110() + = (As1) `a` := [As1] "aa" && "" == "a"; +test bool concreteMatchAs111() + = (As1) `a` := [As1] "a.a" && "" == "a"; +test bool concreteMatchAs112() + = (As1) `aa` := [As1] "aaa" && "" == "a"; +test bool concreteMatchAs113() + = (As1) `aa` := [As1] "a.a.a" && "" == "a"; + +test bool concreteMatchAs114() + = (As1) `a` := [As1] "aa" && "" == "a"; +test bool concreteMatchAs115() + = (As1) `a` := [As1] "a.a" && "" == "a"; +test bool concreteMatchAs116() + = (As1) `aa` := [As1] "aaa" && "" == "a"; +test bool concreteMatchAs117() + = (As1) `aa` := [As1] "a.a.a" && "" == "a"; + +test bool concreteMatchAs118() + = (As1) `aa` := [As1] "aaa" && "" == "a"; +test bool concreteMatchAs119() + = (As1) `aa` := [As1] "a.a.a" && "" == "a"; +test bool concreteMatchAs120() + = (As1) `aa` := [As1] "aaaa" && "" == "aa"; +test bool concreteMatchAs121() + = (As1) `aa` := [As1] "a.aa.a" && "" == "aa"; + +test bool concreteMatchAs122() + = (As1) `aa` := [As1] "aaaa" && "" == "a" && "" == "a"; +test bool concreteMatchAs123() + = (As1) `aa` := [As1] "aaaaa" + && "" == "a" + && "" == "aa"; + +test bool concreteMatchBs01() = (Bs) `` := [Bs] "" && "" == ""; +test bool concreteMatchBs02() = (Bs) `` := [Bs] "b" && "" == "b"; +test bool concreteMatchBs03() = (Bs) `` := [Bs] "bb" && "" == "bb"; + +test bool concreteMatchBs04() = (Bs) `b` := [Bs] "b" && "" == ""; +test bool concreteMatchBs05() = (Bs) `b` := [Bs] "bb" && "" == "b"; + +test bool concreteMatchBs06() + = (Bs) `b` := [Bs] "bbbb" && "" == "bbb"; +test bool concreteMatchBs07() + = (Bs) `bb` := [Bs] "bbbb" && "" == "bb"; +test bool concreteMatchBs08() + = (Bs) `bbb` := [Bs] "bbbb" && "" == "b"; +test bool concreteMatchBs09() + = (Bs) `bbbb` := [Bs] "bbbb" && "" == ""; + +test bool concreteMatchBs10() + = (Bs) `` := [Bs] "" && "" == "" && "" == ""; +test bool concreteMatchBs11() + = (Bs) `bbb` := [Bs] "bbbb" && "" == "" && "" == "b"; +test bool concreteMatchBs12() + = (Bs) `bbb` := [Bs] "bbbbb" && "" == "" && "" == "bb"; +test bool concreteMatchBs13() + = (Bs) `bbb` := [Bs] "bbb" && "" == "" && "" == ""; + +test bool concreteMatchCs101() + = (Cs1) `<{C ","}+ cs>` := [Cs1] "c,c,c,c" && "" == "c,c,c,c"; +test bool concreteMatchCs102() + = (Cs1) `c,<{C ","}+ cs>` := [Cs1] "c,c,c,c" && "" == "c,c,c"; +test bool concreteMatchCs103() + = (Cs1) `c,c,<{C ","}+ cs>` := [Cs1] "c,c,c,c" && "" == "c,c"; + +test bool concreteMatchCs104() + = (Cs1) `<{C ","}+ cs>,c` := [Cs1] "c,c,c,c" && "" == "c,c,c"; +test bool concreteMatchCs105() + = (Cs1) `<{C ","}+ cs>,c,c` := [Cs1] "c,c,c,c" && "" == "c,c"; +test bool concreteMatchCs106() + = (Cs1) `<{C ","}+ cs>,c,c,c` := [Cs1] "c,c,c,c" && "" == "c"; +test bool concreteMatchCs107() + = (Cs1) `<{C ","}+ _>,c,c,c,c` !:= [Cs1] "c,c,c,c"; + +test bool concreteMatchCs108() + = (Cs1) `c,<{C ","}+ cs>,c` := [Cs1] "c,c,c" && "" == "c"; +test bool concreteMatchCs109() + = (Cs1) `c,<{C ","}+ cs>,c` := [Cs1] "c,c,c,c" && "" == "c,c"; + +test bool concreteMatchCs110() + = (Cs1) `<{C ","}+ cs1>,<{C ","}+ cs2>` := [Cs1] "c,c" + && "" == "c" + && "" == "c"; +test bool concreteMatchCs111() + = (Cs1) `c,<{C ","}+ cs1>,<{C ","}+ cs2>` := [Cs1] "c,c,c" + && "" == "c" + && "" == "c"; + +test bool concreteMatchCs112() + = (Cs1) `<{C ","}+ cs1>,c,<{C ","}+ cs2>` := [Cs1] "c,c,c,c,c" + && "" == "c" + && "" == "c,c,c"; +test bool concreteMatchCs113() + = (Cs1) `<{C ","}+ cs1>,<{C ","}+ cs2>,c` := [Cs1] "c,c,c,c,c" + && "" == "c" + && "" == "c,c,c"; + +test bool concreteMatchCs114() + = (Cs1) `c,<{C ","}+ cs1>,c,<{C ","}+ cs2>,c` := [Cs1] "c,c,c,c,c" + && "" == "c" + && "" == "c"; + +test bool concreteMatchDs01() = (Ds) `<{D ","}* ds>` := [Ds] "" && "" == ""; +test bool concreteMatchDs02() + = (Ds) `<{D ","}* ds>` := [Ds] "d,d,d,d" && "" == "d,d,d,d"; +test bool concreteMatchDs03() + = (Ds) `<{D ","}* ds>,d` := [Ds] "d,d,d,d" && "" == "d,d,d"; +test bool concreteMatchDs04() + = (Ds) `<{D ","}* ds>,d,d` := [Ds] "d,d,d,d" && "" == "d,d"; +test bool concreteMatchDs05() + = (Ds) `d,<{D ","}* ds>,d` := [Ds] "d,d,d,d" && "" == "d,d"; +test bool concreteMatchDs06() + = (Ds) `d,d,<{D ","}* ds>` := [Ds] "d,d,d,d" && "" == "d,d"; +test bool concreteMatchDs07() + = (Ds) `d,d,<{D ","}* ds>,d` := [Ds] "d,d,d,d" && "" == "d"; + +test bool concreteMatchDs08() + = (Ds) `<{D ","}* ds1>,<{D ","}* ds2>` := [Ds] "" + && "" == "" + && "" == ""; +test bool concreteMatchDs09() + = (Ds) `d,<{D ","}* ds1>,<{D ","}* ds2>` := [Ds] "d" + && "" == "" + && "" == ""; +test bool concreteMatchDs10() + = (Ds) `<{D ","}* ds1>,d,<{D ","}* ds2>` := [Ds] "d" + && "" == "" + && "" == ""; +test bool concreteMatchDs11() + = (Ds) `<{D ","}* ds1>,<{D ","}* ds2>,d` := [Ds] "d" + && "" == "" + && "" == ""; +test bool concreteMatchDs12() + = (Ds) `<{D ","}* ds1>,d,d,<{D ","}* ds2>,d` := [Ds] "d,d,d,d,d" + && "" == "" + && "" == "d,d"; +test bool concreteMatchDs13() + = (Ds) `<{D ","}* ds1>,d,d,d,<{D ","}* ds2>` := [Ds] "d,d,d,d,d" + && "" == "" + && "" == "d,d"; + +test bool concreteListEnum1() = ["" | B x <- ((Bs) ``).bs0] == []; +test bool concreteListEnum2() = ["" | B x <- ((Bs) `b`).bs0] == ["b"]; +test bool concreteListEnum3() + = ["" | B x <- ((Bs) `bbbbb`).bs0] == ["b", "b", "b", "b", "b"]; +test bool concreteListEnum4() = ["" | D x <- ((Ds) ``).ds] == []; +test bool concreteListEnum5() = ["" | D x <- ((Ds) `d`).ds] == ["d"]; +test bool concreteListEnum6() + = ["" | D x <- ((Ds) `d,d,d,d,d`).ds] == ["d", "d", "d", "d", "d"]; + +test bool lexicalListEnum1() + = ["" | E x <- ((Es) `e,e,e,e,e,e,e`).es] == ["e", "e", "e", "e", "e", "e", "e"]; +test bool lexicalListEnum2() + = ["" | F x <- ((Fs) `ffffff`).fs] == ["f", "f", "f", "f", "f", "f"]; + +test bool lexicalSequenceMatch1() = (Mies) `ac` := (Mies) `ac`; +test bool lexicalSequenceMatch2() = (Mies) `ac` := [Mies] "ac"; +test bool lexicalSequenceMatch3() = (Mies) `ac` !:= (Mies) `ad`; +test bool lexicalSequenceMatch4() = (Mies) `ac` !:= [Mies] "ad"; + +test bool syntaxSequenceMatch1() = (Noot) `ac` := (Noot) `ac`; +test bool syntaxSequenceMatch2() = (Noot) `ac` := [Noot] "ac"; +test bool syntaxSequenceMatch3() = (Noot) `ac` !:= (Noot) `ad`; +test bool syntaxSequenceMatch4() = (Noot) `ac` !:= [Noot] "ad"; + +test bool lexicalTokenMatch1() + = (MyName) `location` := (MyName) `location`; +test bool lexicalTokenMatch2() = (MyName) `location` := [MyName] "location"; + +test bool concreteMatchVisit1() { + result = false; + visit([A] "a") { + case (A)``: + result = true; + } + return result; +} +test bool concreteMatchVisit2() { + result = 0; + visit([As1] "aaa") { + case (A)``: + result += 1; + } + return result == 3; +} + +@ignoreInterpreter{While this should work, the fix is too large, and there are workarounds} +test bool concreteMatchVisitLayout() { + result = false; + visit([start[XorY]] ".x.") { + case (Layout)`.`: + result = true; + } + return result; +} + +test bool concreteReplaceInLayout() { + result = visit([start[XorY]] ".x;") { + case (Layout)`.` => (Layout)`;` + } + return result := [start[XorY]] ";x;"; +} + +test bool concreteMatchWithStart() = /XorY _ := [start[XorY]] ";x;"; + +test bool concreteSwitch1() { + switch([XorY] "x") { + case (XorY) `x`: + return true; + } + return false; +} + +test bool concreteSwitch2() { + switch([XorY] "x") { + case (XorY) `x`: + return true; + case (XorY) `y`: + return false; + } + return false; +} + +test bool concreteSwitch3() { + switch([XorY] "y") { + case (XorY) `x`: + return false; + case (XorY) `y`: + return true; + } + return false; +} + +test bool concreteSwitch4() { + switch([XorY] "y") { + case x(): + throw "fail to due extra match"; + case (XorY) `y`: + return true; + } + throw "fail due to missing match"; +} + +test bool concreteSwitch5() { + switch([XorY] "y") { + case (XorY) `x`: + throw "fail to due extra match"; + case y(): + return true; + } + throw "fail due to missing match"; +} + +test bool concreteSwitch6() { + switch([XorY] "y") { + case x(): + throw "fail to due extra match"; + case y(): + return true; + } + throw "fail due to missing match"; +} + +test bool concreteSwitch7() { + switch([As1] "aaa") { + case (As1) ``: + return "" == "aaa"; + default: + return false; + } +//throw "fail due to missing match"; +} + +test bool concreteSwitch8() { + switch([As1] "a.a.a") { + case (As1) ``: + return "" == "a.a.a"; + default: + return false; + } +//throw "fail due to missing match"; +} + +test bool concreteSwitch9() { + switch([Bs] "bbb") { + case (Bs) ``: + return "" == "bbb"; + default: + return false; + } +//throw "fail due to missing match"; +} + +test bool concreteSwitch10() { + switch([Bs] "b..b..b") { + case (Bs) ``: + return "" == "b..b..b"; + default: + return false; + } +//throw "fail due to missing match"; +} + +test bool concreteSwitch11() { + switch([Cs1] "c,c,c,c") { + case (Cs1) `<{C ","}+ cs>`: + return "" == "c,c,c,c"; + default: + return false; + } +//throw "fail due to missing match"; +} + +test bool concreteSwitch12() { + switch([Cs1] "c.,.c.,.c.,.c") { + case (Cs1) `<{C ","}+ cs>`: + return "" == "c.,.c.,.c.,.c"; + default: + return false; + } +//throw "fail due to missing match"; +} + +test bool concreteSwitch13() { + switch([Ds] "d,d,d,d") { + case (Ds) `<{D","}* ds>`: + return "" == "d,d,d,d"; + default: + return false; + } +//throw "fail due to missing match"; +} + +test bool concreteSwitch14() { + switch([Ds] "d.,.d.,.d.,.d") { + case (Ds) `<{D","}* ds>`: + return "" == "d.,.d.,.d.,.d"; + default: + return false; + } +//throw "fail due to missing match"; +} + +test bool matchInsideLexicalCyclicGrammar1() = /E _ := [EFs] "eefef"; + +test bool matchInsideLexical() = /E _ := [EF] "eefef"; + +test bool matchInsideSyntaxCyclicGrammar2() = /A _ := [ABs] "bbaab"; + +test bool matchInsideSyntax() = /A _ := [AB] "bbaab"; + +test bool matchInsideSyntax2() = /A2 _ := [AB2] "AABBAA"; + +// Calls with concrete parameters +int cntAs0(A* as) = size([a | A a <- as]); + +int cntAs1(A+ as) = cntAs0(as); + +test bool callAs1() = cntAs0(([As0] "").as0) == 0; +test bool callAs2() = cntAs0(([As0] "a").as0) == 1; +test bool callAs3() = cntAs0(([As0] "a..a").as0) == 2; + +test bool callAs4() = cntAs1(([As1] "a").as1) == 1; +test bool callAs5() = cntAs1(([As1] "a..a").as1) == 2; + +int cntCs0({C ","}* cs) = size([c | C c <- cs]); + +int cntCs1({C ","}* cs) = cntCs0(cs); + +test bool callCs1() = cntCs0(([Cs0] "").cs0) == 0; +test bool callCs2() = cntCs0(([Cs0] "c").cs0) == 1; +test bool callCs3() = cntCs0(([Cs0] "c..,..c").cs0) == 2; + +test bool callCs4() = cntCs1(([Cs1] "c").cs1) == 1; +test bool callCs5() = cntCs1(([Cs1] "c..,..c").cs1) == 2; + +int cntAroundCs0((AroundCs0) `OPENCs0<{C ","}* cs0>CLOSE`) + = cntCs0(cs0); + +test bool around1() = cntAroundCs0([AroundCs0] "OPENCs0CLOSE") == 0; + +test bool around2() = cntAroundCs0([AroundCs0] "OPENCs0.c.CLOSE") == 1; + +test bool around3() = cntAroundCs0([AroundCs0] "OPENCs0.c.,.c.CLOSE") == 2; + +int cntAroundCs1((AroundCs1) `OPENCs1<{C ","}+ cs1>CLOSE`) + = cntCs0(cs1); + +test bool around4() = cntAroundCs1([AroundCs1] "OPENCs1.c.CLOSE") == 1; + +test bool around5() = cntAroundCs1([AroundCs1] "OPENCs1.c.,.c.CLOSE") == 2; + +// test for issue #834 +int cntTrees(list[A] trees) = ( 0 | it + 1 | A _ <- trees ); +test bool callNoTree() = cntTrees([]) == 0; +test bool callOneTree() = cntTrees([[A] "a"]) == 1; +test bool callTwoTrees() = cntTrees([[A] "a", [A] "a"]) == 2; +test bool callTreeTrees() = cntTrees([[A] "a", [A] "a", [A] "a"]) == 3; + +void f([1, *int L, 3]) { +} + +test bool listArgAndEmptyBody() { + f([1, 2, 2, 3]); + return true; +} + +// Descendant in parameterized concrete sort +test bool descentInParameterizedSort1() = /A _ := (M[A]) `[[a]]`; +test bool descentInParameterizedSort2() = /A _ !:= (M[B]) `[[b]]`; + +test bool descentInParameterizedSort3() = /B _ := (M[B]) `[[b]]`; +test bool descentInParameterizedSort4() = /B _ !:= (M[A]) `[[a]]`; + +test bool descentInParameterizedSort5() = /A _ := (N[A,B]) `\<\\>`; +test bool descentInParameterizedSort6() = /B _ := (N[A,B]) `\<\\>`; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Patterns2.rsc| +module lang::rascal::tests::concrete::Patterns2 + +import ParseTree; +layout Whitespace = [\ ]* !>> [\ ]; +lexical IntegerLiteral = [0-9]+; +lexical Identifier = [a-z]+; + +syntax Exp + = IntegerLiteral + | Identifier + | bracket "(" Exp ")" + > left Exp "*" Exp + > left Exp "+" Exp + | Exp "==" Exp + ; + +syntax Stat + = Identifier ":=" Exp + | "if" Exp "then" {Stat ";"}* "else" {Stat ";"}* "fi" + ; + +test bool concreteMatch201() = (Exp) `1` := [Exp] "1"; +test bool concreteMatch202() = (Exp) `1*2` := [Exp] "1*2"; +test bool concreteMatch203() = (Exp) `1 *2` := [Exp] "1* 2"; +test bool concreteMatch204() = (Exp) `1==2` := [Exp] "1==2"; + +test bool concreteMatch205() = (Exp) `` := [Exp] "1"; + +test bool concreteMatch206() = (Stat) `a:=1` := [Stat] "a:=1"; +test bool concreteMatch207() + = (Stat) ` := ` := [Stat] "a:= 1" + && "" == "a" + && "" == "1"; +test bool concreteMatch208() + = (Stat) ` := + ` := [Stat] "a:= 10 + 20" + && "" == "a" + && "" == "10" + && "" == "20"; +test bool concreteMatch209() + = (Stat) `if x then a := 1 else b:=2 fi` := + [Stat] "if x then a := 1 else b:=2 fi"; +test bool concreteMatch210() + = (Stat) `if then <{Stat ";"}* th> else <{Stat ";"}* el> fi` := + [Stat] "if x then a := 1;b:=2 else c:=3 fi" + && "" == "x" + && "" == "a := 1;b:=2" + && "" == "c:=3"; + +test bool concreteMatch211() + = (Stat) `if then <{Stat ";"}* th> else := fi` + := [Stat] "if x then a := 1;b:=2 else c:=3 fi" + && "" == "x" + && "" == "a := 1;b:=2" + && "" == "c" + && "" == "3"; +test bool concreteMatch212() + = (Stat) `if then a:=1;<{Stat ";"}* th> else <{Stat ";"}* el> fi` := + [Stat] "if x then a := 1;b:=2 else c:=3 fi" + && "" == "x" + && "" == "b:=2" + && "" == "c:=3"; +test bool concreteMatch213() + = (Stat) `if then a:=1;<{Stat ";"}* th>;b:=2 else <{Stat ";"}* el> fi` + := [Stat] "if x then a := 1;b:=2 else c:=3 fi" + && "" == "x" + && "" == "" + && "" == "c:=3"; +test bool concreteMatch214() + = (Stat) `if then a:=1;b:=2;<{Stat ";"}* th> else <{Stat ";"}* el> fi` + := [Stat] "if x then a := 1;b:=2 else c:=3 fi" + && "" == "x" + && "" == "" + && "" == "c:=3"; +test bool concreteMatch215() + = (Stat) `if then <{Stat ";"}* th1>;a := 1;<{Stat ";"}* th2> else <{Stat ";"}* el> fi` + := [Stat] "if x then a := 1;b:=2 else c:=3 fi" + && "" == "" + && "" == "b:=2" + && "" == "c:=3"; +test bool concreteMatch216() + = (Stat) `if then <{Stat ";"}* th1>;a := 1;<{Stat ";"}* th2>; d := 5 else <{Stat ";"}* el> fi` + := [Stat] "if x then a := 1;b:=2;d:=5 else c:=3 fi" + && "" == "" + && "" == "b:=2" + && "" == "c:=3"; + +int sw(value e) { + n = 0; + switch(e) { + case (Exp) `1`: + n = 1; + case (Exp) `2`: + n = 2; + case (Exp) `1* 2`: + n = 3; + case (Exp) `1==2`: + n = 4; + case (Exp) `5==5`: + n = 5; + default: + n = 6; + } + return n; +} +test bool concreteMatch220() = sw([Exp] "1") == 1; + +test bool concreteMatch221() = sw([Exp] "2") == 2; +test bool concreteMatch222() = sw([Exp] "1 * 2") == 3; +test bool concreteMatch223() = sw([Exp] "1 == 2") == 4; +test bool concreteMatch224() = sw([Exp] "5 == 5") == 5; +test bool concreteMatch225() = sw([IntegerLiteral] "2") == 6; + +test bool semiConcrete1() = list[Identifier] _ := [[Identifier] "x"]; +test bool semiConcrete2() + = tuple[Identifier, Identifier] _ := <[Identifier] "x", [Identifier] "y">; + +test bool concreteMatch230() + = ["" + | /Identifier ident := [Stat] "if x then a := 1;b:=2 else c:=3 fi"] == ["x", "a", "b", "c"]; + +test bool concreteMatch231() { + res = []; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case Identifier ident: + res += ""; + } + return res == ["x", "a", "b", "c"]; +} + +test bool concreteMatch232() + = ["" | /Stat stat := [Stat] "if x then a := 1;b:=2 else c:=3 fi"] == ["a := 1", "b:=2", "c:=3", "if x then a := 1;b:=2 else c:=3 fi"]; + +test bool concreteMatch233() { + res = []; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case Stat stat: + res += ""; + } + return + res == ["a := 1", "b:=2", "c:=3", "if x then a := 1;b:=2 else c:=3 fi"]; +} + +test bool concreteMatch234() + = ["" + | /{Stat ";"}* stats := [Stat] "if x then a := 1;b:=2 else c:=3 fi"] == ["a := 1;b:=2", "c:=3"]; + +test bool concreteMatch235() { + res = []; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case {Stat ";"}* stats: + res += ""; + } + return res == ["a := 1;b:=2", "c:=3"]; +} + +test bool concreteMatch236() + = ["" + | /{Stat ";"}+ stats := [Stat] "if x then a := 1;b:=2 else c:=3 fi"] == ["a := 1;b:=2", "c:=3"]; + +test bool concreteMatch237() { + res = []; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case {Stat ";"}+ stats: + res += ""; + } + return res == ["a := 1;b:=2", "c:=3"]; +} + +test bool concreteMatch238() + = ["" | /{Stat ";"}+ stats := [Stat] "if x then else c:=3 fi"] == ["c:=3"]; + +test bool concreteMatch239() { + res = []; + visit([Stat] "if x then else c:=3 fi") { + case {Stat ";"}+ stats: + res += ""; + } + return res == ["c:=3"]; +} + +test bool concreteMatch240() + = [s | /lit(str s) := [Stat] "if x then a := 1;b:=2 else c:=3 fi"] == [ + "if", + "then", + ";", + "else", + ";", + "fi", + "if", + "then", + ";", + ":=", + ":=", + ";", + ":=", + ":=", + "else", + ";", + ":=", + ":=", + "fi" + ]; + +test bool concreteMatch241() { + res = []; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case lit(str s): + res += s; + } + return + res == [ + "if", + "then", + ";", + "else", + ";", + "fi", + "if", + "then", + ";", + ":=", + ":=", + ";", + ":=", + ":=", + "else", + ";", + ":=", + ":=", + "fi" + ]; +} + +test bool concreteMatch242() + = [n | /int n := [Stat] "if x then a := 1;b:=2 else c:=3 fi"] == [ + 105, + 105, + 102, + 102, + 105, + 102, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 97, + 122, + 97, + 122, + 120, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 116, + 116, + 104, + 104, + 101, + 101, + 110, + 110, + 116, + 104, + 101, + 110, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 97, + 122, + 97, + 122, + 97, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 58, + 58, + 61, + 61, + 58, + 61, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 48, + 57, + 48, + 57, + 49, + 32, + 32, + 32, + 32, + 32, + 32, + 59, + 59, + 59, + 32, + 32, + 32, + 32, + 32, + 32, + 97, + 122, + 97, + 122, + 98, + 32, + 32, + 32, + 32, + 32, + 32, + 58, + 58, + 61, + 61, + 58, + 61, + 32, + 32, + 32, + 32, + 32, + 32, + 48, + 57, + 48, + 57, + 50, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 101, + 101, + 108, + 108, + 115, + 115, + 101, + 101, + 101, + 108, + 115, + 101, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 97, + 122, + 97, + 122, + 99, + 32, + 32, + 32, + 32, + 32, + 32, + 58, + 58, + 61, + 61, + 58, + 61, + 32, + 32, + 32, + 32, + 32, + 32, + 48, + 57, + 48, + 57, + 51, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 102, + 102, + 105, + 105, + 102, + 105 + ]; + +test bool concreteMatch243() { + res = []; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case int n: + res += n; + } + return + res == [ + 105, + 105, + 102, + 102, + 105, + 102, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 97, + 122, + 97, + 122, + 120, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 116, + 116, + 104, + 104, + 101, + 101, + 110, + 110, + 116, + 104, + 101, + 110, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 97, + 122, + 97, + 122, + 97, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 58, + 58, + 61, + 61, + 58, + 61, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 48, + 57, + 48, + 57, + 49, + 32, + 32, + 32, + 32, + 32, + 32, + 59, + 59, + 59, + 32, + 32, + 32, + 32, + 32, + 32, + 97, + 122, + 97, + 122, + 98, + 32, + 32, + 32, + 32, + 32, + 32, + 58, + 58, + 61, + 61, + 58, + 61, + 32, + 32, + 32, + 32, + 32, + 32, + 48, + 57, + 48, + 57, + 50, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 101, + 101, + 108, + 108, + 115, + 115, + 101, + 101, + 101, + 108, + 115, + 101, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 97, + 122, + 97, + 122, + 99, + 32, + 32, + 32, + 32, + 32, + 32, + 58, + 58, + 61, + 61, + 58, + 61, + 32, + 32, + 32, + 32, + 32, + 32, + 48, + 57, + 48, + 57, + 51, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 102, + 102, + 105, + 105, + 102, + 105 + ]; +} + +test bool concreteMatch244() + = size([x | /x: appl(_, _) := [Stat] "if x then a := 1;b:=2 else c:=3 fi"]) == 60; + +test bool concreteMatch245() { + n = 0; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case appl(_, _): + n += 1; + } + return n == 60; +} + +test bool concreteMatch246a() + = size( + [x | /x: \char-class(_) := [Stat] "if x then a := 1;b:=2 else c:=3 fi"] + ) == 75; + +test bool concreteMatch247a() { + n = 0; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case \char-class(_): + n += 1; + } + return n == 75; +} + +test bool concreteMatch246b() + = size([x | /x: \sort(_) := [Stat] "if x then a := 1;b:=2 else c:=3 fi"]) == 16; + +test bool concreteMatch247b() { + n = 0; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case \sort(_): + n += 1; + } + return n == 16; +} + +test bool concreteMatch248() + = size([x | /x: \layouts(_) := [Stat] "if x then a := 1;b:=2 else c:=3 fi"]) == 34; + +test bool concreteMatch249() { + n = 0; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case \layouts(_): + n += 1; + } + return n == 34; +} + +test bool concreteMatch250() + = [n | /char(int n) := [Stat] "if x then a := 1;b:=2 else c:=3 fi"] == [ + 105, + 102, + 32, + 120, + 32, + 116, + 104, + 101, + 110, + 32, + 97, + 32, + 58, + 61, + 32, + 49, + 59, + 98, + 58, + 61, + 50, + 32, + 101, + 108, + 115, + 101, + 32, + 99, + 58, + 61, + 51, + 32, + 102, + 105 + ]; + +test bool concreteMatch251() { + res = []; + ; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case char(int n): + res += n; + } + return + res == [ + 105, + 102, + 32, + 120, + 32, + 116, + 104, + 101, + 110, + 32, + 97, + 32, + 58, + 61, + 32, + 49, + 59, + 98, + 58, + 61, + 50, + 32, + 101, + 108, + 115, + 101, + 32, + 99, + 58, + 61, + 51, + 32, + 102, + 105 + ]; +} + +test bool concreteMatch252() + = size([p | /p: prod(_, _, _) := [Stat] "if x then a := 1;b:=2 else c:=3 fi"]) == 37; + +test bool concreteMatch253() { + n = 0; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case prod(_, _, _): + n += 1; + } + return n == 37; +} + +test bool concreteMatch254() + = size([r | /r: range(_, _) := [Stat] "if x then a := 1;b:=2 else c:=3 fi"]) == 75; + +test bool concreteMatch255() { + n = 0; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case range(_, _): + n += 1; + } + return n == 75; +} + +test bool concreteMatch256() + = size( + [iss + | /iss: \iter-star-seps(_, _) := [Stat] "if x then a := 1;b:=2 else c:=3 fi"] + ) == 4; + +test bool concreteMatch257() { + n = 0; + visit([Stat] "if x then a := 1;b:=2 else c:=3 fi") { + case \iter-star-seps(_, _): + n += 1; + } + return n == 4; +} + +test bool matchAppl1() { + Tree a + = appl ( + regular(\iter-seps ( + sort("Tag"), + [layouts("LAYOUTLIST")] + )), + [] + ); + switch(a) { + case appl(_, []): + return true; + } + return false; +} + +test bool matchAppl2() { + Tree a + = appl ( + regular(\iter-seps ( + sort("Tag"), + [layouts("LAYOUTLIST")] + )), + [] + ); + switch(a) { + case appl(_, [Tree _]): + return false; + } + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Patterns3.rsc| +@bootstrapParser +module lang::rascal::tests::concrete::Patterns3 + +import ParseTree; +import Type; +import lang::rascal::\syntax::Rascal; +import Set; + +rel[Sym, Name] extractPatternTree1(Concrete concrete) { + psList = {| /hole(\one(Sym sym, Name n)) := concrete}; + return psList; +} + +test bool concrete1() + = Concrete CT := T ? size(extractPatternTree1(CT)) == 1 : false; +test bool concrete2() + = Concrete CT := T ? extractPatternTree1(CT) == ThePsList : false; + +rel[Sym, Name] extractPatternTree2(Concrete concrete) { + psList = {}; + visit(concrete) { + case hole(\one(Sym sym, Name n)): + psList += {}; + } + return psList; +} + +test bool concrete3() + = Concrete CT := T ? size(extractPatternTree2(CT)) == 1 : false; +test bool concrete4() + = Concrete CT := T ? extractPatternTree2(CT) == ThePsList : false; + +// Parse tree of (Concrete) `` +public Tree T + = appl ( + prod( + label("typed", lex("Concrete")), + [ + lit("("), + label("l1", layouts("LAYOUTLIST")), + label("symbol", sort("Sym")), + label("l2", layouts("LAYOUTLIST")), + lit(")"), + label("l3", layouts("LAYOUTLIST")), + lit("`"), + label("parts", \iter-star(lex("ConcretePart"))), + lit("`") + ], + {} + ), + [ + appl ( + prod(lit("("), [\char-class([range(40, 40)])], {}), + [char(40)] + ), + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [ + appl ( + regular(\iter-star(lex("LAYOUT"))), + [] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 122, 0, <9, 9>, <9, 9> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 122, 0, <9, 9>, <9, 9> + )], + appl ( + prod( + label("nonterminal", sort("Sym")), + [ + conditional( + label("nonterminal", lex("Nonterminal")), {\not-follow(lit("["))} + ) + ], + {} + ), + [ + appl ( + prod( + lex("Nonterminal"), + [ + conditional( + seq([ + conditional( + \char-class([range(65, 90)]), + {\not-precede(\char-class([range(65, 90)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]), + {delete(keywords("RascalKeywords"))} + ) + ], + {} + ), + [ + appl ( + regular( + seq([ + conditional( + \char-class([range(65, 90)]), + {\not-precede(\char-class([range(65, 90)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]) + ), + [ + char(65), + appl ( + regular( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ) + ), + [] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 123, 0, <9, 10>, <9, 10> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 122, 1, <9, 9>, <9, 10> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 122, 1, <9, 9>, <9, 10> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 122, 1, <9, 9>, <9, 10> + )], + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [ + appl ( + regular(\iter-star(lex("LAYOUT"))), + [] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 123, 0, <9, 10>, <9, 10> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 123, 0, <9, 10>, <9, 10> + )], + appl ( + prod(lit(")"), [\char-class([range(41, 41)])], {}), + [char(41)] + ), + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [ + appl ( + regular(\iter-star(lex("LAYOUT"))), + [ + appl ( + prod( + lex("LAYOUT"), + [ + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ], + {} + ), + [char(32)] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 124, 1, <9, 11>, <9, 12> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 124, 1, <9, 11>, <9, 12> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 124, 1, <9, 11>, <9, 12> + )], + appl ( + prod(lit("`"), [\char-class([range(96, 96)])], {}), + [char(96)] + ), + appl ( + regular(\iter-star(lex("ConcretePart"))), + [ + appl ( + prod( + label("hole", lex("ConcretePart")), + [label("hole", sort("ConcreteHole"))], + {Attr::\tag("category"("MetaVariable"))} + ), + [ + appl ( + prod( + label("one", sort("ConcreteHole")), + [ + lit("\<"), + layouts("LAYOUTLIST"), + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("\>") + ], + {} + ), + [ + appl ( + prod(lit("\<"), [\char-class([range(60, 60)])], {}), + [char(60)] + ), + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [ + appl ( + regular(\iter-star(lex("LAYOUT"))), + [] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 127, 0, <9, 14>, <9, 14> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 127, 0, <9, 14>, <9, 14> + )], + appl ( + prod( + label("nonterminal", sort("Sym")), + [ + conditional( + label("nonterminal", lex("Nonterminal")), {\not-follow(lit("["))} + ) + ], + {} + ), + [ + appl ( + prod( + lex("Nonterminal"), + [ + conditional( + seq([ + conditional( + \char-class([range(65, 90)]), + {\not-precede(\char-class([range(65, 90)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]), + {delete(keywords("RascalKeywords"))} + ) + ], + {} + ), + [ + appl ( + regular( + seq([ + conditional( + \char-class([range(65, 90)]), + {\not-precede(\char-class([range(65, 90)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]) + ), + [ + char(65), + appl ( + regular( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ) + ), + [] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 128, 0, <9, 15>, <9, 15> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 127, 1, <9, 14>, <9, 15> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 127, 1, <9, 14>, <9, 15> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 127, 1, <9, 14>, <9, 15> + )], + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [ + appl ( + regular(\iter-star(lex("LAYOUT"))), + [ + appl ( + prod( + lex("LAYOUT"), + [ + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ], + {} + ), + [char(32)] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 128, 1, <9, 15>, <9, 16> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 128, 1, <9, 15>, <9, 16> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 128, 1, <9, 15>, <9, 16> + )], + appl ( + prod( + lex("Name"), + [ + conditional( + seq([ + conditional( + \char-class([range(65, 90), range(95, 95), range(97, 122)]), + {\not-precede(\char-class([range(65, 90), range(95, 95), range(97, 122)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]), + {delete(keywords("RascalKeywords"))} + ) + ], + {} + ), + [ + appl ( + regular( + seq([ + conditional( + \char-class([range(65, 90), range(95, 95), range(97, 122)]), + {\not-precede(\char-class([range(65, 90), range(95, 95), range(97, 122)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]) + ), + [ + char(97), + appl ( + regular( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ) + ), + [] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 130, 0, <9, 17>, <9, 17> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 129, 1, <9, 16>, <9, 17> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 129, 1, <9, 16>, <9, 17> + )], + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [ + appl ( + regular(\iter-star(lex("LAYOUT"))), + [] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 130, 0, <9, 17>, <9, 17> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 130, 0, <9, 17>, <9, 17> + )], + appl ( + prod(lit("\>"), [\char-class([range(62, 62)])], {}), + [char(62)] + ) + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 126, 5, <9, 13>, <9, 18> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 126, 5, <9, 13>, <9, 18> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 126, 5, <9, 13>, <9, 18> + )], + appl ( + prod(lit("`"), [\char-class([range(96, 96)])], {}), + [char(96)] + ) + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 121, 11, <9, 8>, <9, 19> + )] ; + +rel[Tree, Tree] ThePsList + = {, <9, 15> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 127, 1, <9, 14>, <9, 15> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 127, 1, <9, 14>, <9, 15> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 127, 1, <9, 14>, <9, 15> + )], appl ( + prod( + lex("Name"), + [ + conditional( + seq([ + conditional( + \char-class([range(65, 90), range(95, 95), range(97, 122)]), + {\not-precede(\char-class([range(65, 90), range(95, 95), range(97, 122)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]), + {delete(keywords("RascalKeywords"))} + ) + ], + {} + ), + [ + appl ( + regular( + seq([ + conditional( + \char-class([range(65, 90), range(95, 95), range(97, 122)]), + {\not-precede(\char-class([range(65, 90), range(95, 95), range(97, 122)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]) + ), + [ + char(97), + appl ( + regular( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ) + ), + [] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 130, 0, <9, 17>, <9, 17> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 129, 1, <9, 16>, <9, 17> + )] + ] + )[@\loc = |project://rascal/src/org/rascalmpl/library/experiments/Compiler/Examples/Tst2.rsc|( + 129, 1, <9, 16>, <9, 17> + )] >} ; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Patterns4.rsc| +module lang::rascal::tests::concrete::Patterns4 + +import ParseTree; + +syntax OptTestGrammar = A? a B b; + +syntax A = "a"; +syntax B = "b"; + +layout L = " "*; +syntax QValue = "[" QConst "]"; + +syntax QConst = QExp; + +syntax QExp + = "x" + | QExp "-" QExp + ; + +syntax Question = "question" QValue? v; + +test bool optionalNotPresentIsFalse() + = !((A)`a` <- ([OptTestGrammar] "b").a); +test bool optionalPresentIsTrue() = (A)`a` <- ([OptTestGrammar] "ab").a; + +test bool optAbsent() { + if ((Question) `question ` := [Question] "question") { + for ((QValue) `[]` <- v) { + return false; + } + } + return true; +} + +test bool optPresent() { + if ((Question) `question ` := [Question] "question [x-x]") { + for ((QValue) `[]` <- v) { + return const := [QConst] "x-x"; + } + } + return false; +} + +bool absent((Question) `question `) { + bool b = true; + for ((QValue) `[]` <- v) { + b = false; + } + return b; +} + +test bool optAbsentInParameter() = absent([Question] "question"); + +bool present((Question) `question `) { + bool b = false; + for ((QValue) `[]` <- v) { + b = const := [QConst] "x-x"; + } + return b; +} + +test bool optPresentInParameter() = present([Question] "question [x-x]"); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Patterns5.rsc| +module lang::rascal::tests::concrete::Patterns5 + +syntax ConcreteHole = ; + +import ParseTree; + +Tree tree_reg1 + = appl ( + regular(\iter-star(\char-class([range(48, 57)]))), + [] + ) ; + +bool matchAppl(Tree v: appl(Production p, list[Tree] args)) = true; + +test bool matchApplRegular1() = matchAppl(tree_reg1); + +Tree tree_reg2 + = appl ( + regular(\iter-star(\char-class([range(48, 57)]))), + [appl ( + regular(\iter-star(lex("LAYOUT"))), + [] + )] + ) ; + +test bool visitApplRegular() = /appl(_, [_]) := tree_reg2; + +test bool matchApplRegular2() + = appl(_, [_]) !:= appl ( + regular(\iter-star(lex("LAYOUT"))), + [] + ); + +// A concrete pattern for EXP with two EXP holes +Tree tree_conc + = appl ( + prod( + label("concrete", sort("Pattern")), + [label("concrete", lex("Concrete"))], + {} + ), + [ + appl ( + prod(label("parsed", lex("Concrete")), [sort("EXP")], {}), + [ + appl ( + prod( + label("addition", sort("EXP")), + [ + label("lhs", sort("EXP")), + layouts("LAYOUTLIST"), + lit("+"), + layouts("LAYOUTLIST"), + label( + "rhs", conditional(sort("EXP"), {except("match"), except("noMatch")}) + ) + ], + {\assoc(\Associativity::\left())} + ), + [ + appl ( + prod( + label("$MetaHole", sort("EXP")), + [sort("ConcreteHole")], + {Attr::\tag("holeType"(sort("EXP")))} + ), + [ + appl ( + prod( + label("one", sort("ConcreteHole")), + [ + lit("\<"), + layouts("LAYOUTLIST"), + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("\>") + ], + {} + ), + [ + appl ( + prod(lit("\<"), [\char-class([range(60, 60)])], {}), + [char(60)] + ), + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [appl ( + regular(\iter-star(lex("LAYOUT"))), + [] + )] + ), + appl ( + prod( + label("nonterminal", sort("Sym")), + [ + conditional( + label("nonterminal", lex("Nonterminal")), {\not-follow(lit("["))} + ) + ], + {} + ), + [ + appl ( + prod( + lex("Nonterminal"), + [ + conditional( + seq([ + conditional( + \char-class([range(65, 90)]), + {\not-precede(\char-class([range(65, 90)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]), + {delete(keywords("RascalKeywords"))} + ) + ], + {} + ), + [ + appl ( + regular( + seq([ + conditional( + \char-class([range(65, 90)]), + {\not-precede(\char-class([range(65, 90)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]) + ), + [ + char(69), + appl ( + regular( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ) + ), + [ + char(120), + char(112), + char(114), + char(101), + char(115), + char(115), + char(105), + char(111), + char(110) + ] + ) + ] + ) + ] + ) + ] + ), + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [ + appl ( + regular(\iter-star(lex("LAYOUT"))), + [ + appl ( + prod( + lex("LAYOUT"), + [ + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ], + {} + ), + [char(32)] + ) + ] + ) + ] + ), + appl ( + prod( + lex("Name"), + [ + conditional( + seq([ + conditional( + \char-class([range(65, 90), range(95, 95), range(97, 122)]), + {\not-precede(\char-class([range(65, 90), range(95, 95), range(97, 122)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]), + {delete(keywords("RascalKeywords"))} + ) + ], + {} + ), + [ + appl ( + regular( + seq([ + conditional( + \char-class([range(65, 90), range(95, 95), range(97, 122)]), + {\not-precede(\char-class([range(65, 90), range(95, 95), range(97, 122)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]) + ), + [ + char(95), + appl ( + regular( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ) + ), + [] + ) + ] + ) + ] + ), + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [appl ( + regular(\iter-star(lex("LAYOUT"))), + [] + )] + ), + appl ( + prod(lit("\>"), [\char-class([range(62, 62)])], {}), + [char(62)] + ) + ] + ) + ] + ), + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [ + appl ( + regular(\iter-star(lex("LAYOUT"))), + [ + appl ( + prod( + lex("LAYOUT"), + [ + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ], + {} + ), + [char(32)] + ) + ] + ) + ] + ), + appl ( + prod(lit("+"), [\char-class([range(43, 43)])], {}), + [char(43)] + ), + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [ + appl ( + regular(\iter-star(lex("LAYOUT"))), + [ + appl ( + prod( + lex("LAYOUT"), + [ + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ], + {} + ), + [char(32)] + ) + ] + ) + ] + ), + appl ( + prod( + label("$MetaHole", sort("EXP")), + [sort("ConcreteHole")], + {Attr::\tag("holeType"(sort("EXP")))} + ), + [ + appl ( + prod( + label("one", sort("ConcreteHole")), + [ + lit("\<"), + layouts("LAYOUTLIST"), + label("symbol", sort("Sym")), + layouts("LAYOUTLIST"), + label("name", lex("Name")), + layouts("LAYOUTLIST"), + lit("\>") + ], + {} + ), + [ + appl ( + prod(lit("\<"), [\char-class([range(60, 60)])], {}), + [char(60)] + ), + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [appl ( + regular(\iter-star(lex("LAYOUT"))), + [] + )] + ), + appl ( + prod( + label("nonterminal", sort("Sym")), + [ + conditional( + label("nonterminal", lex("Nonterminal")), {\not-follow(lit("["))} + ) + ], + {} + ), + [ + appl ( + prod( + lex("Nonterminal"), + [ + conditional( + seq([ + conditional( + \char-class([range(65, 90)]), + {\not-precede(\char-class([range(65, 90)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]), + {delete(keywords("RascalKeywords"))} + ) + ], + {} + ), + [ + appl ( + regular( + seq([ + conditional( + \char-class([range(65, 90)]), + {\not-precede(\char-class([range(65, 90)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]) + ), + [ + char(69), + appl ( + regular( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ) + ), + [ + char(120), + char(112), + char(114), + char(101), + char(115), + char(115), + char(105), + char(111), + char(110) + ] + ) + ] + ) + ] + ) + ] + ), + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [ + appl ( + regular(\iter-star(lex("LAYOUT"))), + [ + appl ( + prod( + lex("LAYOUT"), + [ + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ], + {} + ), + [char(32)] + ) + ] + ) + ] + ), + appl ( + prod( + lex("Name"), + [ + conditional( + seq([ + conditional( + \char-class([range(65, 90), range(95, 95), range(97, 122)]), + {\not-precede(\char-class([range(65, 90), range(95, 95), range(97, 122)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]), + {delete(keywords("RascalKeywords"))} + ) + ], + {} + ), + [ + appl ( + regular( + seq([ + conditional( + \char-class([range(65, 90), range(95, 95), range(97, 122)]), + {\not-precede(\char-class([range(65, 90), range(95, 95), range(97, 122)]))} + ), + conditional( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ), + {\not-follow( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + )} + ) + ]) + ), + [ + char(95), + appl ( + regular( + \iter-star( + \char-class([range(48, 57), range(65, 90), range(95, 95), range(97, 122)]) + ) + ), + [] + ) + ] + ) + ] + ), + appl ( + prod( + layouts("LAYOUTLIST"), + [ + conditional( + \iter-star(lex("LAYOUT")), + {\not-follow( + \char-class([ + range(9, 13), + range(32, 32), + range(133, 133), + range(160, 160), + range(5760, 5760), + range(6158, 6158), + range(8192, 8202), + range(8232, 8233), + range(8239, 8239), + range(8287, 8287), + range(12288, 12288) + ]) + ), + \not-follow(lit("//")), + \not-follow(lit("/*"))} + ) + ], + {} + ), + [appl ( + regular(\iter-star(lex("LAYOUT"))), + [] + )] + ), + appl ( + prod(lit("\>"), [\char-class([range(62, 62)])], {}), + [char(62)] + ) + ] + ) + ] + ) + ] + ) + ] + ) + ] + ) ; + +list[Symbol] getHoleTypes( + appl( + prod( + label("concrete", sort("Pattern")), + [label("concrete", lex("Concrete"))], {} + ), [Tree concrete1] + ) +) { + list[Symbol] holes = []; + if (appl( + prod(Symbol::label("parsed", Symbol::lex("Concrete")), [_], _), + [Tree concrete2] + ) := concrete1) { + for (/appl( + prod( + Symbol::label("$MetaHole", Symbol _), + [Symbol::sort("ConcreteHole")], + {\tag("holeType"(Symbol holeType))} + ), [ConcreteHole _] + ) := concrete2) { + holes += holeType; + } + } + return holes; +} + +test bool getHoleTypesInConcretePattern() + = getHoleTypes(tree_conc) == [sort("EXP"), sort("EXP")]; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/PostParseFilter.rsc| +module lang::rascal::tests::concrete::PostParseFilter + +// copied from issue report #1210 +import Exception; +import ParseTree; + +syntax X + = a: A + | b: B + ; +syntax A = "X"; +syntax B = "X"; + +// Match a concrete pattern to have an X as return value. +X amb({f: (X)``, *value _}) = f; + +test bool t1() { + try { + // TODO: if allowAmbiguity=false one might expect this test to succeed as well, + // but it doesn't because the rewrite rule is never executed before the exception + // has been thrown already. See the discussion with issue #1210 + Tree t = parse(#X, "X", allowAmbiguity = true); + return true; + } + catch Ambiguity(loc _, str _, str _): { + Tree t = parse(#X, "X", allowAmbiguity = true); + assert /amb(_) !:= t : "Tree still contains an ambiguity node."; + return false; + } +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/PrefixMatching.rsc| +@synopsis{tests regression of issue #1594} +module lang::rascal::tests::concrete::PrefixMatching + +import ParseTree; + +lexical Layout = [\t\ ]+ !>> [\t\ ]; +layout LayoutList = Layout* !>> [\t\ ;]; +lexical Integer = integer: [0-9]; +lexical Identifier = [a-zA-Z0-9_]; +start syntax Statement = assign: Identifier id "=" Expression val; +syntax Expression = Integer integer; + +str input = "V = 3" ; + +test bool prefixAssignStatement() { + Statement stat = parse(#Statement, input); + + return assign(lhs, rhs) := stat; +} + +test bool prefixAssignTree() { + Tree stat = parse(#Statement, input); + + return assign(lhs, rhs) := stat; +} + +test bool prefixAssignValue() { + value stat = parse(#Statement, input); + + return assign(lhs, rhs) := stat; +} + +test bool specialCaseForAppl1() { + return appl(prod(sort("S"), [], {}), []) := appl ( + prod(sort("S"), [], {}), + [] + ); +} + +test bool specialCaseForAppl2() { + return + Tree::appl(prod(sort("S"), [], {}), []) := appl ( + prod(sort("S"), [], {}), + [] + ); +} + +test bool concreteAssignStatement() { + Statement stat = parse(#Statement, input); + + return (Statement)` = ` := stat; +} + +test bool concreteAssignTree() { + Tree stat = parse(#Statement, input); + + return (Statement)` = ` := stat; +} + +test bool concreteAssignValue() { + value stat = parse(#Statement, input); + + return (Statement)` = ` := stat; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/SubscriptAndSlice.rsc| +module lang::rascal::tests::concrete::SubscriptAndSlice + +import Exception; +import Node; +import ParseTree; + +lexical X = [xyzXYZ]; +lexical XStar = X* xs; +lexical XPlus = X+ xs1; +lexical XComma = {X ","}* xcommas; + +syntax A = [abcABC]; +syntax AStar = A* as; +syntax APlus = A+ as1; +syntax AComma = {A ","}* acommas; +syntax ACommaPlus = {A ","}+ acommas; +layout L = []*; + +bool eqNoSrc(Tree t, Tree u) = unsetRec(t, "src") == unsetRec(u, "src"); + +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscript1() = eqNoSrc(([XStar] "xyz").xs[0], [X] "x"); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscript2() = eqNoSrc(([XStar] "xyz").xs[1], [X] "y"); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscript3() = eqNoSrc(([XStar] "xyz").xs[2], [X] "z"); + +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscript4() = eqNoSrc(([XStar] "xyz").xs[-1], [X] "z"); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscript5() = eqNoSrc(([XStar] "xyz").xs[-2], [X] "y"); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscript6() = eqNoSrc(([XStar] "xyz").xs[-3], [X] "x"); + +@ignoreInterpreter{Incorrect/not implemented} +@expected{IndexOutOfBounds} +test bool lexSubscript7() = eqNoSrc(([XStar] "xyz").xs[3], [X] "x"); +@ignoreInterpreter{Incorrect/not implemented} +@expected{IndexOutOfBounds} +test bool lexSubscript8() = eqNoSrc(([XStar] "xyz").xs[-4], [X] "x"); + +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscriptSep1() + = eqNoSrc(([XComma] "x,y,z").xcommas[0], [X] "x"); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscriptSep2() + = eqNoSrc(([XComma] "x,y,z").xcommas[1], [X] "y"); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscriptSep3() + = eqNoSrc(([XComma] "x,y,z").xcommas[2], [X] "z"); + +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscriptSep4() + = eqNoSrc(([XComma] "x,y,z").xcommas[-1], [X] "z"); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscriptSep5() + = eqNoSrc(([XComma] "x,y,z").xcommas[-2], [X] "y"); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSubscriptSep6() + = eqNoSrc(([XComma] "x,y,z").xcommas[-3], [X] "x"); + +@ignoreInterpreter{Incorrect/not implemented} +@expected{IndexOutOfBounds} +test bool lexSubscriptSep7() + = eqNoSrc(([XComma] "x,y,z").xcommas[3], [X] "x"); +@ignoreInterpreter{Incorrect/not implemented} +@expected{IndexOutOfBounds} +test bool lexSubscriptSep8() + = eqNoSrc(([XComma] "x,y,z").xcommas[-4], [X] "x"); + +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSlice1() = eqNoSrc(([XStar] "xyz").xs[0..0], ([XStar] "").xs); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSlice2() = eqNoSrc(([XStar] "xyz").xs[0..1], ([XStar] "x").xs); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSlice3() + = eqNoSrc(([XStar] "xyz").xs[0..2], ([XStar] "xy").xs); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSlice4() + = eqNoSrc(([XStar] "xyz").xs[0..3], ([XStar] "xyz").xs); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSlice5() = eqNoSrc(([XStar] "xyz").xs[1..1], ([XStar] "").xs); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSlice6() = eqNoSrc(([XStar] "xyz").xs[1..2], ([XStar] "y").xs); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSlice7() + = eqNoSrc(([XStar] "xyz").xs[1..3], ([XStar] "yz").xs); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSlice8() = eqNoSrc(([XStar] "xyz").xs[2..3], ([XStar] "z").xs); + +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceNeg1() + = eqNoSrc(([XStar] "xyz").xs[2..1], ([XStar] "z").xs); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceNeg2() + = eqNoSrc(([XStar] "xyz").xs[2..0], ([XStar] "zy").xs); + +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceStep1() + = eqNoSrc(([XStar] "xyzXYZxyz").xs[0, 2..], ([XStar] "xzYxz").xs); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceStep2() + = eqNoSrc(([XStar] "xyzXYZxyz").xs[0, 3..], ([XStar] "xXx").xs); + +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceNegStep1() + = eqNoSrc(([XStar] "xyzXYZxyz").xs[8, 6..], ([XStar] "zxYzx").xs); + +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceSep1() + = eqNoSrc(([XComma] "x,y,z").xcommas[0..0], ([XComma] "").xcommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceSep2() + = eqNoSrc(([XComma] "x,y,z").xcommas[0..1], ([XComma] "x").xcommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceSep3() + = eqNoSrc(([XComma] "x,y,z").xcommas[0..2], ([XComma] "x,y").xcommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceSep4() + = eqNoSrc(([XComma] "x,y,z").xcommas[0..3], ([XComma] "x,y,z").xcommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceSep5() + = eqNoSrc(([XComma] "x,y,z").xcommas[1..1], ([XComma] "").xcommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceSep6() + = eqNoSrc(([XComma] "x,y,z").xcommas[1..2], ([XComma] "y").xcommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceSep7() + = eqNoSrc(([XComma] "x,y,z").xcommas[1..3], ([XComma] "y,z").xcommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceSep8() + = eqNoSrc(([XComma] "x,y,z").xcommas[2..3], ([XComma] "z").xcommas); + +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceSepStep1() + = eqNoSrc( + ([XComma] "x,y,z,X,Y,Z,x,y,z").xcommas[0, 2..], + ([XComma] "x,z,Y,x,z").xcommas + ); +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceSepStep2() + = eqNoSrc( + ([XComma] "x,y,z,X,Y,Z,x,y,z").xcommas[0, 3..], + ([XComma] "x,X,x").xcommas + ); + +@ignoreInterpreter{Incorrect/not implemented} +test bool lexSliceSepNegStep1() + = eqNoSrc( + ([XComma] "x,y,z,X,Y,Z,x,y,z").xcommas[8, 6..], + ([XComma] "z,x,Y,z,x").xcommas + ); + +@ignoreInterpreter{Incorrect/not implemented} +@expected{IllegalArgument} +test bool lexIllegalSlice() { + ([XPlus] "xyz").xs1[0..0]; + return false; +} + +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscript1() = eqNoSrc(([AStar] "abc").as[0], [A] "a"); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscript2() = eqNoSrc(([AStar] "abc").as[1], [A] "b"); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscript3() = eqNoSrc(([AStar] "abc").as[2], [A] "c"); + +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscript4() = eqNoSrc(([AStar] "abc").as[-1], [A] "c"); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscript5() = eqNoSrc(([AStar] "abc").as[-2], [A] "b"); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscript6() = eqNoSrc(([AStar] "abc").as[-3], [A] "a"); + +@ignoreInterpreter{Incorrect/not implemented} +@expected{IndexOutOfBounds} +test bool cfgSubscript7() = eqNoSrc(([AStar] "abc").as[3], [A] "a"); +@ignoreInterpreter{Incorrect/not implemented} +@expected{IndexOutOfBounds} +test bool cfgSubscript8() = eqNoSrc(([AStar] "abc").as[-4], [A] "a"); + +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscriptSep1() + = eqNoSrc(([AComma] "a,b,c").acommas[0], [A] "a"); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscriptSep2() + = eqNoSrc(([AComma] "a,b,c").acommas[1], [A] "b"); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscriptSep3() + = eqNoSrc(([AComma] "a,b,c").acommas[2], [A] "c"); + +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscriptSep4() + = eqNoSrc(([AComma] "a,b,c").acommas[-1], [A] "c"); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscriptSep5() + = eqNoSrc(([AComma] "a,b,c").acommas[-2], [A] "b"); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSubscriptSep6() + = eqNoSrc(([AComma] "a,b,c").acommas[-3], [A] "a"); + +@ignoreInterpreter{Incorrect/not implemented} +@expected{IndexOutOfBounds} +test bool cfgSubscriptSep7() + = eqNoSrc(([AComma] "a,b,c").acommas[3], [A] "c"); +@ignoreInterpreter{Incorrect/not implemented} +@expected{IndexOutOfBounds} +test bool cfgSubscriptSep8() + = eqNoSrc(([AComma] "a,b,c").acommas[-4], [A] "c"); + +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSlice1() = eqNoSrc(([AStar] "abc").as[0..0], ([AStar] "").as); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSlice2() = eqNoSrc(([AStar] "abc").as[0..1], ([AStar] "a").as); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSlice3() + = eqNoSrc(([AStar] "abc").as[0..2], ([AStar] "ab").as); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSlice4() + = eqNoSrc(([AStar] "abc").as[0..3], ([AStar] "abc").as); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSlice5() = eqNoSrc(([AStar] "abc").as[1..1], ([AStar] "").as); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSlice6() = eqNoSrc(([AStar] "abc").as[1..2], ([AStar] "b").as); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSlice7() + = eqNoSrc(([AStar] "abc").as[1..3], ([AStar] "bc").as); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSlice8() = eqNoSrc(([AStar] "abc").as[2..3], ([AStar] "c").as); + +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceStep1() + = eqNoSrc(([AStar] "abcABCabc").as[0, 2..], ([AStar] "acBac").as); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceStep2() + = eqNoSrc(([AStar] "abcABCabc").as[0, 3..], ([AStar] "aAa").as); + +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceNegStep1() + = eqNoSrc(([AStar] "abcABCabc").as[8, 6..], ([AStar] "caBca").as); + +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceSep1() + = eqNoSrc(([AComma] "a,b,c").acommas[0..0], ([AComma] "").acommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceSep2() + = eqNoSrc(([AComma] "a,b,c").acommas[0..1], ([AComma] "a").acommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceSep3() + = eqNoSrc(([AComma] "a,b,c").acommas[0..2], ([AComma] "a,b").acommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceSep4() + = eqNoSrc(([AComma] "a,b,c").acommas[0..3], ([AComma] "a,b,c").acommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceSep5() + = eqNoSrc(([AComma] "a,b,c").acommas[1..1], ([AComma] "").acommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceSep6() + = eqNoSrc(([AComma] "a,b,c").acommas[1..2], ([AComma] "b").acommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceSep7() + = eqNoSrc(([AComma] "a,b,c").acommas[1..3], ([AComma] "b,c").acommas); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceSep8() + = eqNoSrc(([AComma] "a,b,c").acommas[2..3], ([AComma] "c").acommas); + +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceSepStep1() + = eqNoSrc( + ([AComma] "a,b,c,A,B,C,a,b,c").acommas[0, 2..], + ([AComma] "a,c,B,a,c").acommas + ); +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceSepStep2() + = eqNoSrc( + ([AComma] "a,b,c,A,B,C,a,b,c").acommas[0, 3..], + ([AComma] "a,A,a").acommas + ); + +@ignoreInterpreter{Incorrect/not implemented} +test bool cfgSliceSepNegStep1() + = eqNoSrc( + ([AComma] "a,b,c,A,B,C,a,b,c").acommas[8, 6..], + ([AComma] "c,a,B,c,a").acommas + ); + +@ignoreInterpreter{Incorrect/not implemented} +@expected{IllegalArgument} +test bool cfgIllegalSlice() { + ([APlus] "abc").as1[0..0]; + return false; +} + +@ignoreInterpreter{Incorrect/not implemented} +@expected{IllegalArgument} +test bool cfgIllegalSliceSep() { + ([ACommaPlus] "a,b,c").acommas[0..0]; + return false; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Syntax1.rsc| +module lang::rascal::tests::concrete::Syntax1 + +import Exception; +import ParseTree; +import IO; + +layout Whitespace = [\ \t\n]*; + +start syntax A = "a"; +start syntax A = "A"; +start syntax As = A+; +start syntax B = "b"; +start syntax B = "B"; +start syntax Bs = B+; +start syntax C = A B; +start syntax D = "d"; +start syntax DS = D+ ds; +start syntax E = "e"; +start syntax ES = {E ","}+ args; + +start syntax X1 = "x" x1; +syntax X2 = () x2; +syntax X3 = ("x" "y") x3; +syntax X4 = [a-z] x4; +syntax X5 = ("x" | "y") x5; +syntax X6 = 'ax' x6; +syntax X7 = "x"? x7; + +test bool tX1() = "<([X1] "x").x1>" == "x"; +test bool tX2() = "<([X2] "").x2>" == ""; +test bool tX3() = "<([X3] "xy").x3>" == "xy"; +test bool tX4() = "<([X4] "x").x4>" == "x"; +test bool tX5() = "<([X5] "x").x5>" == "x"; +test bool tX7b() = "<([X7] "").x7>" == ""; + +test bool parseD1() = (D)`d` := parse(#D, "d"); + +@expected{ParseError} +test bool parseD2() = (D)`d` := parse(#D, " d"); +@expected{ParseError} +test bool parseD3() = (D)`d` := parse(#D, "d "); + +test bool parseD4() = (start[D])`d` := parse(#start[D], " d "); +test bool parseD5() = (D)`d` := parse(#start[D], " d ").top; + +test bool parseDS() = (DS)`d d d` := parse(#DS, "d d d"); + +test bool parseDSfromFile() { + writeFile(|tmp:///DS.trm|, "d d d"); + return (DS)`d d d` := parse(#DS, |tmp:///DS.trm|); +} + +test bool singleA() = (A) `a` := (A) `a`; + +test bool DSmatch() = (DS)`d d` := (DS)`d d`; + +test bool DvarsTypedInsert2() = (DS)`` := (DS)`d`; + +test bool DvarsTypedInsert3() = (DS)`` := (DS)`d d`; + +test bool DvarsTypedInsert4() + = (DS)`d ` := (DS)`d d` && (DS)`d ` := (DS)`d d` && Xs := Xs2; + +test bool DvarsTypedInsert5() + = (DS)`d ` := (DS)`d d d` && (DS)`d ` := (DS)`d d d` && Xs := Xs2; + +test bool sortA() = A _ := [A] "a"; +test bool sortB() = B _ := [B] "b"; +test bool sortC() = C _ := [C] "ab"; +test bool sortD() = D _ := [D] "d"; +test bool sortDS() = DS _ := [DS] "ddd"; +test bool sortE() = E _ := [E] "e"; +test bool sortES1() = ES _ := [ES] "e,e,e"; +test bool sortES2() = {E ","}+ _ := ([ES] "e,e,e").args; + +test bool asType1() = <(As) `aaaa`, (Bs) `bbb`> := <[As] "aaaa", [Bs] "bbb">; + +test bool asType2() = <(As) `aaAA`, (Bs) `bbBB`> := <[As] "aaAA", [Bs] "bbBB">; + +int cntDS(D+ ds) = size([d | d <- ds]); + +test bool cntDS1() = cntDS(((DS) `d`).ds) == 1; +test bool cntDS2() = cntDS(((DS) `dd`).ds) == 2; +test bool cntDS3() = cntDS(((DS) `d d`).ds) == 2; + +int cntES({E ","}+ es) = size([e | e <- es]); + +test bool cntES1() = cntES(((ES) `e`).args) == 1; +test bool cntES2() = cntES(((ES) `e,e`).args) == 2; +test bool cntES3() = cntES(((ES) `e ,e`).args) == 2; +test bool cntES4() = cntES(((ES) `e, e`).args) == 2; +test bool cntES5() = cntES(((ES) `e , e`).args) == 2; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Syntax2.rsc| +module lang::rascal::tests::concrete::Syntax2 + +import IO; +import ParseTree; +import lang::pico::\syntax::Main; + +test bool Pico1() { + t1 = (Program) `begin declare x: natural; x := 10 end`; + return true; +} + +test bool Pico2() { + return Program _ := (Program) `begin declare x: natural; x := 10 end`; +} + +test bool Pico3() { + return + (Program) `` := + (Program) `begin declare x: natural; x := 10 end`; +} + +Tree t1 = (Program) `begin declare x: natural; x := 10 end` ; + +//Tree t2 = (Declarations) `declare x : natural;`; +test bool PicoQuotedId() = (Id) `a` := (Id) `a`; + +test bool PicoQuotedType() = (Type) `natural` := (Type) `natural`; +test bool PicoQuotedExp1() = (Expression) `x` := (Expression) `x`; +test bool PicoQuotedExp2() = (Expression) `"abc"` := (Expression) `"abc"`; +test bool PicoQuotedExp3() = (Expression) `42` := (Expression) `42`; +test bool PicoQuotedExp4() = (Expression) `(42)` := (Expression) `(42)`; +test bool PicoQuotedExp5() = (Expression) `4 + 2` := (Expression) `4 + 2`; +test bool PicoQuotedExp6() = (Expression) `4 - 2` := (Expression) `4 - 2`; + +test bool PicoQuotedStat1() = (Statement) `x:=1` := (Statement) `x:=1`; + +test bool PicoQuotedIfElse1() + = (Statement) `if x then a:=1;b:=2 else fi` := + (Statement) `if x then a:=1;b:=2 else fi`; + +test bool PicoQuotedWhile1() + = (Statement) `while x do a:=1;b:=2 od` := + (Statement) `while x do a:=1;b:=2 od`; + +test bool PicoQuotedProgram1() { + return (Program) `` := t1; +} + +test bool PicoQuotedProgram2() { + return Program _ := t1; +} + +test bool PicoQuotedProgram3() { + return (Program) `begin <{Statement ";"}* _> end` := t1; +} + +test bool PicoQuotedProgram4() { + return + (Program)`begin <{Statement ";"}* stats> end` + := t1; +} + +test bool PicoQuotedProgram5() { + if ((Program)`begin <{Statement ";"}* stats> end` := t1) + { + return decl := (Declarations) `declare x: natural;`; + } + return false; +} + +test bool PicoAssign1() { + Statement s = (Statement)`x := 0`; + Id idY = (Id) `y`; + s.var = idY; + return s := (Statement)`y := 0`; +} + +test bool PicoAssign2() { + Statement s = (Statement)`x := 0`; + Expression exp1 = (Expression) `1`; + s.val = exp1; + return s := (Statement)`x := 1`; +} + +test bool PicoAssign3() { + Statement s = (Statement)`x := 0`; + Expression exp1 = (Expression) `1+2`; + s.val = exp1; + return s := (Statement)`x := 1+2`; +} + +test bool PicoAssign4() { + Statement ifStat = (Statement)`if x then a:=1;b:=2 else fi`; + Expression idY = (Expression) `y`; + ifStat.cond = idY; + return ifStat := (Statement)`if y then a:=1;b:=2 else fi`; +} + +test bool PicoAssign5() { + Statement ifStat1 = (Statement)`if x then a:=1;b:=2 else fi`; + Statement ifStat2 = (Statement)`if y then a:=10;b:=20 else fi`; + ifStat1.thenPart = ifStat2.thenPart; + return ifStat1 := (Statement)`if x then a:=10;b:=20 else fi`; +} + +test bool PicoAssign6() { + Statement ifStat1 = (Statement)`if x then a:=1;b:=2 else fi`; + Statement ifStat2 = (Statement)`if y then a:=10;b:=20 else fi`; + ifStat1.elsePart = ifStat2.thenPart; + return ifStat1 := (Statement)`if x then a:=1;b:=2 else a:=10;b:=20 fi`; +} + +test bool PicoAssign7() { + Statement ifStat1 = (Statement)`if x then a:=1;b:=2 else fi`; + Statement ifStat2 = (Statement)`if y then a:=10;b:=20 else fi`; + ifStat1.elsePart = ifStat2.thenPart; + return (Statement)`if x then a:=1;b:=2 else a:=10;b:=20 fi` := ifStat1; +} + +test bool PicoAssign8() { + Statement ifStat = (Statement)`if x then a:=1;b:=2 else a:=10;b:=20 fi`; + thenPart = ifStat.thenPart; + elsePart = ifStat.elsePart; + + ifStat.thenPart = elsePart; + ifStat.elsePart = thenPart; + return ifStat := (Statement)`if x then a:=10;b:=20 else a:=1;b:=2 fi`; +} + +test bool descendPicoStatement1() { + S = (Statement) `a:=1`; + L = [X | /Statement X := S]; + return L == [S]; +} + +test bool descendPicoStatement2() { + S = (Statement)`if x then a:=10;b:=20 else a:=1;b:=2 fi`; + L = [X | /Statement X := S]; + return size(L) == 5; +} +/* + + + @Test + @Ignore("Functionality subject to future/current change") + public void enumeratorPicoStatement1Untyped(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{L = [X | X <- `a:=1` ]; L == [ `a:=1` ];}")); + } + + @Test + @Ignore("Functionality subject to future/current change") + public void enumeratorPicoStatement1Typed(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{L = [X | Statement X <- `a:=1` ]; L == [ `a:=1` ];}")); + } + + @Test + @Ignore("Functionality subject to future/current change") + public void enumeratorPicoStatementsUntyped(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{L = [X | X <- `a:=1;a:=2;a:=3` ]; L == [`a:=1`, `a:=2`, `a:=3`];}")); + } + + @Test + @Ignore("Functionality subject to future/current change") + public void enumeratorPicoStatementsTyped(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{L = [X | Statement X <- `a:=1;a:=2;a:=3` ]; L == [`a:=1`, `a:=2`, `a:=3`];}")); + } + + @Test + @Ignore("Functionality subject to future/current change") + public void enumeratorPicoStatementsConcretePattern1(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{L = [X | `<\\PICO-ID X>:=1` <- `a:=1;b:=2;c:=1` ]; L == [ `a`, `c` ];}")); + } + + @Test + @Ignore("Functionality subject to future/current change") + public void enumeratorPicoStatementsConcretePattern2(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{L = [X | /`b:=` <- `a:=1;b:=2;c:=3` ]; L == [ (EXP)`2` ];}")); + } + + @Test + @Ignore("Functionality subject to future/current change") + public void enumeratorPicoStatementsConcretePattern3(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{L = [Id | /`<\\PICO-ID Id> : ` <- `x : natural, y : string` ]; L == [ `x`, `y` ];}")); + } + + @Test + @Ignore("Functionality subject to future/current change") + public void enumeratorPicoStatementsConcretePattern4(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{L = []; for(/`<\\PICO-ID Id> : ` <- `x : natural, y : string`){L += Id;} L == [ `x`, `y` ];}")); + } + + @Test + @Ignore("Functionality subject to future/current change") + public void forPicoStatementsTyped1(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{L = [X | /Statement X <- `a:=1;a:=2;a:=3` ]; L == [`a:=1`, `a:=2`, `a:=3`];}")); + } + *//* + + test bool forPicoStatementsTyped2() {L = [X | /Statement X <- (Program) `begin declare a : natural; a:=1;a:=2;a:=3 end` ]; L == [(Statement) `a:=1`, (Statement)`a:=2`, (Statement) `a:=3`];} + + + @Test + @Ignore("Functionality subject to future/current change") + public void forPicoStatementsTyped3(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{L = [X | /EXP X <- `begin declare a : natural; a:=1;b:=2;c:=3 end` ]; L == [(EXP)`1`, (EXP)`2`, (EXP)`3` ];}")); + } + + @Test + public void PicoStringDoesNotOverrideRascalString1(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{str s = \"abc\"; s == \"abc\";}")); + } + + @Test + public void PicoStringDoesNotOverrideRascalString2(){ + prepare("import lang::pico::\\syntax::Main;"); + prepareMore("import ParseTree;"); + assertTrue(runTestInSameEvaluator("{int n = 3; s = \"abc\"; s == \"abc3\";}")); + } + +} +*/### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Syntax3.rsc| +module lang::rascal::tests::concrete::Syntax3 + +import ParseTree; + +syntax Aas + = nil: [a]* + | a: [a] [a]* + | aas: [a] [a] [a]* + ; + +&T <: Tree ambFilter(amb(set[&T <: Tree] alternatives)) { + set[&T <: Tree] result = {a| &T <: Tree a <- alternatives, !(a is nil)}; + + if ({oneTree} := result) { + return oneTree; + } + return ParseTree::amb(result); +} + +test bool resolveableAmbIsGone() + = amb(_) !:= parse(#Aas, "a", allowAmbiguity = true, filters = {ambFilter}); + +test bool twoAmbsLeft() + = amb({_, _}) := parse(#Aas, "aa", allowAmbiguity = true, filters = {ambFilter}); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Syntax4.rsc| +@bootstrapParser +module lang::rascal::tests::concrete::Syntax4 + +import lang::rascal::\syntax::Rascal; +import List; + +test bool lexicalMatch1() = (Name) `a` := [Name] "a"; +test bool lexicalMatch2() = (Name) `ab` := [Name] "ab"; + +test bool lexicalMatch3() = (QualifiedName) `a` := [QualifiedName] "a"; +test bool lexicalMatch4() = (QualifiedName) `ab` := [QualifiedName] "ab"; + +test bool lexicalMatch5() = (Expression) `a` := [Expression] "a"; +test bool lexicalMatch6() = (Expression) `ab` := [Expression] "ab"; + +int cntStats(Statement* stats) = size([s | s <- stats]); + +test bool cntStats1() = cntStats(((Expression) `{x=1;}`).statements) == 1; +test bool cntStats2() + = cntStats(((Expression) `{x=1;x=2;}`).statements) == 2; + +// Match patterns +test bool matchPat1() = (Pattern) ` ` := [Pattern] "int x"; +test bool matchSetPat1() = (Pattern) `{10,11}` := [Pattern] "{10,11}"; +test bool matchSetPat2() + = (Pattern) `{,}` := [Pattern] "{10,11}"; + +test bool matchListPat1() = (Pattern) `[10,11]` := [Pattern] "[10,11]"; +test bool matchListPat2() + = (Pattern) `[,]` := [Pattern] "[10,11]"; + +test bool matchQNamePat1() = (Pattern) `x` := [Pattern] "x"; +test bool matchQNamePat2() + = (Pattern) `` := [Pattern] "x"; + +test bool matchMultiPat1() = (Pattern) `x*` := [Pattern] "x*"; +test bool matchMultiPat2() + = (Pattern) `*` := [Pattern] "x*"; + +test bool matchSplice1Pat() = (Pattern) `*x` := [Pattern] "*x"; +test bool matchSplice2Pat() = (Pattern) `+x` := [Pattern] "+x"; +test bool matchNegativPat() = (Pattern) `-x` := [Pattern] "-x"; + +test bool matchLitPat1() = (Pattern) `10` := [Pattern] "10"; +test bool matchLitPat2() = (Pattern) `` := [Pattern] "10"; + +test bool matchTuplePat1() = (Pattern) `\<10,11\>` := [Pattern] "\<10,11\>"; +test bool matchTuplePat2() + = (Pattern) `\<,\>` := [Pattern] "\<10,11\>"; + +test bool matchTypedVarPat1() = (Pattern) `int x` := [Pattern] "int x"; +test bool matchTypedVarPat2() + = (Pattern) ` ` := [Pattern] "int x"; +test bool matchTypedVarPat3() + = (Pattern) ` x` := [Pattern] "int x"; +test bool matchTypedVarPat4() + = (Pattern) `int ` := [Pattern] "int x"; + +//@Ignore{Not yet implemented} +//test bool matchMapPat() = (Pattern) `(1:10,2:11)` := [Pattern] "(1:10,2:11)"; +test bool matchCallPat() = (Pattern) `f(10,11)` := [Pattern] "f(10,11)"; + +test bool matchVarBecomesPat1() = (Pattern) `x:10` := [Pattern] "x:10"; +test bool matchVarBecomesPat2() + = (Pattern) `:10` := [Pattern] "x:10"; + +test bool matchAsTypePat() = (Pattern) `[int]10` := [Pattern] "[int]10"; +test bool matchDescendentPat() = (Pattern) `/10` := [Pattern] "/10"; +test bool matchAntiPat() = (Pattern) `!10` := [Pattern] "!10"; + +test bool matchTypedVarBecomesPat1() + = (Pattern) `int x:3` := [Pattern] "int x:3"; +test bool matchTypedVarBecomesPat2() + = (Pattern) ` x:3` := [Pattern] "int x:3"; +test bool matchTypedVarBecomesPat3() + = (Pattern) `int :3` := [Pattern] "int x:3"; +test bool matchTypedVarBecomesPat4() + = (Pattern) `int x:` := [Pattern] "int x:3"; +test bool matchTypedVarBecomesPat5() + = (Pattern) ` :` := [Pattern] "int x:3"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Syntax5.rsc| +@bootstrapParser +module lang::rascal::tests::concrete::Syntax5 + +import ParseTree; +import lang::rascal::\syntax::Rascal; + +test bool expr1() + = (Expression) ` + ` := (Expression) `1 + 2`; + +test bool expr2() + = (Expression) `{ }` := (Expression) `{ 1 }`; + +test bool pat1() = (Pattern) `` := (Pattern) `"1"`; + +test bool assign1() + = (Assignable) ` ? ` := (Assignable) `x[1]?0`; + +test bool isThisATuple() + = (Expression)`\< <{Expression ","}+ _> \>` := parse(#Expression, "\<1\>"); + +test bool concreteFragmentHasSrc() + = e: (Expression) ` + ` := (Expression) `1 + 2` + && e.src? + && x.src?; + +test bool concreteFragmentHasLegacyLoc() + = e: (Expression) ` + ` := (Expression) `1 + 2` + && e@\loc? + && x@\loc?; + +test bool concreteFragmentHasCorrectSrcs() + = e: (Expression) ` + ` := (Expression) `1 + 2` + && e.src.length == 5 + && x.src.offset == e.src.offset + && x.src.length == 1 + && y.src.offset == e.src.offset + 4 + && y.src.length == 1; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Syntax6.rsc| +module lang::rascal::tests::concrete::Syntax6 + +import ParseTree; + +syntax Exp + = left Exp "+" Exp + | LitA + | LitB + | AZ + ; +syntax LitA = "a"; +syntax LitB = "b"; +lexical AZ = [a-z]; + +test bool expIsAmbiguous() + = /amb(_) := parse(#Exp, "a+a", allowAmbiguity = true); + +test bool visitAndDeepMatchGoThroughAmb() { + t = parse(#Exp, "a+a", allowAmbiguity = true); + + u = visit(t) { + case (Exp) `` => (Exp) `` when LitB b := [LitB] "b" + } + + return t != u && /LitB _ := u; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Syntax7.rsc| +module lang::rascal::tests::concrete::Syntax7 + +import ParseTree; +import Exception; + +syntax A = ":" >> 'if' [a-zA-z]+; +syntax B = ":" !>> 'if' [a-zA-z]+; +syntax C = [a-zA-Z]+ 'if' << ":"; +syntax D = [a-zA-Z]+ 'if' !<< ":"; +syntax E = [a-zA-Z]+ \ 'if'; + +lexical LOOPKW + = WHILE + | + | DO + ; +lexical WHILE = 'while'; +lexical DO = "do"; +lexical BEGINEND + = BEGIN + | + | END + ; +lexical BEGIN = 'begin'; +lexical END = "END"; + +keyword KW + = LOOPKW + | + | BEGINEND + ; + +bool expectParseError(type[Tree] grammar, str input) { + try { + parse(grammar, input); + return false; + } + catch ParseError(_): + return true; +} + +test bool caseInsensitiveFollowRequirement1() + = expectParseError(#A, ":jg"); + +test bool caseInsensitiveFollowRequirement2() + = !expectParseError(#A, ":if"); + +test bool caseInsensitiveFollowRequirement3() + = !expectParseError(#A, ":iF"); + +test bool caseInsensitiveFollowRequirement4() + = !expectParseError(#A, ":iF"); + +test bool caseInsensitiveFollowRequirement5() + = !expectParseError(#A, ":IF"); + +test bool caseInsensitiveFollowRestriction() + = !expectParseError(#B, ":jg"); + +test bool caseInsensitiveFollowRestriction2() + = expectParseError(#B, ":if"); + +test bool caseInsensitiveFollowRestriction3() + = expectParseError(#B, ":iF"); + +test bool caseInsensitiveFollowRestriction4() + = expectParseError(#B, ":iF"); + +test bool caseInsensitiveFollowRestriction5() + = expectParseError(#B, ":IF"); + +test bool caseInsensitivePrecedeRequirement1() + = expectParseError(#C, "jg:"); + +test bool caseInsensitivePrecedeRequirement2() + = !expectParseError(#C, "if:"); + +test bool caseInsensitivePrecedeRequirement3() + = !expectParseError(#C, "iF:"); + +test bool caseInsensitivePrecedeRequirement4() + = !expectParseError(#C, "iF:"); + +test bool caseInsensitivePrecedeRequirement5() + = !expectParseError(#C, "IF:"); + +test bool caseInsensitivePrecedeRestriction() + = !expectParseError(#D, "jg:"); + +test bool caseInsensitivePrecedeRestriction2() + = expectParseError(#D, "if:"); + +test bool caseInsensitivePrecedeRestriction3() + = expectParseError(#D, "iF:"); + +test bool caseInsensitivePrecedeRestriction4() + = expectParseError(#D, "iF:"); + +test bool caseInsensitivePrecedeRestriction5() + = expectParseError(#D, "IF:"); + +test bool caseInsensitiveKeyword1() = expectParseError(#E, "if"); + +test bool caseInsensitiveKeyword2() = expectParseError(#E, "iF"); + +test bool caseInsensitiveKeyword3() = expectParseError(#E, "If"); + +test bool caseInsensitiveKeyword4() = expectParseError(#E, "IF"); + +test bool caseInsensitiveKeyword5() = !expectParseError(#E, "Ig"); + +test bool caseInsensitiveKeyword6() = !expectParseError(#E, "aF"); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Syntax8.rsc| +module lang::rascal::tests::concrete::Syntax8 + +layout Layout = " "*; + +syntax AB + = "a" + | "b" + ; +syntax ABPlus = AB+ abs; +syntax ABStar = AB* abs; +syntax ABPlusSep = {AB ","}+ abs; +syntax ABStarSep = {AB ","}* abs; + +test bool cntABPStar0() { + n = 0; + for (_ <- ([ABStar] "").abs) + n += 1; + return n == 0; +} +test bool cntABPStar3() { + n = 0; + for (_ <- ([ABStar] "a b a").abs) + n += 1; + return n == 3; +} + +test bool cntABPlus1() { + n = 0; + for (_ <- ([ABPlus] "a").abs) + n += 1; + return n == 1; +} +test bool cntABPlus3() { + n = 0; + for (_ <- ([ABPlus] "a b a").abs) + n += 1; + return n == 3; +} + +test bool cntABStarSep0() { + n = 0; + for (_ <- ([ABStarSep] "").abs) + n += 1; + return n == 0; +} +test bool cntABStarSep3() { + n = 0; + for (_ <- ([ABStarSep] "a, b, a").abs) + n += 1; + return n == 3; +} + +test bool cntABPlusSep1() { + n = 0; + for (_ <- ([ABPlusSep] "a").abs) + n += 1; + return n == 1; +} +test bool cntABPlusSep3() { + n = 0; + for (_ <- ([ABPlusSep] "a, b, a").abs) + n += 1; + return n == 3; +} + +test bool all1() = all(x <- ([ABStar] "a a a").abs, "a" := ""); +test bool all2() = !all(x <- ([ABStar] "a b a").abs, "a" := ""); +test bool all3() = all(x <- ([ABStarSep] "a, a, a").abs, "a" := ""); +test bool all4() = !all(x <- ([ABStarSep] "a, b, a").abs, "a" := ""); + +test bool any1() = any(x <- ([ABStar] "a b a").abs, "b" := ""); +test bool any2() = !any(x <- ([ABStar] "a a a").abs, "b" := ""); +test bool any3() = any(x <- ([ABStarSep] "a, a, a").abs, "a" := ""); +test bool any4() = !any(x <- ([ABStarSep] "a, a, a").abs, "b" := ""); + +int size(&E* l) = ( 0 | it + 1 | _ <- l ); + +@ignoreInterpreter{Not implemented} +test bool sizeABStar0() = size(([ABStar] "").abs) == 0; +@ignoreInterpreter{Not implemented} +test bool sizeABStar1() = size(([ABStar] "a").abs) == 1; +@ignoreInterpreter{Not implemented} +test bool sizeABStar2() = size(([ABStar] "a a").abs) == 2; + +@ignoreInterpreter{Not implemented} +test bool sizeABPlus1() = size(([ABPlus] "a").abs) == 1; +@ignoreInterpreter{Not implemented} +test bool sizeABPlus2() = size(([ABPlus] "a b").abs) == 2; +@ignoreInterpreter{Not implemented} +test bool sizeABPlus3() = size(([ABPlus] "a b a").abs) == 3; + +int size({&E &S}* l) = ( 0 | it + 1 | _ <- l ); + +@ignoreInterpreter{Not implemented} +test bool sizeABStarSep0() = size(([ABStarSep] "").abs) == 0; +@ignoreInterpreter{Not implemented} +test bool sizeABStarSep1() = size(([ABStarSep] "a").abs) == 1; +@ignoreInterpreter{Not implemented} +test bool sizeABStarSep2() = size(([ABStarSep] "a, b").abs) == 2; + +@ignoreInterpreter{Not implemented} +test bool sizeABPlusSep1() = size(([ABPlusSep] "a").abs) == 1; +@ignoreInterpreter{Not implemented} +test bool sizeABPlusSep2() = size(([ABPlusSep] "a, b").abs) == 2; +@ignoreInterpreter{Not implemented} +test bool sizeABPlusSep3() = size(([ABPlusSep] "a, b, a").abs) == 3; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/SyntaxKeywordFields.rsc| +module lang::rascal::tests::concrete::SyntaxKeywordFields + +import Node; +import ParseTree; + +syntax A = "a"; +syntax B + = "b" + | [b] + ; + +// ambiguous on purpose +// we only allow declarations on Tree for now, for lack of a syntax to declare them on non-terminals. +data Tree(str y = "y"); + +&T <: Tree get(&T <: Tree e) = e; + +// to be able to access the kw param feature, you have to remove the loc annotation first (until we remove annotations): +test bool assignKw() { + a = get((A) `a`); + a.y = "2"; + return a.y == "2"; +} + +// due to src locations no two concrete syntax patterns are `==` equal +test bool eqTest0() = get((A)`a`) != get((A)`a`); +test bool eqTest1() = (A)`a` := (A)`a`; + +//@ignore{interpreter fails this test somehow} +//test bool eqTest2() = get((A)`a`) := get((A)`a`); +test bool eqTest3() = get((A)`a`).y == get((A)`a`)[y = "y"].y; + +test bool eqTest4() = get((A)`a`) != get((A)`a`)[y = "y"] && eqTest3()/* superfluous for doc purposes */ ; + +test bool updateKw() = get((A)`a`)[y = "z"].y == "z"; + +test bool neqKwTest1() = get((A)`a`)[y = "z"] != (A)`a`; + +test bool defKw() = get((A)`a`).y == "y"; + +test bool normalProd() + = prod(sort("A"), [lit("a")], {}) := get((A)`a`)[y = "z"].prod; + +test bool normalArgs() = [_] := get((A) `a`)[y = "y"].args; + +test bool ambTest() = get([B] "b").y == "y"; + +test bool ambTest2() = {_, _} := get([B] "b")[y = "z"].alternatives; + +test bool ambTest3() = get([B] "b")[y = "z"].y == "z"; + +test bool ambTest4() { + t = get([B] "b"); + t.y = "z"; + return t.y == "z"; +} + +test bool charTest() = get(char(32)).y == "y"; + +test bool charTest2() = get(char(32))[y = "z"].y == "z"; + +test bool charTest3() = get(char(32))[y = "z"].character == 32; + +test bool charTest4() { + t = get(char(32)); + t.y = "z"; + return t.y == "z"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/Terms.rsc| +@license{ + Copyright (c) 2009-2020 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module lang::rascal::tests::concrete::Terms + +import ParseTree; + +layout Whitespace = [\ ]*; +lexical IntegerLiteral = [0-9]+; +lexical Identifier = [a-z]+; + +syntax Exp + = IntegerLiteral + | Identifier + | bracket "(" Exp ")" + > left Exp "*" Exp + > left Exp "+" Exp + | Exp "==" Exp + ; + +syntax Stat + = Identifier ":=" Exp + | "if" Exp cond "then" {Stat ";"}* thenPart "else" {Stat ";"}* elsePart "fi" + ; + +Exp e = [Exp] "x + 1" ; +Stat s1 = [Stat] "a := 2 * 3" ; +Stat s2 = [Stat] "b := 4 + 5" ; +Stat s3 = (Stat) `if then ; else fi` ; + +test bool concreteTerm1() = s3.cond == e; + +test bool concreteTerm2() = s3.thenPart[0] == s1; + +test bool concreteTerm3() = s3.thenPart[1] == s2; + +test bool concreteTerm4() = s3.elsePart[0] == s1; + +test bool testAssociativity() + = /amb(_) !:= parse(#Exp, "x + x + x", allowAmbiguity = true); + +test bool testPriority() + = /amb(_) !:= parse(#Exp, "x + x * x", allowAmbiguity = true); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/BasicRecoveryTests.rsc| +/** + * Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/module lang::rascal::tests::concrete::recovery::BasicRecoveryTests + +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; + +layout Layout = [\ ]* !>> [\ ]; + +syntax S = T; + +syntax T = ABC End; +syntax ABC = 'a' 'b' 'c'; +syntax End = "$"; + +test bool basicOk() = checkRecovery ( + #S, + "a b c $", + [] + ); + +test bool abx() = checkRecovery ( + #S, + "a b x $", + ["x "] + ); + +test bool axc() = checkRecovery ( + #S, + "a x c $", + ["x c"] + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/ErrorLocationTests.rsc| +/** + * Copyright (c) 2025, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/module lang::rascal::tests::concrete::recovery::ErrorLocationTests + +import lang::rascal::\syntax::Rascal; +import ParseTree; +import vis::Text; +import IO; +import util::ParseErrorRecovery; + +/** +A smoke test to see if parse error position reporting is done correctly. +The reported parse error location should be the point where the parser originally got stuck (just before the last '|' character) +even though error recovery results in a tree that skips the string "mn" before the actual parse error. +*/void testLocation() { + str src = "module X\n\ndata E=a()|id(str nm|bb();"; + + Module m = parse(#Module, src, allowRecovery = true); + list[Tree] errors = findBestParseErrors(m); + println(prettyTree(errors[0])); + assert errors[0].parseError == |unknown:///|(30, 1, <3, 20>, <3, 21>); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/ErrorRecoveryBenchmark.rsc| +/** +* Copyright (c) 2024-2025, NWO-I Centrum Wiskunde & Informatica (CWI) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/module lang::rascal::tests::concrete::recovery::ErrorRecoveryBenchmark + +import lang::rascal::tests::concrete::recovery::RecoveryTestSupport; +import IO; + +void runTestC() { + testRecoveryC(); +} +void runTestDiff() { + testRecoveryDiff(); +} +void runTestDot() { + testRecoveryDot(); +} +void runTestJava() { + testRecoveryJava(); +} +void runTestJson() { + testRecoveryJson(); +} +void runTestPico() { + testRecoveryPico(); +} +void runTestRascal() { + testRecoveryRascal(); +} + +FileStats testRecoveryC() + = testErrorRecovery( + |std:///lang/c90/syntax/C.rsc|, + "TranslationUnit", + |std:///lang/c90/examples/hello-world.c| + ); +FileStats testRecoveryDiff() + = testErrorRecovery( + |std:///lang/diff/unified/UnifiedDiff.rsc|, + "DiffFile", + |std:///lang/diff/unified/examples/example.diff| + ); +FileStats testRecoveryDot() + = testErrorRecovery( + |std:///lang/dot/syntax/Dot.rsc|, + "DOT", + |std:///lang/dot/examples/parser-state.dot| + ); +FileStats testRecoveryJava() + = testErrorRecovery( + |std:///lang/java/syntax/Java15.rsc|, + "CompilationUnit", + zippedFile( + "m3/snakes-and-ladders-project-source.zip", + "src/snakes/LastSquare.java" + ) + ); +FileStats testRecoveryJson() + = testErrorRecovery( + |std:///lang/json/syntax/JSON.rsc|, + "JSONText", + |std:///lang/json/examples/ex01.json| + ); +FileStats testRecoveryPico() + = testErrorRecovery( + |std:///lang/pico/syntax/Main.rsc|, + "Program", + |std:///lang/pico/examples/fac.pico| + ); +FileStats testRecoveryRascal() + = testErrorRecovery( + |std:///lang/rascal/syntax/Rascal.rsc|, + "Module", + |std:///lang/rascal/vis/ImportGraph.rsc| + ); + +FileStats testMemoBug() + = testErrorRecovery( + |std:///lang/rascal/syntax/Rascal.rsc|, + "Module", + |std:///lang/rascal/tests/concrete/PostParseFilter.rsc| + ); + +void runLanguageTests() { + testRecoveryC(); + testRecoveryDiff(); + testRecoveryDot(); + testRecoveryJava(); + testRecoveryJson(); + testRecoveryPico(); + testRecoveryRascal(); +} + +// Usage: ErrorRecoveryBenchmark [ [ [ []]]] +int main(list[str] args) { + RecoveryTestConfig config = createRecoveryTestConfig(args); + batchRecoveryTest(config); + return 0; +} + +int rascalSmokeTest() + = main([ + "source-loc=|std:///|", + "max-amb-depth=2", + "max-files=3", + "max-file-size=500", + "sample-window=3", + "random-seed=1", + "count-nodes=true" + ]); +int rascalStandardTest() + = main(["source-loc=|std:///|", "max-files=1000", "max-file-size=5120"]); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/ErrorTreeSemanticsTests.rsc| +/** + * Copyright (c) 2025, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/@description{ + This module contains tests for error tree semantics in Rascal. As most of this functionality is not implemented yet (in the interprter), + most tests currently fail. + } +module lang::rascal::tests::concrete::recovery::ErrorTreeSemanticsTests + +// We need to produce an error tree to test with +import lang::pico::\syntax::Main; + +import ParseTree; +import util::ParseErrorRecovery; +import IO; +import vis::Text; +import Set; +import Exception; + +// Ambiguous syntax to check amb memoization +syntax Amb + = AmbWord () + | () AmbWord + ; +syntax AmbWord + = "^" [a-z] "$" () + | "^" () [a-z] "$" + ; + +@synopsis{Check if a tree is an error tree.} +private bool isParseError(appl(error(_, _, _), _)) = true; +private default bool isParseError(Tree tree) = false; + +@synopsis{Check if a tree is an amb cluster} +private bool isAmbCluster(amb(_)) = true; +private default bool isAmbCluster(Tree tree) = false; + +@synopsis{Get first amb child} +private set[Tree] getAmbAlternatives(amb(alts)) = alts; + +@synopsis{Check equality modulo location information} +private +bool equals(appl(prod, args1), appl(prod, args2)) + = allEqual(args1, args2); +private +bool equals(amb(alts1), amb(alts2)) + = size(alts1) == size(alts2) && allEqual(alts1, alts2); +private bool equals(cycle(Symbol sym, int length), cycle(sym, lenght)) = true; +private bool equals(char(int c), char(c)) = true; +private default bool equals(Tree tree1, Tree tree2) = false; + +private bool allEqual(list[Tree] args1, list[Tree] args2) { + if (size(args1) != size(args2)) { + return false; + } + for (i <- [0..size(args1) - 1]) { + if (!equals(args1[i], args2[i])) { + return false; + } + } + return true; +} + +private bool allEqual(set[Tree] args1, set[Tree] args2) { + if (size(args1) != size(args2)) { + return false; + } + for (Tree arg1 <- args1) { + bool found = false; + for (Tree arg2 <- args2, !found) { + if (equals(arg1, arg2)) { + found = true; + args2 -= arg2; + break; + } + } + if (!found) { + return false; + } + } + + return true; +} + +private str sortName(Tree tree) = printSymbol(tree.prod.def, true); + +private str getLabel(Tree tree) = tree.prod.def.name; + +private +Program parsePico(str input) + = parse(#Program, input, allowRecovery = true, allowAmbiguity = true); + +private +Program getTestProgram() + = parsePico( + "begin declare; + while input do + input x= 14; + output := 0 + od +end" + ); + +private Statement getTestStatement() { + Program prg = getTestProgram(); + for (/(Statement) stat := prg, isParseError(stat), "" == "input x= 14") { + return stat; + } + + fail; +} + +private Statement getWhileStatement() { + Program prg = getTestProgram(); + for (/(Statement) stat := prg, stat is loop, !isParseError(stat)) { + return stat; + } + + fail; +} + +test bool verifyTestTree() { + Program prg = getTestProgram(); + println("tree:\n"); + println("all errors:"); + for (Tree error <- findAllParseErrors(prg)) { + println("error : "); + } + + assert "" == "input x= 14"; + assert "" == "while input do + input x= 14; + output := 0 + od"; + return true; +} + +@synopsis{Do some basic sanity checks on the test program} +test bool testDeepMatch() { + Program prg = getTestProgram(); + list[str] expected = ["assign", "assign", "loop"]; + + // Multiset of expected labels + // Find the error statements + for (/(Statement) stat := prg, isParseError(stat)) { + str label = getLabel(stat); + assert label in expected; + expected -= getLabel(stat); + // Remove the label from the expected list + } + + assert size(expected) == 0; + + // All expected labels should be found + return true; +} + +@synopsis{Test that all error trees are visited} +test bool testVisit() { + Program prg = getTestProgram(); + list[str] expected = ["assign", "assign", "loop"]; + // Multiset of expected labels + visit(prg) { + case (Statement) stat: + if (isParseError(stat)) { + str label = getLabel(stat); + assert label in expected; + expected -= getLabel(stat); + // Remove the label from the expected list + } + } + + assert size(expected) == 0; + + // All expected labels should be found + return true; +} + +test bool testIs() = !(getTestStatement() is assign); + +test bool testHasBeforeDot() = getTestStatement() has var; + +test bool testHasAfterDot() = !(getTestStatement() has val); + +test bool testIsDefinedBeforeDot() = getTestStatement().var?; + +test bool testIsDefinedValidTree() { + Statement stat = parse(#Statement, "a := 42"); + return stat.var?; +} + +test bool testTreeBuiltinFieldPresence() { + Statement stat = parse(#Statement, "a := 42"); + return stat.prod? && stat.args?; +} + +data Tree(int testThisField = -1); + +test bool testTreeKeywordFieldPresence() { + Statement stat = parse(#Statement, "a := 42"); + stat.testThisField = 42; + return stat.testThisField?; +} + +test bool testIsDefinedAfterDot() = !getTestStatement().val?; + +test bool testFieldAccessBeforeDot() + = "" == "input"; + +bool testFieldAccessAfterDot() { + try { + getTestStatement().val; + return false; + } + catch ParseErrorRecovery(NoSuchField("val"), _): { + return true; + } +} + +test bool testFieldAssignmentBeforeDot() { + Statement stat = getTestStatement(); + stat.var = (Id)`hello`; + return "" == "hello x= 14"; +} + +test bool testFieldAssignmentAfterDot() { + try { + stat = getTestStatement(); + stat.val = (Expression)`hello`; + return false; + } + catch ParseErrorRecovery(NoSuchField("val"), _): { + return true; + } +} + +test bool testBracketFieldAssignmentBeforeDot() { + stat = getTestStatement(); + return "" == "hello x= 14"; +} + +test bool testBracketFieldAssignmentAfterDot() { + stat = getTestStatement(); + try { + stat[val = (Expression)`hello`]; + return false; + } + catch ParseErrorRecovery(NoSuchField("val"), _): { + return true; + } +} + +test bool testIndexedFieldBeforeDot() + = equals(getTestStatement()[0], (Id)`input`); + +test bool testIndexedFieldAfterDot() { + try { + getTestStatement()[1]; + return false; + } + catch ParseErrorRecovery(IndexOutOfBounds(1), l): { + return true; + } +} + +test bool testIndexedFieldTrueOutOfBounds() { + try { + getTestStatement()[100]; + return false; + } + catch IndexOutOfBounds(100): { + return true; + } +} + +// The following two tests are ignored. Indexed field assignment currently does not +// work for regular trees so there is no use implementing support for error trees. +@ignore +test bool testIndexedFieldAssignmentBeforeDot() { + // Note that this currently does also not work on regular trees (in the interpreter)! + Statement stat = getTestStatement(); + stat[0] = (Id)`hello`; + return "" == "hello x= 14"; +} + +@ignore +test bool testIndexedFieldAssignmentAtOrAfterDot() { + Statement stat = getTestStatement(); + try { + stat[1] = (Id)`hello`; + return false; + } + catch ParseErrorRecovery(IndexOutOfBounds(2), _): { + return true; + } +} + +@description{Check that concrete syntax can be used to match holes with error subtrees. +Also check that error trees cannot be deconstructed using concrete syntax.} +test bool testConcreteMatchWithErrors() { + Statement whileStat = getWhileStatement(); + + // A tree with error children should match + if ((Statement)`while do ; od` := + whileStat) { + assert isAmbCluster(stat1); + Tree tree = getFirstFrom(getAmbAlternatives(stat1)); + assert "" == "input x= 14"; + + // An error tree should not match + if ((Statement)` := ` := tree) { + return false; + } + // Although a single hole should match + assert (Statement)`` := tree; + + return true; + } + return false; +} + +@description{ +This function creates a test tree that has plenty of opportunities to memo amb children: + ❖ + ├─ Amb = AmbWord () + │ ├─ ❖ + │ │ ├─ !error dot=4: AmbWord = "^" () [a-z] "$" + │ │ │ ├─ () + │ │ │ └─ skipped + │ │ │ ├─ X + │ │ │ └─ $ + │ │ ├─ !error dot=1: AmbWord = "^" [a-z] "$" () + │ │ │ └─ skipped + │ │ │ ├─ X + │ │ │ └─ $ + │ │ └─ !error dot=2: AmbWord = "^" [a-z] "$" () + │ │ └─ skipped + │ │ ├─ X + │ │ └─ $ + │ └─ () + └─ Amb = () AmbWord + ├─ () + └─ ❖ + ├─ !error dot=4: AmbWord = "^" () [a-z] "$" + │ ├─ () + │ └─ skipped + │ ├─ X + │ └─ $ + ├─ !error dot=1: AmbWord = "^" [a-z] "$" () + │ └─ skipped + │ ├─ X + │ └─ $ + └─ !error dot=2: AmbWord = "^" [a-z] "$" () + └─ skipped + ├─ X + └─ $ +} +private +Amb ambTestTree() + = parse(#Amb, "^X$", allowRecovery = true, allowAmbiguity = true); + +test bool testNodeDeepMatchAmbMemo() { + Amb ambTree = ambTestTree(); + + // Count the number of errors that is actually found by a deep match + int count = ( 0 | it + 1 | /appl(error(_, _, _), _) := ambTree ); + + // There will only be 3 matches if deep matches are memoized, 6 if they are not. + return count == 3; +} + +test bool testConcreteDeepMatchAmbMemo() { + Amb ambTree = ambTestTree(); + + // Count the number of errors that is actually found by a deep match + int count = ( 0 | it + 1 | /(AmbWord)`` := ambTree, isParseError(w) ); + + // There will only be 3 matches if deep matches are memoized, 6 if they are not. + return count == 3; +} + +test bool testAmbMatchAmbMemo() { + Amb ambTree = ambTestTree(); + + int count = ( 0 | it + 1 | /a: amb(alts) := ambTree ); + + return count == 2; +} + +test bool testVisitAmbMemo() { + Amb ambTree = ambTestTree(); + + int count = 0; + visit(ambTree) { + case appl(error(_, _, _), _): + count = count + 1; + } + + // There will only be 3 matches if deep matches are memoized, 6 if they are not. + return count == 3; +} + +test bool testVisitReplacementAmbMemo() { + Amb ambTree = ambTestTree(); + + // Return a different tree for each index + AmbWord replacement(int index) { + list[str] letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]; + return + parse( + #AmbWord, "^" + letters[index] + "$", allowAmbiguity = true, maxAmbDepth = 0 + ); + } + + int count = 0; + visitedTree = visit(ambTree) { + case appl(error(_, _, _), _) => { + count = count + 1; + replacement(count); + } + } + + /* + visitedTree without memoization: + ❖ + ├─ Amb = AmbWord () + │ ├─ ❖ + │ │ ├─ AmbWord = "^" () [a-z] "$" + │ │ │ ├─ () + │ │ │ └─ b + │ │ ├─ AmbWord = "^" () [a-z] "$" + │ │ │ ├─ () + │ │ │ └─ c + │ │ └─ AmbWord = "^" () [a-z] "$" + │ │ ├─ () + │ │ └─ d + │ └─ () + └─ Amb = () AmbWord + ├─ () + └─ ❖ + ├─ AmbWord = "^" () [a-z] "$" + │ ├─ () + │ └─ g + ├─ AmbWord = "^" () [a-z] "$" + │ ├─ () + │ └─ e + └─ AmbWord = "^" () [a-z] "$" + ├─ () + └─ f + + Expected with memoization: + ├─ Amb = AmbWord () + │ ├─ ❖ + │ │ ├─ AmbWord = "^" () [a-z] "$" + │ │ │ ├─ () + │ │ │ └─ b + │ │ ├─ AmbWord = "^" () [a-z] "$" + │ │ │ ├─ () + │ │ │ └─ c + │ │ └─ AmbWord = "^" () [a-z] "$" + │ │ ├─ () + │ │ └─ d + │ └─ () + └─ Amb = () AmbWord + ├─ () + └─ ❖ + ├─ AmbWord = "^" () [a-z] "$" + │ ├─ () + │ └─ b + ├─ AmbWord = "^" () [a-z] "$" + │ ├─ () + │ └─ c + └─ AmbWord = "^" () [a-z] "$" + ├─ () + └─ d + */// There will only be 3 matches if deep matches are memoized, 6 if they are not. + return count == 3; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/ListRecoveryTests.rsc| +/** + * Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/module lang::rascal::tests::concrete::recovery::ListRecoveryTests + +import ParseTree; +import util::ParseErrorRecovery; + +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; + +layout Layout = [\ ]* !>> [\ ]; + +syntax S = T End; + +syntax T = {AB ","}*; +syntax AB = "a" "b"; +syntax End = "$"; + +Tree parseList(str s, bool visualize = false) { + return + parser(#S, allowRecovery = true, allowAmbiguity = true)( + s, |unknown:///?visualize=< "" >| + ); +} + +test bool listOk() = checkRecovery ( + #S, + "a b , a b , a b $", + [] + ); + +test bool listTypo() = checkRecovery ( + #S, + "a b, a x, ab $", + ["x"] + ); + +test bool listTypoWs() = checkRecovery ( + #S, + "a b , a x , a b $", + ["x "] + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/NestedRecoveryTests.rsc| +/** + * Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/module lang::rascal::tests::concrete::recovery::NestedRecoveryTests + +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; + +layout Layout = [\ ]* !>> [\ ]; + +syntax S = T; + +syntax T = A B C; + +syntax A = "a"; +syntax B = "b" "b"; +syntax C = "c"; + +test bool nestedOk() = checkRecovery ( + #S, + "a b b c", + [] + ); + +test bool nestedTypo() = checkRecovery ( + #S, + "a b x c", + ["x "] + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/NonAsciiTest.rsc| +/** + * Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/module lang::rascal::tests::concrete::recovery::NonAsciiTest + +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; + +syntax S = T; + +syntax T = A B C; + +syntax A = "ª"; +syntax B = "ß" "ß"; +syntax C = "©"; + +test bool nonAsciiOk() = checkRecovery ( + #S, + "ªßß©", + [] + ); + +test bool nonAsciiError() = checkRecovery ( + #S, + "ªßxß©", + ["xß"] + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/ParseBenchmark.rsc| +/** +* Copyright (c) 2025, NWO-I Centrum Wiskunde & Informatica (CWI) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/module lang::rascal::tests::concrete::recovery::ParseBenchmark + +import IO; +import String; +import ValueIO; +import ParseTree; +import Grammar; +import util::Benchmark; +import lang::rascal::\syntax::Rascal; +import lang::rascal::grammar::definition::Modules; + +public data ParseBenchmarkConfig + = parseBenchmarkConfig( + loc syntaxFile = |std:///lang/rascal/syntax/Rascal.rsc|, + str topSort = "Module", + str extension = ".rsc", + loc files = |unknown:///|, + loc statFile = |tmp:///parse-benchmark-stats.csv|, + int warmupIterations = 100, + int parseIterations = 10); + +list[loc] gatherFiles(loc dir, str ext) { + list[loc] files = []; + for (entry <- listEntries(dir)) { + loc file = dir + entry; + if (isFile(file)) { + if (endsWith(file.path, ext)) { + files += file; + } + } + else + if (isDirectory(file)) { + files += gatherFiles(file, ext); + } + } + + return files; +} + +private +void warmupParser( + &T(value input, loc origin) benchmarkParser, list[loc] files, int iterations +) { + println("Warming up parser ()"); + for (int i <- [0..iterations]) { + loc file = files[i % size(files)]; + try { + benchmarkParser(readFile(file), file); + } + catch ParseError(_): { + println("Skipping warmup for file with parse errors: "); + } + } +} + +private +void runBenchmark( + ParseBenchmarkConfig config, &T(value input, loc origin) benchmarkParser, + loc file +) { + println("Benchmarking "); + str content = readFile(file); + int contentSize = size(content); + int iterations = config.parseIterations; + int i = 0; + try { + int begin = realTime(); + while(i < iterations) { + benchmarkParser(content, file); + i += 1; + } + int duration = realTime() - begin; + + appendToFile(config.statFile, ",,\n"); + } + catch ParseError(_): { + println("Ignoring file with parse errors: "); + } +} + +private +void runBenchmark( + ParseBenchmarkConfig config, &T(value input, loc origin) benchmarkParser, + list[loc] files +) { + for (loc file <- files) { + runBenchmark(config, benchmarkParser, file); + } +} + +private str syntaxLocToModuleName(loc syntaxFile) { + str path = replaceLast(substring(syntaxFile.path, 1), ".rsc", ""); + return replaceAll(path, "/", "::"); +} + +private void benchmark(ParseBenchmarkConfig config) { + writeFile(config.statFile, "input,size,duration\n"); + Module \module = parse(#start[Module], config.syntaxFile).top; + str modName = syntaxLocToModuleName(config.syntaxFile); + Grammar gram = modules2grammar(modName, {\module}); + + str topSort = config.topSort; + if (sym: \start(\sort(topSort)) <- gram.starts) { + type[value] begin = type(sym, gram.rules); + benchmarkParser = parser(begin); + list[loc] files = gatherFiles(config.files, config.extension); + + warmupParser(benchmarkParser, files, config.warmupIterations); + runBenchmark(config, benchmarkParser, files); + } + else { + throw "Cannot find top sort in "; + } +} + +int main(list[str] args) { + ParseBenchmarkConfig config = parseBenchmarkConfig(); + + for (str arg <- args) { + if (/=/ := arg) { + switch(toLowerCase(name)) { + case "syntax": + config.syntaxFile = readTextValueString(#loc, val); + case "sort": + config.topSort = val; + case "files": + config.files = readTextValueString(#loc, val); + case "ext": + config.extension = val; + case "stats": + config.statFile = readTextValueString(#loc, val); + case "warmup": + config.warmupIterations = toInt(val); + case "parses": + config.parseIterations = toInt(val); + } + } + } + + benchmark(config); + + return 0; +} + +public int benchmarkRascal() = main(["files=|std:///|"]); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/PicoRecoveryTests.rsc| +/** + * Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/module lang::rascal::tests::concrete::recovery::PicoRecoveryTests + +import lang::pico::\syntax::Main; + +import ParseTree; +import util::ParseErrorRecovery; +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; + +Tree parsePico(str input, bool visualize = false) + = parser(#Program, allowRecovery = true, allowAmbiguity = true)( + input, |unknown:///?visualize=< "" >| + ); + +test bool picoOk() + = checkRecovery ( + #Program, + "begin declare input : natural, + output : natural, + repnr : natural, + rep : natural; + input := 14; + output := 0; + while input - 1 do + rep := output; + repnr := input; + while repnr - 1 do + output := output + rep; + repnr := repnr - 1 + od; + input := input - 1 + od +end", + [] + ); + +test bool picoTypo() + = checkRecovery ( + #Program, + "begin declare input : natural, + output : natural, + repnr : natural, + rep : natural; + input := 14; + output := 0; + while input - 1 do + rep := output; + repnr := input; + while repnr - 1 do + output := output x rep; + repnr := repnr - 1 + od; + input := input - 1 + od +end", + ["output x rep"] + ); + +test bool picoMissingSemi() + = checkRecovery ( + #Program, + "begin declare input : natural, + output : natural, + repnr : natural, + rep : natural; + input := 14; + output := 0; + while input - 1 do + rep := output; + repnr := input; + while repnr - 1 do + output := output + rep; + repnr := repnr - 1 + od + input := input - 1 + od +end", + ["od +end", "input := input - 1 +"] + ); + +test bool picoTypoSmall() + = checkRecovery ( + #Program, + "begin declare; + while input do + input x= 14; + output := 0 + od +end", + ["x= 14"] + ); + +test bool picoMissingSemiSmall() + = checkRecovery ( + #Program, + "begin declare; + while input do + input := 14 + output := 0 + od +end", + ["output := 0 + od"] + ); + +test bool picoEof() + = checkRecovery ( + #Program, + "begin declare; input := 0;", + ["input := 0;"] + ); + +test bool picoEofError() + = checkRecovery ( + #Program, + "begin declare x y; input := 0;", + ["x y;", "input := 0;"] + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/PrefixSharingTest.rsc| +module lang::rascal::tests::concrete::recovery::PrefixSharingTest + +syntax Stat = Expr ";"; + +syntax Expr + = N "+" N + | N "-" N + ; + +syntax N = [0-9]; + +import ParseTree; +import util::ParseErrorRecovery; +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; +import vis::Text; +import IO; + +Tree parseStat(str input, bool visualize = false) + = parser(#Stat, allowRecovery = true, allowAmbiguity = true)( + input, |unknown:///?visualize=< "" >| + ); + +test bool exprOk() = checkRecovery ( + #Stat, + "1+2;", + [] + ); + +test bool exprUnknownTerminator() + = checkRecovery ( + #Stat, + "1+2:\n", + [":\n"] + , visualize = false + ); + +test bool exprUnknownOperator() + = checkRecovery ( + #Stat, + "1*2;", + ["*2"] + , visualize = false + ); + +// Used to manually inspect the resulting error tree +void exprPrefixSharing() { + Tree t = parseStat("1*2;", visualize = false); + println(prettyTree(t)); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/RascalRecoveryTests.rsc| +/** + * Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/module lang::rascal::tests::concrete::recovery::RascalRecoveryTests + +import lang::rascal::\syntax::Rascal; + +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; + +bool debugging = false ; + +test bool rascalOk() + = checkRecovery ( + #start[Module], + " + module A + + int inc(int i) { + return i+1; + } + ", + [] + ); + +test bool rascalFunctionDeclarationOk() + = checkRecovery ( + #FunctionDeclaration, + "void f(){}", + [] + ); + +test bool rascalModuleFollowedBySemi() + = checkRecovery ( + #start[Module], + " + module A + ; + ", + [";"] + ); + +test bool rascalOperatorTypo() + = checkRecovery ( + #start[Module], + " + module A + + int f() = 1 x 1; + ", + ["x 1;"] + ); + +test bool rascalIllegalStatement() + = checkRecovery ( + #start[Module], + "module A void f(){a}", + ["a}"] + ); + +test bool rascalMissingCloseParen() + = checkRecovery ( + #start[Module], + "module A void f({} void g(){}", + ["("] + ); + +test bool rascalFunctionDeclarationMissingCloseParen() + = checkRecovery ( + #FunctionDeclaration, + "void f({} void g() {}", + ["("] + ); + +test bool rascalIfMissingExpr() + = checkRecovery ( + #FunctionDeclaration, + "void f(){if(){1;}}", + [")"] + ); + +test bool rascalIfBodyEmpty() + = checkRecovery ( + #start[Module], + "module A void f(){1;} void g(){if(1){}} void h(){1;}", + ["{", "} "] + ); + +test bool rascalMissingOpeningParen() + = checkRecovery ( + #start[Module], + "module A +void f) { + } + void g() { + }", + ["} +", ") {"] + ); + +test bool rascalFunFunMissingCloseParen() + = checkRecovery ( + #start[Module], + "module A void f(){void g({}} void h(){}", + ["void g({}", "} "] + ); + +test bool rascalIfMissingOpeningParen() + = checkRecovery ( + #start[Module], + "module A void f() { + if 1) { + 1; + }}", + ["1) "] + ); + +test bool rascalIfMissingCloseParen() + = checkRecovery ( + #start[Module], + "module A +void f() { + if (1 { + 1; + }}", + ["1 "] + ); + +test bool rascalIfMissingSemi() + = checkRecovery ( + #start[Module], + "module A +void f() { + if (true) { + a + } +}", + ["{", "} +"] + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/RecoveryCheckSupport.rsc| +/** + * Copyright (c) 2024-2025, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/module lang::rascal::tests::concrete::recovery::RecoveryCheckSupport + +import ParseTree; +import util::ParseErrorRecovery; +import String; +import IO; +import Grammar; +import analysis::statistics::Descriptive; +import util::Math; +import Set; +import List; +import vis::Text; + +bool checkRecovery( + type[&T <: Tree] begin, + str input, + list[str] expectedErrors, + bool visualize = false +) { + Tree t + = parser(begin, allowRecovery = true, allowAmbiguity = true)( + input, |unknown:///?visualize=< "" >| + ); + return checkErrors(t, expectedErrors); +} + +// Print a list of errors +void printErrors(list[Tree] errors) { + for (Tree error <- errors) { + println("\'\'"); + } +} + +// Check a tree contains exactly the expected error +bool checkError(Tree t, str expectedError) + = checkErrors ( + t, + [expectedError] + ); + +// Check if a tree contains exactly the expected errors +bool checkErrors(Tree t, list[str] expectedErrors) { + list[Tree] errors = findBestParseErrors(t); + if (size(errors) != size(expectedErrors)) { + println( + "Expected errors, found " + ); + printErrors(errors); + return false; + } + for (error <- errors) { + str errorText = getErrorText(error); + if (errorText notin expectedErrors) { + println("Unexpected error: \'\'"); + println("All errors found:"); + printErrors(errors); + return false; + } + } + + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/RecoveryTestSupport.rsc| +/** + * Copyright (c) 2024-2025, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/module lang::rascal::tests::concrete::recovery::RecoveryTestSupport + +import lang::rascal::\syntax::Rascal; +import ParseTree; +import util::ParseErrorRecovery; +import String; +import IO; +import util::Benchmark; +import Grammar; +import analysis::statistics::Descriptive; +import util::Math; +import Set; +import List; +import Exception; +import vis::Text; +import ValueIO; +import util::Eval; + +import lang::rascal::grammar::definition::Modules; + +public data RecoveryTestConfig + = recoveryTestConfig( + loc syntaxFile = |unknown:///|, + str syntaxModule = "", + str topSort = "", + int maxAmbDepth = 2, + loc dir = |unknown:///|, + str ext = "", + int maxFiles = 1000000, + int minFileSize = 0, + int maxFileSize = 1000000000, + int maxRecoveryAttempts = 50, + int maxRecoveryTokens = 3, + int fromFile = 0, + int sampleWindow = 1, + bool countNodes = false, + bool verifyResult = false, + loc statFile = |unknown:///|); + +alias FrequencyTable = map[int val, int count]; + +public data TestMeasurement (loc source = |unknown:///|, int duration = 0) + = successfulParse() + | recovered( int errorCount = 0, int errorSize = 0) + | parseError() + | successfulDisambiguation() + | skipped() + ; + +public data FileStats + = fileStats( + int totalParses = 0, + int successfulParses = 0, + int successfulRecoveries = 0, + int successfulDisambiguations = 0, + int failedRecoveries = 0, + int parseErrors = 0, + int slowParses = 0, + FrequencyTable parseTimeRatios = (), + FrequencyTable errorCounts = (), + FrequencyTable errorSizes = ()); + +public data TestStats + = testStats( + int filesTested = 0, + int testCount = 0, + FrequencyTable successfulParses = (), + FrequencyTable successfulRecoveries = (), + FrequencyTable successfulDisambiguations = (), + FrequencyTable failedRecoveries = (), + FrequencyTable parseErrors = (), + FrequencyTable slowParses = (), + FrequencyTable parseTimeRatios = (), + FrequencyTable errorCounts = (), + FrequencyTable errorSizes = ()); + +@javaClass{org.rascalmpl.library.util.ParseErrorRecovery} +java int countTreeNodes(&T <: Tree tree); + +@javaClass{org.rascalmpl.library.util.ParseErrorRecovery} +java int countUniqueTreeNodes(&T <: Tree tree); + +@javaClass{org.rascalmpl.library.util.ParseErrorRecovery} +java int countUniqueTreeNodes(Tree tree); +@javaClass{org.rascalmpl.library.util.ParseErrorRecovery} +java int countTreeNodes(Tree tree); + +@javaClass{org.rascalmpl.library.util.ParseErrorRecovery} +java &T <: Tree pruneAmbiguities(&T <: Tree t, int maxDepth = 3); + +private +TestMeasurement testRecovery( + RecoveryTestConfig config, &T(value input, loc origin) standardParser, + &T(value input, loc origin) recoveryParser, str input, loc source, + int referenceParseTime, int referenceNodeCount, + int referenceNodeCountUnique +) { + int startTime = 0; + int duration = 0; + int disambDuration = -1; + int errorCount = -1; + int errorSize = -1; + str result = "?"; + int nodeCount = -1; + int nodeCountUnique = -1; + int disambNodeCount = -1; + int disambNodeCountUnique = -1; + + TestMeasurement measurement = successfulParse(); + startTime = realTime(); + try { + Tree tree = recoveryParser(input, source); + int parseEndTime = realTime(); + + duration = parseEndTime - startTime; + if (config.countNodes) { + nodeCount = countTreeNodes(tree); + nodeCountUnique = countUniqueTreeNodes(tree); + } + if (hasParseErrors(tree)) { + if (config.countNodes) { + Tree disambTree = disambiguateParseErrors(tree); + disambDuration = realTime() - parseEndTime; + disambNodeCount = nodeCount; + disambNodeCountUnique = nodeCountUnique; + + list[Tree] errors = findAllParseErrors(disambTree); + errorCount = size(errors); + errorSize = ( 0 | it + size(getErrorText(err)) | err <- errors ); + } + measurement + = recovered( + source = source, + duration = duration, + errorCount = errorCount, + errorSize = errorSize + ); + result = "recovery"; + + if (config.verifyResult && "" != input) { + throw "Yield of recovered tree does not match the original input"; + } + } + else { + measurement = successfulParse(source = source, duration = duration); + result = "success"; + } + } + catch ParseError(_): { + result = "error"; + duration = realTime() - startTime; + measurement = parseError(source = source, duration = duration); + } + + if (config.statFile != |unknown:///|) { + int durationRatio = percent(duration, referenceParseTime); + if (config.countNodes) { + int nodeRatio = percent(nodeCount, referenceNodeCount); + int unodeRatio = percent(nodeCountUnique, referenceNodeCountUnique); + + appendToFile( + config.statFile, + ",,,,,,,,,,,,,\n" + ); + } + else { + appendToFile( + config.statFile, + ",,,,\n" + ); + } + } + return measurement; +} + +FileStats updateStats( + FileStats stats, TestMeasurement measurement, int referenceParseTime, + int recoverySuccessLimit +) { + stats.totalParses += 1; + + int ratio = measurement.duration / referenceParseTime; + int parseTimeRatio = ratio == 0 ? 0 : round(log2(ratio)); + + switch(measurement) { + case successfulParse(): { + print("."); + stats.successfulParses += 1; + } + case recovered( + errorCount = errorCount, errorSize = errorSize + ): { + stats.parseTimeRatios + = increment(stats.parseTimeRatios, parseTimeRatio); + stats.errorCounts = increment(stats.errorCounts, errorCount); + stats.errorSizes = increment(stats.errorSizes, errorSize); + if (errorSize <= recoverySuccessLimit) { + print("+"); + stats.successfulRecoveries += 1; + } + else { + print("-"); + stats.failedRecoveries += 1; + } + } + case successfulDisambiguation(): { + stats.parseTimeRatios + = increment(stats.parseTimeRatios, parseTimeRatio); + print("&"); + stats.successfulDisambiguations += 1; + } + case parseError(): { + stats.parseTimeRatios + = increment(stats.parseTimeRatios, parseTimeRatio); + print("?"); + stats.parseErrors += 1; + } + } + + if (measurement.duration > referenceParseTime * 10) { + print("!"); + stats.slowParses += 1; + } + return stats; +} + +FileStats mergeFileStats(FileStats stats1, FileStats stats2) { + return + fileStats( + totalParses = stats1.totalParses + stats2.totalParses, + successfulParses = stats1.successfulParses + stats2.successfulParses, + successfulRecoveries = stats1.successfulRecoveries + + stats2.successfulRecoveries, + successfulDisambiguations = stats1.successfulDisambiguations + + stats2.successfulDisambiguations, + failedRecoveries = stats1.failedRecoveries + stats2.failedRecoveries, + parseErrors = stats1.parseErrors + stats2.parseErrors, + slowParses = stats1.slowParses + stats2.slowParses, + parseTimeRatios = + mergeFrequencyTables(stats1.parseTimeRatios, stats2.parseTimeRatios), + errorCounts = + mergeFrequencyTables(stats1.errorCounts, stats2.errorCounts), + errorSizes = mergeFrequencyTables(stats1.errorSizes, stats2.errorSizes) + ); +} + +FrequencyTable increment(FrequencyTable frequencyTable, int val) { + if (val in frequencyTable) { + frequencyTable[val] += 1; + } + else { + frequencyTable[val] = 1; + } + + return frequencyTable; +} + +TestStats consolidateStats( + TestStats cumulativeStats, FileStats fileStats +) { + int totalFailed = fileStats.totalParses - fileStats.successfulParses; + + cumulativeStats.successfulParses + = increment( + cumulativeStats.successfulParses, + percentage(fileStats.successfulParses, fileStats.totalParses) + ); + cumulativeStats.successfulRecoveries + = increment( + cumulativeStats.successfulRecoveries, + percentage(fileStats.successfulRecoveries, totalFailed) + ); + cumulativeStats.successfulDisambiguations + = increment( + cumulativeStats.successfulDisambiguations, + percentage(fileStats.successfulDisambiguations, totalFailed) + ); + cumulativeStats.failedRecoveries + = increment( + cumulativeStats.failedRecoveries, + percentage(fileStats.failedRecoveries, totalFailed) + ); + cumulativeStats.parseErrors + = increment( + cumulativeStats.parseErrors, + percentage(fileStats.parseErrors, totalFailed) + ); + cumulativeStats.slowParses + = increment( + cumulativeStats.slowParses, + percentage(fileStats.slowParses, totalFailed) + ); + cumulativeStats.parseTimeRatios + = mergeFrequencyTables( + cumulativeStats.parseTimeRatios, fileStats.parseTimeRatios + ); + cumulativeStats.errorCounts + = mergeFrequencyTables( + cumulativeStats.errorCounts, fileStats.errorCounts + ); + cumulativeStats.errorSizes + = mergeFrequencyTables(cumulativeStats.errorSizes, fileStats.errorSizes); + + cumulativeStats.filesTested += 1; + cumulativeStats.testCount += fileStats.totalParses; + + return cumulativeStats; +} + +map[int, int] mergeFrequencyTables(map[int, int] hist1, map[int, int] hist2) { + for (int pct <- hist2) { + if (pct in hist1) { + hist1[pct] += hist2[pct]; + } + else { + hist1[pct] = hist2[pct]; + } + } + + return hist1; +} + +TestStats mergeStats(TestStats stats, TestStats stats2) { + stats.filesTested += stats2.filesTested; + stats.testCount += stats2.testCount; + stats.successfulParses + = mergeFrequencyTables(stats.successfulParses, stats2.successfulParses); + stats.successfulRecoveries + = mergeFrequencyTables( + stats.successfulRecoveries, stats2.successfulRecoveries + ); + stats.successfulDisambiguations + = mergeFrequencyTables( + stats.successfulDisambiguations, stats2.successfulDisambiguations + ); + stats.failedRecoveries + = mergeFrequencyTables(stats.failedRecoveries, stats2.failedRecoveries); + stats.parseErrors + = mergeFrequencyTables(stats.parseErrors, stats2.parseErrors); + stats.slowParses + = mergeFrequencyTables(stats.slowParses, stats2.slowParses); + stats.parseTimeRatios + = mergeFrequencyTables(stats.parseTimeRatios, stats2.parseTimeRatios); + stats.errorCounts + = mergeFrequencyTables(stats.errorCounts, stats2.errorCounts); + stats.errorSizes + = mergeFrequencyTables(stats.errorSizes, stats2.errorSizes); + + return stats; +} + +// Backwards compatible version +FileStats testSingleCharDeletions( + &T(value input, loc origin) standardParser, + &T(value input, loc origin) recoveryParser, + loc source, + str input, + int referenceParseTime, + int referenceNodeCount, + int referenceNodeCountUnique, + int recoverySuccessLimit, + int begin = 0, + int end = -1, + loc statFile = |unknown:///| +) + = testSingleCharDeletions( + recoveryTestConfig(statFile = statFile), standardParser, recoveryParser, source, input, referenceParseTime, referenceNodeCount, referenceNodeCountUnique, recoverySuccessLimit, + begin = begin, + end = end + ); + +FileStats testSingleCharDeletions( + RecoveryTestConfig config, + &T(value input, loc origin) standardParser, + &T(value input, loc origin) recoveryParser, + loc source, + str input, + int referenceParseTime, + int referenceNodeCount, + int referenceNodeCountUnique, + int recoverySuccessLimit, + int begin = 0, + int end = -1 +) { + FileStats stats = fileStats(); + int len = size(input); + int i = begin; + + while(i < len && (end == -1 || i <= end)) { + str modifiedInput = input[..i] + input[i + 1..]; + + source.query = "deletedChar="; + TestMeasurement measurement + = testRecovery( + config, + standardParser, + recoveryParser, + modifiedInput, + source, + referenceParseTime, + referenceNodeCount, + referenceNodeCountUnique + ); + stats + = updateStats( + stats, measurement, referenceParseTime, recoverySuccessLimit + ); + int skip = 1 + arbInt(config.sampleWindow); + int next = min(i + skip, len); + for (contains(substring(input, i, next), "\n")) { + println(); + } + i += 1 + arbInt(config.sampleWindow); + } + + return stats; +} + +// Backwards compatible version +FileStats testDeleteUntilEol( + &T(value input, loc origin) standardParser, + &T(value input, loc origin) recoveryParser, + loc source, + str input, + int referenceParseTime, + int referenceNodeCount, + int referenceNodeCountUnique, + int recoverySuccessLimit, + int begin = 0, + int end = -1, + loc statFile = |unknown:///| +) + = testDeleteUntilEol( + recoveryTestConfig(statFile = statFile), standardParser, recoveryParser, source, input, referenceParseTime, referenceNodeCount, referenceNodeCountUnique, recoverySuccessLimit, + begin = begin, + end = end + ); + +str getDeleteUntilEolInput(str input, int begin) { + int lineStart = 0; + list[int] lineEndings = findAll(input, "\n"); + + int line = 0; + for (int lineEnd <- lineEndings) { + line = line + 1; + if (lineEnd < begin) { + continue; + } + for (int pos <- [lineStart..lineEnd]) { + if (pos < begin) { + continue; + } + if (pos > begin) { + throw "Position not found"; + } + return substring(input, 0, pos) + substring(input, lineEnd); + } + lineStart = lineEnd + 1; + } + + throw "Line not found"; +} + +FileStats testDeleteUntilEol( + RecoveryTestConfig config, + &T(value input, loc origin) standardParser, + &T(value input, loc origin) recoveryParser, + loc source, + str input, + int referenceParseTime, + int referenceNodeCount, + int referenceNodeCountUnique, + int recoverySuccessLimit, + int begin = 0, + int end = -1 +) { + FileStats stats = fileStats(); + int lineStart = 0; + list[int] lineEndings = findAll(input, "\n"); + + int line = 0; + int pos = begin; + for (int lineEnd <- lineEndings) { + line = line + 1; + if (lineEnd < begin) { + continue; + } + while(pos <= lineEnd) { + // Check boundaries (used for quick bug testing) + if (end != -1 && end < pos) { + return stats; + } + if (pos < begin) { + pos += 1 + arbInt(config.sampleWindow); + continue; + } + str modifiedInput = input[..pos] + input[lineEnd..]; + source.query = "deletedUntilEol=::"; + TestMeasurement measurement + = testRecovery( + config, + standardParser, + recoveryParser, + modifiedInput, + source, + referenceParseTime, + referenceNodeCount, + referenceNodeCountUnique + ); + stats + = updateStats( + stats, measurement, referenceParseTime, recoverySuccessLimit + ); + pos += 1 + arbInt(config.sampleWindow); + } + lineStart = lineEnd + 1; + println(); + } + + return stats; +} + +private int percentage(int number, int total) { + return total == 0 ? 0 : (100 * number) / total; +} + +int statLabelWidth = 40 ; +int statFieldWidth = 10 ; + +void printFileStats(RecoveryTestConfig config, FileStats fileStats) { + void printStat(str label, int stat, int total, bool prints = true) { + int pct = percentage(stat, total); + print(left(label + ":", statLabelWidth)); + str pctStr = prints ? " (%)" : ""; + println(left("", statFieldWidth)); + } + + println(); + printStat("Total parses", fileStats.totalParses, fileStats.totalParses); + printStat( + "Successful parses", fileStats.successfulParses, fileStats.totalParses + ); + int failedParses = fileStats.totalParses - fileStats.successfulParses; + printStat( + "Successful recoveries", fileStats.successfulRecoveries, failedParses + ); + printStat( + "Failed recoveries", fileStats.failedRecoveries, failedParses + ); + printStat("Parse errors", fileStats.parseErrors, failedParses); + printStat("Slow parses", fileStats.slowParses, failedParses); + printFrequencyTableHeader(); + printFrequencyTableStats( + "Parse time ratios", fileStats.parseTimeRatios, + unit = "log2(ratio)", + printTotal = false + ); + if (config.countNodes) { + printFrequencyTableStats( + "Parse error count", fileStats.errorCounts, unit = "errors" + ); + printFrequencyTableStats( + "Error size", fileStats.errorSizes, unit = "chars" + ); + } +} + +void printFrequencyTableHeader() { + print(left("", statLabelWidth + 1)); + print(right("mean", statFieldWidth)); + print(right("median", statFieldWidth)); + print(right("95 %", statFieldWidth)); + print(right("min", statFieldWidth)); + print(right("max", statFieldWidth)); + println(right("total", statFieldWidth)); +} + +void printFrequencyTableStats( + str label, + FrequencyTable frequencyTable, + str unit = "%", + bool printTotal = true, + bool ignoreZero = false +) { + print(left(label + " ():", statLabelWidth)); + + int totalCount = ( 0 | it + frequencyTable[val] | val <- frequencyTable ); + + int total = 0; + int median = 0; + int medianCount = 0; + int cumulativeCount = 0; + int ninetyFivePercentileLimit = round(totalCount * 0.95); + int ninetyFivePercentile = -1; + int minVal = 1000000000; + int maxVal = -1000000000; + + for (val <- sort(toList(frequencyTable.val))) { + minVal = min(minVal, val); + maxVal = max(maxVal, val); + + int count = frequencyTable[val]; + cumulativeCount += count; + + if (ninetyFivePercentile == -1 && cumulativeCount >= ninetyFivePercentileLimit) + { + ninetyFivePercentile = val; + } + total += val * count; + + if (!(val == 0 && ignoreZero) && count > medianCount) { + medianCount = count; + median = val; + } + } + + if (totalCount == 0) { + print("-"); + } + else { + num mean = round(toReal(total) / totalCount, 0.01); + print(right("", statFieldWidth)); + print(right("", statFieldWidth)); + print(right("", statFieldWidth)); + print(right("", statFieldWidth)); + print(right("", statFieldWidth)); + println(right("", statFieldWidth)); + } +} + +void printStats(RecoveryTestConfig config, TestStats stats) { + if (stats.filesTested != 1) { println("Files tested: "); } + println("Total parses: "); + printFrequencyTableHeader(); + printFrequencyTableStats("Succesful parses", stats.successfulParses); + printFrequencyTableStats( + "Succesful recoveries", stats.successfulRecoveries + ); + printFrequencyTableStats("Failed recoveries", stats.failedRecoveries); + printFrequencyTableStats("Parse errors", stats.parseErrors); + printFrequencyTableStats("Slow parses", stats.slowParses); + printFrequencyTableStats( + "Parse time ratios", stats.parseTimeRatios, + unit = "log2/%", + printTotal = false + ); + if (config.countNodes) { + printFrequencyTableStats( + "Parse error counts", stats.errorCounts, unit = "errors", ignoreZero = true + ); + printFrequencyTableStats( + "Parse error sizes", stats.errorSizes, unit = "chars", ignoreZero = true + ); + } + println(); +} + +private str getTopSort(RecoveryTestConfig config) { + if (contains(config.topSort, "::")) { + // Fully qualified + int last = findLast(config.topSort, "::"); + return substring(config.topSort, last + 2); + } + return config.topSort; +} + +loc zippedFile(str zip, str path) { + loc res = getResource(zip); + loc zipFile = res[scheme = "jar+"][path = res.path + "!/"]; + return zipFile + path; +} + +FileStats testErrorRecovery(loc syntaxFile, str topSort, loc testInput) + = testErrorRecovery( + recoveryTestConfig(syntaxFile = syntaxFile, topSort = topSort), testInput + ); + +FileStats testErrorRecovery(RecoveryTestConfig config, loc testInput) + = testErrorRecovery(config, testInput, readFile(testInput)); + +FileStats testErrorRecovery( + RecoveryTestConfig config, loc testInput, str input +) { + type[value] begin = type(Symbol::\void(), ()); + str topSort = getTopSort(config); + if (config.syntaxModule != "") { + // Use module + println( + "Loading start type from syntax module: , topSort=" + ); + RascalRuntime rt = createRascalRuntime(); + rt.eval(#void, "import ;"); + begin + = rt.eval(#type[value], "type[value] begin = #start[];").val; + } + else { + // Use file + Module \module = parse(#start[Module], config.syntaxFile).top; + str modName = replaceAll("<\module.header.name>", "\\", ""); + println( + "Loading start type from file , module name: , topSort: " + ); + Grammar gram = modules2grammar(modName, {\module}); + + if (sym: \start(\sort(topSort)) <- gram.starts) { + begin = type(sym, gram.rules); + } + else { + throw "Cannot find top sort in "; + } + } + + value(str, loc) standardParser + = parser(begin, allowAmbiguity = true, allowRecovery = false); + recoveryParser + = parser( + begin, + allowAmbiguity = true, + maxAmbDepth = config.maxAmbDepth, + allowRecovery = true, + maxRecoveryAttempts = config.maxRecoveryAttempts, + maxRecoveryTokens = config.maxRecoveryTokens + ); + + // Initialization run + standardParser(input, testInput); + + // Timed run + int startTime = realTime(); + value t = standardParser(input, testInput); + int referenceParseTime = max(1, realTime() - startTime); + int referenceNodeCount = 0; + int referenceNodeCountUnique = 0; + if (Tree tree := t) { + referenceNodeCount = countTreeNodes(tree); + referenceNodeCountUnique = countUniqueTreeNodes(tree); + } + else { + throw "Not a tree? "; + } + + recoverySuccessLimit = 2048; + + //size(input)/4; + str syntaxSpec + = config.syntaxModule == "" ? "" : config.syntaxModule; + println( + "Error recovery of () on , reference parse time: ms." + ); + println("Configuration: "); + + println(); + println("Single char deletions:"); + FileStats singleCharDeletionStats + = testSingleCharDeletions( + config, + standardParser, + recoveryParser, + testInput, + input, + referenceParseTime, + referenceNodeCount, + referenceNodeCountUnique, + recoverySuccessLimit + ); + printFileStats(config, singleCharDeletionStats); + + println(); + println("Deletes until end-of-line:"); + FileStats deleteUntilEolStats + = testDeleteUntilEol( + config, + standardParser, + recoveryParser, + testInput, + input, + referenceParseTime, + referenceNodeCount, + referenceNodeCountUnique, + recoverySuccessLimit + ); + printFileStats(config, deleteUntilEolStats); + + FileStats stats + = mergeFileStats(singleCharDeletionStats, deleteUntilEolStats); + println(); + println( + "-----------------------------------------------------------" + ); + println("Total test stats for :"); + printFileStats(config, stats); + return stats; +} + +private int fileNr = 0 ; + +void batchRecoveryTest(RecoveryTestConfig config) { + println("Running batch test with config "); + + fileNr = 0; + + if (config.statFile != |unknown:///|) { + if (config.countNodes) { + writeFile( + config.statFile, + "source,size,result,duration,durationRatio,nodeRatio,unodeRatio,disambiguationDuration,errorCount,errorSize,nodes,unodes,disambNodes,udisambNodes\n" + ); + } + else { + writeFile( + config.statFile, "source,size,result,duration,durationRatio\n" + ); + } + } + int startTime = realTime(); + TestStats stats = runBatchRecoveryTest(config, testStats()); + int duration = realTime() - startTime; + + println(); + println( + "==================================================================" + ); + println( + "Recovery batch test done in seconds, total result:" + ); + printStats(config, stats); +} + +private +list[loc] gatherFiles( + loc dir, str ext, int maxFiles, int minFileSize, int maxFileSize, int fromFile +) { + list[loc] files = []; + for (entry <- listEntries(dir)) { + loc file = dir + entry; + if (isFile(file)) { + if (endsWith(file.path, ext)) { + str content = readFile(file); + int contentSize = size(content); + if (contentSize >= minFileSize && contentSize < maxFileSize) { + fileNr += 1; + if (fileNr < fromFile) { + println("Skipping file #: , (\< )"); + continue; + } + files += file; + } + } + } + else + if (isDirectory(file)) { + files + += gatherFiles( + file, ext, maxFiles - size(files), minFileSize, maxFileSize, fromFile + ); + } + if (size(files) >= maxFiles) { + break; + } + } + + return files; +} + +private +TestStats runBatchRecoveryTest( + RecoveryTestConfig config, TestStats cumulativeStats +) { + list[loc] files + = gatherFiles( + config.dir, + config.ext, + config.maxFiles, + config.minFileSize, + config.maxFileSize, + config.fromFile + ); + int fileCount = size(files); + println("Batch testing files"); + + int index = 0; + for (file <- files) { + str content = readFile(file); + index += 1; + println( + "========================================================================" + ); + println( + "Testing file # ( of left)" + ); + try { + FileStats fileStats = testErrorRecovery(config, file, content); + cumulativeStats = consolidateStats(cumulativeStats, fileStats); + println(); + println( + "------------------------------------------------------------------------" + ); + println("Cumulative stats after testing :"); + printStats(config, cumulativeStats); + } + catch ParseError(l): + println("Ignoring file because it cannot be parsed: "); + } + + return cumulativeStats; +} + +str getTestInput(loc testUri) { + str query = testUri.query; + loc file = testUri[query = ""]; + + str input = readFile(file); + if (/deletedUntilEol=[0-9]*::/ := query) { + println("deleteUntilEol: begin=, end="); + return getDeleteUntilEolInput(input, toInt(begin)); + } + else + if (/deletedChar=/ := query) { + int at = toInt(index); + return substring(input, 0, at) + substring(input, at + 1); + } + throw "Unsupported test location: "; +} + +RecoveryTestConfig createRecoveryTestConfig(list[str] args) { + loc sourceLoc = |unknown:///|; + str syntaxSpec = "rascal"; + int maxAmbDepth = 2; + int maxFiles = 1000; + int maxFileSize = 1000000; + int minFileSize = 0; + int maxRecoveryAttempts = 50; + int maxRecoveryTokens = 3; + int fromFile = 0; + int sampleWindow = 1; + bool countNodes = false; + loc statFile = |tmp:///error-recovery-test.stats|; + + // |unknown:///| to disable stat writing + for (str arg <- args) { + if (/=/ := arg) { + switch(toLowerCase(name)) { + case "syntax": + syntaxSpec = val; + case "source-loc": + sourceLoc = readTextValueString(#loc, val); + case "max-amb-depth": + maxAmbDepth = toInt(val); + case "max-files": + maxFiles = toInt(val); + case "min-file-size": + minFileSize = toInt(val); + case "max-file-size": + maxFileSize = toInt(val); + case "max-recovery-attempts": + maxRecoveryAttempts = toInt(val); + case "max-recovery-tokens": + maxRecoveryTokens = toInt(val); + case "from-file": + fromFile = toInt(val); + case "stat-file": + statFile = readTextValueString(#loc, val); + case "sample-window": + sampleWindow = toInt(val); + case "random-seed": + arbSeed(toInt(val)); + case "count-nodes": + countNodes = val == "true" || val == "1"; + default: { + throw IllegalArgument(arg, "Unknown argument"); + } + } + } + } + + RecoveryTestConfig config = createSyntaxConfig(syntaxSpec); + config.maxAmbDepth = maxAmbDepth; + config.dir = sourceLoc; + config.maxFiles = maxFiles; + config.minFileSize = minFileSize; + config.maxFileSize = maxFileSize; + config.maxRecoveryAttempts = maxRecoveryAttempts; + config.maxRecoveryTokens = maxRecoveryTokens; + config.fromFile = fromFile; + config.sampleWindow = sampleWindow; + config.countNodes = countNodes; + config.statFile = statFile; + + return config; +} + +private RecoveryTestConfig createSyntaxConfig(str spec) { + println("Determining test config based on syntax spec: "); + switch(spec) { + case "rascal": + return + recoveryTestConfig( + syntaxFile = |std:///lang/rascal/syntax/Rascal.rsc|, + topSort = "Module", + ext = ".rsc" + ); + case "java18": + return + recoveryTestConfig( + syntaxFile = |cwd:///src/lang/java/syntax/Java18.rsc|, + topSort = "CompilationUnit", + ext = ".java" + ); + case "java15": + return + recoveryTestConfig( + syntaxFile = |cwd:///src/lang/java/syntax/Java15.rsc|, + topSort = "CompilationUnit", + ext = ".java" + ); + case "cobol": + return + recoveryTestConfig( + syntaxModule = "lang::vscobolii::Main", topSort = "VSCobolII", ext = ".CBL" + ); + default: + throw IllegalArgument(spec, "Unknown syntax spec"); + // TODO: parse as syntax file, top sort, and extension spec + } +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/ToyRascal.rsc| +/** + * Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/module lang::rascal::tests::concrete::recovery::ToyRascal + +start syntax FunctionDeclaration = Signature FunctionBody; + +syntax Signature = Name Parameters; + +syntax Name + = "f" + | "g" + ; + +syntax Parameters = "(" ")"; + +syntax FunctionBody = "{" Statement* statements "}"; + +syntax Statement + = "{" Statement+ statements "}" + | "if" "(" {Expression ","}+ ")" Statement + | "s" ";" + ; + +syntax Expression = "1"; + +syntax Label + = "l" ":" + | + ; + +layout Layout = [\n\r\t]*; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/ToyRascalRecoveryTests.rsc| +/** + * Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/module lang::rascal::tests::concrete::recovery::ToyRascalRecoveryTests + +import lang::rascal::tests::concrete::recovery::ToyRascal; + +import ParseTree; +import util::ParseErrorRecovery; +import IO; + +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; + +Tree parseToyRascal(str input, bool visualize = false) { + Tree result + = parser( + #start[FunctionDeclaration], allowRecovery = true, allowAmbiguity = true + )( + input, |unknown:///?visualize=< "" >| + ); + list[Tree] errors = findAllParseErrors(result); + if (errors != []) { + println("Tree has errors"); + for (error <- errors) { + println("- "); + } + + println("Best error: "); + } + return result; +} + +test bool toyRascalOk() { + Tree t = parseToyRascal("f(){s;}"); + return !hasParseErrors(t); +} + +test bool toyRascalMissingOpenParen() { + Tree t = parseToyRascal("f){}", visualize = false); + return hasParseErrors(t) && getErrorText(findBestParseErrors(t)[0]) == ")"; +} + +test bool toyRascalMissingCloseParen() { + Tree t = parseToyRascal("f({}", visualize = false); + return hasParseErrors(t) && getErrorText(findBestParseErrors(t)[0]) == "("; +} + +test bool toyRascalMissingIfBody() { + Tree t = parseToyRascal("f(){if(1){}}", visualize = false); + return hasParseErrors(t) && getErrorText(findBestParseErrors(t)[0]) == "}"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/DataTypeSlow.rsc| +module lang::rascal::tests::concrete::recovery::bugs::DataTypeSlow + +import lang::rascal::tests::concrete::recovery::RecoveryTestSupport; +import lang::rascal::\syntax::Rascal; +import ParseTree; +import util::ParseErrorRecovery; +import String; +import IO; +import util::Benchmark; + +bool testDataType() { + normalParser = parser(#start[Module], allowRecovery = false); + recoveryParser + = parser(#start[Module], allowRecovery = true, maxAmbDepth = 1000); + loc source + = |std:///lang/rascal/tests/functionality/DataType.rsc?deletedUntilEol=253:8344:8395|; + str input = getTestInput(source); + + //println("input: "); + int begin = realTime(); + Tree normalParse = normalParser(readFile(source), source); + int norrmalEnd = realTime(); + Tree result = recoveryParser(input, source); + int recoveryEnd = realTime(); + println("normal parse time: "); + println("recovery parse time: "); + + for (i <- [1..2]) + recoveryParser(input, source); + println("total nodes: "); + println("unique nodes: "); + assert input == ""; + + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/FCASlow.rsc| +module lang::rascal::tests::concrete::recovery::bugs::FCASlow + +import lang::rascal::tests::concrete::recovery::RecoveryTestSupport; +import lang::rascal::\syntax::Rascal; +import ParseTree; +import util::ParseErrorRecovery; +import String; +import IO; + +/** +This tests the slowest error recovery in all tests < 10kb +*/bool testFCA() { + recoveryParser + = parser( + #start[Module], + allowRecovery = true, + allowAmbiguity = true, + maxAmbDepth = 1000 + ); + loc source + = |std:///analysis/formalconcepts/FCA.rsc?deletedUntilEol=54:2242:2261|; + str input = getTestInput(source); + + //println("input: "); + Tree result = recoveryParser(input, source); + println("total nodes: "); + println("unique nodes: "); + assert input == ""; + + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/FlowGraphOutOfMemoryBug.rsc| +module lang::rascal::tests::concrete::recovery::bugs::FlowGraphOutOfMemoryBug + +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; +import lang::rascal::tests::concrete::recovery::RecoveryTestSupport; +import lang::rascal::\syntax::Rascal; +import ParseTree; +import IO; +import util::ParseErrorRecovery; +import String; + +test bool testFlowGraphBug() { + standardParser + = parser(#start[Module], allowRecovery = false, allowAmbiguity = true); + recoveryParser + = parser(#start[Module], allowRecovery = true, allowAmbiguity = true); + loc source = |std:///analysis/m3/FlowGraph.rsc|; + input = readFile(source); + + // Resulted in extremely long runtime and eventually an out-of-memory exception + testSingleCharDeletions( + standardParser, recoveryParser, source, input, 200, 150, 100, 100, + begin = 387, + end = 387 + ); + + // If the deletion test completes, the test succeeds + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/HtmlAstSlow.rsc| +module lang::rascal::tests::concrete::recovery::bugs::HtmlAstSlow + +import lang::rascal::tests::concrete::recovery::RecoveryTestSupport; +import lang::rascal::\syntax::Rascal; +import ParseTree; +import util::ParseErrorRecovery; +import util::Benchmark; +import String; +import IO; + +bool testHtmlAst() { + recoveryParser + = parser( + #start[Module], allowRecovery = true, allowAmbiguity = true, maxAmbDepth = 2 + ); + + //loc source = |home:///input/rascal/src/org/rascalmpl/library/lang/html/AST.rsc?deletedUntilEol=131:4688:4690|; + loc source + = |home:///input/rascal/src/org/rascalmpl/library/lang/html/AST.rsc?deletedUntilEol=133:4695:4716|; + str input = getTestInput(source); + + //println("input: "); + int startTime = realTime(); + Tree result = char(0); + for (i <- [1..10]) { + try { + result = recoveryParser(input, source); + } + catch ParseError(_): { + println("ParseError"); + } + } + println("duration: "); + + println("total nodes: "); + println("unique nodes: "); + + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/InfiniteLoopBug.rsc| +/** +* Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/module lang::rascal::tests::concrete::recovery::bugs::InfiniteLoopBug + +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; +import lang::rascal::\syntax::Rascal; +import ParseTree; +import IO; + +test bool testInfiniteLoop() { + str input + = readFile( + |std:///lang/rascal/tests/concrete/recovery/bugs/InfiniteLoopInput.txt| + ); + p = parser(#start[Module], allowRecovery = true, allowAmbiguity = true); + println("starting parse"); + p(input, |unknown:///?visualize=false|); + + // If the parse returns, the test succeeds + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/MinimalMultiError.rsc| +/** +* Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/module lang::rascal::tests::concrete::recovery::bugs::MinimalMultiError + +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; + +layout Layout = [\ ]* !>> [\ ]; + +syntax S = T; + +syntax T = AB AB AB End; +syntax AB = 'a' 'b'; +syntax End = "$"; + +test bool multiOk() = checkRecovery ( + #S, + "ababab$", + [] + ); + +test bool multiOneError() = checkRecovery ( + #S, + "acabab$", + ["c"] + ); + +test bool multiTwoError() = checkRecovery ( + #S, + "acacab$", + ["c", "c"] + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/MultiErrorBug.rsc| +/** +* Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/module lang::rascal::tests::concrete::recovery::bugs::MultiErrorBug + +import ParseTree; +import IO; +import util::ParseErrorRecovery; +import List; +import vis::Text; + +start syntax Module = SyntaxDefinition*; + +syntax SyntaxDefinition = Prod ";"; + +lexical Name = [A-Z]; + +syntax Sym + = Sym NonterminalLabel + | StringConstant + ; + +lexical StringConstant = "\"" StringCharacter* "\""; + +lexical StringCharacter = ![\"]; + +lexical NonterminalLabel = [a-z]; + +syntax Prod = Sym*; + +// This is an open issue: instead of two small errors, one big error tree is returned. +bool multiErrorBug() { + str input = "#\"a\";#\"b\";"; + Tree t + = parser(#start[Module], allowRecovery = true, allowAmbiguity = true)( + input, |unknown:///?visualize=false| + ); + + println(prettyTree(t)); + + list[Tree] errors = findAllParseErrors(t); + println(" Errors"); + for (Tree error <- errors) { + Tree skipped = getSkipped(error); + println(" : "); + } + Tree disambiguated = disambiguateParseErrors(t); + list[Tree] disambiguatedErrors = findAllParseErrors(disambiguated); + println("After disambiguating:"); + println(" Errors"); + for (Tree error <- disambiguatedErrors) { + Tree skipped = getSkipped(error); + println(" : "); + } + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/MultiErrorPico.rsc| +/** +* Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/module lang::rascal::tests::concrete::recovery::bugs::MultiErrorPico + +import lang::pico::\syntax::Main; +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; + +bool multiErrorPico() { + return + checkRecovery ( + #start[Program], + "begin + declare; + i := #1; + j := #2; + k := 3 +end", + ["#1", "#2"] + ); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/MultiProdRecovery.rsc| +/** +* Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/module lang::rascal::tests::concrete::recovery::bugs::MultiProdRecovery + +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; + +layout Layout = [\ ]* !>> [\ ]; + +syntax Prog = Stat*; + +syntax Stat + = Expr ";" + | "{" Stat* "}" + ; + +syntax Expr + = Expr "+" Expr + | Expr "-" Expr + | "e" + ; + +test bool multiProdOk() = checkRecovery ( + #Prog, + "{ e + e; }", + [] + ); + +test bool multiProdOperatorError() + = checkRecovery ( + #Prog, + "{ e * e; }", + ["* "] + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/OvertakeBug.rsc| +/** +* Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/module lang::rascal::tests::concrete::recovery::bugs::OvertakeBug + +import lang::rascal::\syntax::Rascal; +import ParseTree; +import IO; + +bool testOvertakeBug() { + str input + = readFile( + |std:///lang/rascal/tests/concrete/recovery/bugs/OvertakeBugInput.txt| + ); + parser(#Module, allowRecovery = true, allowAmbiguity = true)( + input, |unknown:///?visualize=false| + ); + + // If the parse completes without crashing, the test succeeds + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/OvertakenNullableBug.rsc| +/** +* Copyright (c) 2024, NWO-I Centrum Wiskunde & Informatica (CWI) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/module lang::rascal::tests::concrete::recovery::bugs::OvertakenNullableBug + +import lang::rascal::tests::concrete::recovery::RecoveryCheckSupport; +import lang::rascal::tests::concrete::recovery::RecoveryTestSupport; +import lang::rascal::\syntax::Rascal; +import ParseTree; +import IO; + +test bool testOvertakeNullableBug() { + standardParser + = parser(#start[Module], allowRecovery = false, allowAmbiguity = true); + recoveryParser + = parser(#start[Module], allowRecovery = true, allowAmbiguity = true); + loc source + = |std:///lang/rascal/tests/library/analysis/statistics/DescriptiveTests.rsc|; + input = readFile(source); + + testDeleteUntilEol( + standardParser, recoveryParser, source, input, 200, 100, 100, 100, + begin = 561, + end = 561 + ); + + // If the deletion test succeeds without crashing, the test succeeds + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/Patterns3Slow.rsc| +module lang::rascal::tests::concrete::recovery::bugs::Patterns3Slow + +import lang::rascal::tests::concrete::recovery::RecoveryTestSupport; +import lang::rascal::\syntax::Rascal; +import ParseTree; +import util::ParseErrorRecovery; +import util::Benchmark; +import String; +import IO; + +/** +This tests the slowest error recovery in all tests < 10kb +*/bool testPatterns3() { + loc source + = |std:///lang/rascal/tests/concrete/Patterns3.rsc?deletedUntilEol=85:2133:2136|; + str input = getTestInput(source); + recoveryParser + = parser( + #start[Module], + allowRecovery = true, + allowAmbiguity = true, + maxRecoveryAttempts = 50, + maxRecoveryTokens = 30 + ); + + // First fast run to generate the parser + recoveryParser(readFile(source), source); + + // Now time a regular parse + int normalBegin = realTime(); + Tree tree = recoveryParser(readFile(source), source); + + int recoveryBegin = realTime(); + println("normal parse time: "); + + Tree result = recoveryParser(input, source); + for (i <- [1..10]) { + recoveryParser(input, source); + } + int end = realTime(); + println("recovery time: "); + + println("total nodes: "); + println("unique nodes: "); + return input == ""; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/PreferAvoidSlow.rsc| +module lang::rascal::tests::concrete::recovery::bugs::PreferAvoidSlow + +import lang::rascal::tests::concrete::recovery::RecoveryTestSupport; +import lang::rascal::\syntax::Rascal; +import ParseTree; +import util::ParseErrorRecovery; +import String; +import IO; + +bool testPreferAvoid() { + recoveryParser + = parser( + #start[Module], + allowRecovery = true, + allowAmbiguity = true, + maxAmbDepth = 1000 + ); + loc source + = |std:///lang/sdf2/filters/PreferAvoid.rsc?deletedUntilEol=8:208:271|; + str input = getTestInput(source); + println("input: "); + Tree result = recoveryParser(input, source); + println("total nodes: "); + println("unique nodes: "); + assert input == ""; + + for (i <- [0..10]) { + println("Pruning at level "); + pruned = pruneAmbiguities(result, maxDepth = i); + println(" total nodes at level : "); + println( + " unique nodes at level : " + ); + } + + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/concrete/recovery/bugs/ScratchNPE.rsc| +module lang::rascal::tests::concrete::recovery::bugs::ScratchNPE + +import ParseTree; +import util::ParseErrorRecovery; +import Exception; + +start syntax Ambiguous + = "foo" + | [f] [o] [o] + ; + +void testFoo() { + bool ambExceptionThrown = false; + try { + parse(#Ambiguous, "foo", allowRecovery = true, allowAmbiguity = false); + } + catch Ambiguity(l, nonterminal, sentence): { + ambExceptionThrown = true; + assert nonterminal == "Ambiguous"; + assert sentence == "foo"; + } + + assert + ambExceptionThrown : "Ambiguity exception should have been thrown"; + + Tree t = parse(#Ambiguous, "foo", allowRecovery = true, allowAmbiguity = true); + assert amb({_, _}) := t; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extend_function1/M1.rsc| +module lang::rascal::tests::extend_function1::M1 + +default int f(int n) = n; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extend_function1/M2.rsc| +module lang::rascal::tests::extend_function1::M2 + +import lang::rascal::tests::extend_function1::M1; + +int f(2) = 20; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extend_function1/M3.rsc| +module lang::rascal::tests::extend_function1::M3 + +import lang::rascal::tests::extend_function1::M2; + +import Exception; + +int f(3) = 30; + +test bool t1() { + try { + f(1); + return false; + } + catch CallFailed(_): + return true; +} +test bool t2() = f(2) == 20; +test bool t3() = f(3) == 30; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/A1.rsc| +module lang::rascal::tests::extends1::A1 + +extend lang::rascal::tests::extends1::Base; + +import lang::rascal::tests::extends1::A2; + +test bool a1base1() { + S s = [S] "a"; + return s == [S] "a"; +} + +test bool a1base2() { + Sstar s = [Sstar] "aaa"; + return s == [Sstar] "aaa"; +} + +test bool a1base3() { + INTEGER n = 13; + return n == 13; +} + +test bool a1base4() { + D x = d1(); + return x == d1(); +} + +test bool a1base5() { + return ident(13) == 13; +} + +test bool a1base6() { + return f(0) == "zero"; +} + +test bool a1base7() { + return f(9) == "value"; +} + +test bool a1extendedBase1() { + S s = [S] "z"; + return s == [S] "z"; +} + +test bool a1extendedBase2() { + Sstar s = [Sstar] "aza"; + return s == [Sstar] "aza"; +} + +test bool a1extendedBase3() { + D x = d2(); + return x == d2(); +} + +test bool a1extendedBase4() { + E x = e(); + return x == e(); +} + +test bool a1extendedBase5() { + STRING s = "abc"; + return s == "abc"; +} + +test bool a1extendedBase6() { + LIST_INTEGER lst = [1, 2, 3]; + return lst == [1, 2, 3]; +} + +test bool a1extendedBase7() { + return ident("abc") == "abc"; +} + +test bool a1extendedBase8() { + return f(1) == "one"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/A2.rsc| +module lang::rascal::tests::extends1::A2 + +extend lang::rascal::tests::extends1::BaseExtended; + +import lang::rascal::tests::extends1::A1; + +test bool a2base1() { + S s = [S] "a"; + return s == [S] "a"; +} + +test bool a2base2() { + Sstar s = [Sstar] "aaa"; + return s == [Sstar] "aaa"; +} + +test bool a2base3() { + INTEGER n = 13; + return n == 13; +} + +test bool a2base4() { + D x = d1(); + return x == d1(); +} + +test bool a2base5() { + return ident(13) == 13; +} + +test bool a2base6() { + return f(0) == "zero"; +} + +test bool a2base7() { + return f(9) == "value"; +} + +test bool a2extendedBase1() { + S s = [S] "z"; + return s == [S] "z"; +} + +test bool a2extendedBase2() { + Sstar s = [Sstar] "aza"; + return s == [Sstar] "aza"; +} + +test bool a2extendedBase3() { + D x = d2(); + return x == d2(); +} + +test bool a2extendedBase4() { + E x = e(); + return x == e(); +} + +test bool a2extendedBase5() { + STRING s = "abc"; + return s == "abc"; +} + +test bool a2extendedBase6() { + LIST_INTEGER lst = [1, 2, 3]; + return lst == [1, 2, 3]; +} + +test bool a2extendedBase7() { + return ident("abc") == "abc"; +} + +test bool a2extendedBase8() { + return f(1) == "one"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/ABSTRACTTYPE.rsc| +module lang::rascal::tests::extends1::ABSTRACTTYPE + +extend lang::rascal::tests::extends1::PARSETREE; + +data SYM = E(); + +// A <: B +// A <: C +// A <: D +// sort(_) <: TREE +// C <: E +// Z <: A +bool subtype(C(), E()) = true; +bool subtype(Z(), A()) = true; + +test bool ATcomparable1() = comparable(A(), B()); +test bool ATcomparable2() = comparable(B(), A()); + +test bool ATcomparable3() = comparable(A(), C()); +test bool ATcomparable4() = comparable(C(), A()); + +test bool ATcomparable5() = comparable(A(), D()); +test bool ATcomparable6() = comparable(D(), A()); + +test bool ATcomparable7() = !comparable(C(), D()); +test bool ATcomparable8() = !comparable(D(), C()); + +test bool ATsubtype1() = subtype(A(), B()); +test bool ATsubtype2() = subtype(A(), C()); +test bool ATsubtype3() = subtype(A(), D()); +test bool ATsubtype4() = subtype(SORT(A()), TREE()); +test bool ATsubtype5() = subtype(SORT(B()), TREE()); +test bool ATsubtype6() = subtype(SORT(C()), TREE()); +test bool ATsubtype7() = subtype(SORT(D()), TREE()); +test bool ATubtype8() = subtype(C(), E()); +test bool ATubtype9() = subtype(Z(), A()); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/B1.rsc| +module lang::rascal::tests::extends1::B1 + +import lang::rascal::tests::extends1::B2; +import lang::rascal::tests::extends1::B3; + +import lang::rascal::tests::extends1::Base; + +int add1(int n) = n + 1; + +test bool b1base1() { + S s = [S] "a"; + return s == [S] "a"; +} + +test bool b1base2() { + Sstar s = [Sstar] "aaa"; + return s == [Sstar] "aaa"; +} + +test bool b1base3() { + INTEGER n = 13; + return n == 13; +} + +test bool b1base4() { + D x = d1(); + return x == d1(); +} + +test bool b1base5() { + return ident(13) == 13; +} + +test bool b1base6() { + return f(0) == "zero"; +} + +test bool b1base7() { + return f(9) == "value"; +} + +// Defined in B[1-3] +test bool b1tadd1() = add1(5) == 6; +test bool b1tadd2() = add2(5) == 7; +test bool b1tadd3() = add3(5) == 8; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/B2.rsc| +module lang::rascal::tests::extends1::B2 + +import lang::rascal::tests::extends1::B1; +import lang::rascal::tests::extends1::B3; + +import lang::rascal::tests::extends1::BaseExtended; + +int add2(int n) = n + 2; + +test bool b2base1() { + S s = [S] "a"; + return s == [S] "a"; +} + +test bool b2base2() { + Sstar s = [Sstar] "aaa"; + return s == [Sstar] "aaa"; +} + +test bool b2base3() { + INTEGER n = 13; + return n == 13; +} + +test bool b2base4() { + D x = d1(); + return x == d1(); +} + +test bool b2base5() { + return ident(13) == 13; +} + +test bool b2base6() { + return f(0) == "zero"; +} + +test bool b2base7() { + return f(9) == "value"; +} + +test bool b2extendedBase1() { + S s = [S] "z"; + return s == [S] "z"; +} + +test bool b2extendedBase2() { + Sstar s = [Sstar] "aza"; + return s == [Sstar] "aza"; +} + +test bool b2extendedBase3() { + D x = d2(); + return x == d2(); +} + +test bool b2extendedBase4() { + E x = e(); + return x == e(); +} + +test bool b2extendedBase5() { + STRING s = "abc"; + return s == "abc"; +} + +test bool b2extendedBase6() { + LIST_INTEGER lst = [1, 2, 3]; + return lst == [1, 2, 3]; +} + +test bool b2extendedBase7() { + return ident("abc") == "abc"; +} + +test bool b2extendedBase8() { + return f(1) == "one"; +} + +// Defined in B[1-3] +test bool b2tadd1() = add1(5) == 6; +test bool b2tadd2() = add2(5) == 7; +test bool b2tadd3() = add3(5) == 8; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/B3.rsc| +module lang::rascal::tests::extends1::B3 + +import lang::rascal::tests::extends1::B1; +import lang::rascal::tests::extends1::B2; + +int add3(int n) = n + 3; + +// Defined in B[1-3] +test bool b3tadd1() = add1(5) == 6; +test bool b3tadd2() = add2(5) == 7; +test bool b3tadd3() = add3(5) == 8; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/Base.rsc| +module lang::rascal::tests::extends1::Base + +syntax S = "a"; + +syntax Sstar = S*; + +alias INTEGER = int; + +data D = d1(); + +int ident(int n) = n; + +str f(0) = "zero"; + +default str f(value v) = "value"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/Base1.rsc| +module lang::rascal::tests::extends1::Base1 + +data A + = a() + | b() + ; + +str default_message = "default use" ; + +default str EXTENDED_FUNCTION(value _) = default_message; + +str apply_EXTENDED_FUNCTION_a() = EXTENDED_FUNCTION(a()); + +str apply_EXTENDED_FUNCTION_b() = EXTENDED_FUNCTION(b()); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/Base2.rsc| +@license{ + Copyright (c) 2009-2020 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module lang::rascal::tests::extends1::Base2 + +import ParseTree; + +str default_message = "default use" ; + +default str EXTENDED_FUNCTION(int _, Tree _) = default_message; + +str apply_EXTENDED_FUNCTION_a() + = EXTENDED_FUNCTION( + 5, // Now follows the expanded version of (A) `a` + appl ( + prod(sort("A"), [\char-class([range(97, 98)])], {}), + [char(97)] + ) + ); + +str apply_EXTENDED_FUNCTION_b() + = EXTENDED_FUNCTION( + 5, // Now follows the expanded version of (A) `b` + appl ( + prod(sort("A"), [\char-class([range(97, 98)])], {}), + [char(98)] + ) + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/Base3.rsc| +module lang::rascal::tests::extends1::Base3 + +import ParseTree; + +str default_message = "default use" ; + +default str EXTENDED_FUNCTION(Tree _) = default_message; + +str apply_EXTENDED_FUNCTION_a() + = EXTENDED_FUNCTION( + // Now follows the expanded version of (A) `a` + appl ( + prod(sort("A"), [\char-class([range(97, 99)])], {}), + [char(97)] + ) + ); + +str apply_EXTENDED_FUNCTION_b() + = EXTENDED_FUNCTION( + // Now follows the expanded version of (A) `b` + appl ( + prod(sort("A"), [\char-class([range(97, 99)])], {}), + [char(98)] + ) + ); + +str apply_EXTENDED_FUNCTION_c() + = EXTENDED_FUNCTION( + // Now follows the expanded version of (A) `c` + appl ( + prod(sort("A"), [\char-class([range(97, 99)])], {}), + [char(99)] + ) + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/BaseExtended.rsc| +module lang::rascal::tests::extends1::BaseExtended + +extend lang::rascal::tests::extends1::Base; + +syntax S = "z"; + +alias STRING = str; + +alias LIST_INTEGER = list[INTEGER]; + +data D = d2(); + +data E = e(); + +str ident(str s) = s; + +str f(1) = "one"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/CHECKTYPES.rsc| +module lang::rascal::tests::extends1::CHECKTYPES + +import lang::rascal::tests::extends1::ABSTRACTTYPE; + +// A <: B +// A <: C +// A <: D +// sort(_) <: TREE +// C <: E +// Z <: A +test bool CTcomparable1() = comparable(A(), B()); +test bool CTcomparable2() = comparable(B(), A()); + +test bool CTcomparable3() = comparable(A(), C()); +test bool CTcomparable4() = comparable(C(), A()); + +test bool CTcomparable5() = comparable(A(), D()); +test bool CTcomparable6() = comparable(D(), A()); + +test bool CTcomparable7() = !comparable(C(), D()); +test bool CTcomparable8() = !comparable(D(), C()); + +test bool CTsubtype1() = subtype(A(), B()); +test bool CTsubtype2() = subtype(A(), C()); +test bool CTsubtype3() = subtype(A(), D()); +test bool CTsubtype4() = subtype(SORT(A()), TREE()); +test bool CTsubtype5() = subtype(SORT(B()), TREE()); +test bool CTsubtype6() = subtype(SORT(C()), TREE()); +test bool CTsubtype7() = subtype(SORT(D()), TREE()); +test bool CTsubtype8() = subtype(C(), E()); +test bool CTsubtype9() = subtype(Z(), A()); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/COMPILER.rsc| +module lang::rascal::tests::extends1::COMPILER + +import lang::rascal::tests::extends1::ABSTRACTTYPE; + +// A <: B +// A <: C +// A <: D +// sort(_) <: TREE +// C <: E +// Z <: A +test bool CMcomparable1() = comparable(A(), B()); +test bool CMcomparable2() = comparable(B(), A()); + +test bool CMcomparable3() = comparable(A(), C()); +test bool CMcomparable4() = comparable(C(), A()); + +test bool CMcomparable5() = comparable(A(), D()); +test bool CMcomparable6() = comparable(D(), A()); + +test bool CMcomparable7() = !comparable(C(), D()); +test bool CMcomparable8() = !comparable(D(), C()); + +test bool CMcomparable9() = comparable(C(), E()); +test bool CMcomparable10() = comparable(Z(), A()); + +test bool CMsubtype1() = subtype(A(), B()); +test bool CMsubtype2() = subtype(A(), C()); +test bool CMsubtype3() = subtype(A(), D()); +test bool CMsubtype4() = subtype(SORT(A()), TREE()); +test bool CMsubtype5() = subtype(SORT(B()), TREE()); +test bool CMsubtype6() = subtype(SORT(C()), TREE()); +test bool CMsubtype7() = subtype(SORT(D()), TREE()); +test bool CMsubtype8() = subtype(C(), E()); +test bool CMsubtype9() = subtype(Z(), A()); + +value main() = subtype(Z(), A()); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/Extension1.rsc| +module lang::rascal::tests::extends1::Extension1 + +// Extension of a function str(value) with str(A) +extend lang::rascal::tests::extends1::Base1; + +str non_default_message = "*** NON-default use ***" ; + +str EXTENDED_FUNCTION(a()) = non_default_message; + +test bool apply1() = apply_EXTENDED_FUNCTION_a() == non_default_message; + +test bool apply2() = apply_EXTENDED_FUNCTION_b() == default_message; + +str main() = apply_EXTENDED_FUNCTION_a(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/Extension2.rsc| +module lang::rascal::tests::extends1::Extension2 + +// Extension of a function str(int, Tree) with str(int, A) +extend lang::rascal::tests::extends1::Base2; + +syntax A = [ab]; + +str non_default_message = "*** NON-default use ***" ; + +str EXTENDED_FUNCTION(int n, a: (A) `a`) = non_default_message; + +str main() = apply_EXTENDED_FUNCTION_a(); + +test bool apply1() = apply_EXTENDED_FUNCTION_a() == non_default_message; +test bool apply2() = apply_EXTENDED_FUNCTION_b() == default_message; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/Extension3.rsc| +module lang::rascal::tests::extends1::Extension3 + +// Extension of a function str(Tree) with str(A) +// Note: A concrete type at position 0. +extend lang::rascal::tests::extends1::Base3; + +syntax A = [abc]; + +str non_default_message_a = "*** NON-default use a ***" ; +str non_default_message_c = "*** NON-default use c ***" ; + +str EXTENDED_FUNCTION(a: (A) `a`) = non_default_message_a; +str EXTENDED_FUNCTION(a: (A) `c`) = non_default_message_c; + +str main() = apply_EXTENDED_FUNCTION_a(); + +test bool apply1() = apply_EXTENDED_FUNCTION_a() == non_default_message_a; +test bool apply2() = apply_EXTENDED_FUNCTION_b() == default_message; +test bool apply3() = apply_EXTENDED_FUNCTION_c() == non_default_message_c; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/PARENT.rsc| +module lang::rascal::tests::extends1::PARENT + +import lang::rascal::tests::extends1::PARSETREE; +import lang::rascal::tests::extends1::TYPE; + +test bool dontShareANameResolutionCache1() + = lang::rascal::tests::extends1::TYPE::comparable(A(), B()) + && lang::rascal::tests::extends1::PARSETREE::comparable(A(), C()); + +test bool dontShareANameResolutionCache2() + = lang::rascal::tests::extends1::PARSETREE::comparable(A(), C()) + && lang::rascal::tests::extends1::TYPE::comparable(A(), B()); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/PARSETREE.rsc| +module lang::rascal::tests::extends1::PARSETREE + +extend lang::rascal::tests::extends1::TYPE; + +data SYM + = C() + | D() + | SORT(SYM sym) + | TREE() + ; + +// A <: B +// A <: C +// A <: D +// sort(_) <: TREE +bool subtype(A(), C()) = true; +bool subtype(A(), D()) = true; + +bool subtype(SORT(_), TREE()) = true; + +test bool PTcomparable1() = comparable(A(), B()); +test bool PTcomparable2() = comparable(B(), A()); + +test bool PTcomparable3() = comparable(A(), C()); +test bool PTcomparableP4() = comparable(C(), A()); + +test bool PTcomparable5() = comparable(A(), D()); +test bool PTcomparable6() = comparable(D(), A()); + +test bool PTcomparable7() = !comparable(C(), D()); +test bool PTcomparable8() = !comparable(D(), C()); + +test bool PTsubtype1() = subtype(A(), B()); +test bool PTsubtype2() = subtype(A(), C()); +test bool PTsubtype3() = subtype(A(), D()); +test bool PTsubtype4() = subtype(SORT(A()), TREE()); +test bool PTsubtype5() = subtype(SORT(B()), TREE()); +test bool PTsubtype6() = subtype(SORT(C()), TREE()); +test bool PTsubtype7() = subtype(SORT(D()), TREE()); + +@ignore{This failing test seems to be an error in the interpreter; worked before in compiler but is now also broken} +test bool PTsubtype8() = !subtype(Z(), A()); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/TYPE.rsc| +module lang::rascal::tests::extends1::TYPE + +// A <: B +data SYM + = A() + | B() + | Z() + ; + +public bool comparable(SYM s, SYM t) = subtype(s, t) || subtype(t, s); + +bool subtype(A(), B()) = true; + +default bool subtype(SYM s, SYM t) = false; + +test bool TPcomparable1() = comparable(A(), B()); +test bool TPcomparable2() = comparable(B(), A()); + +@ignoreInterpreter{This failing test seems to be an error in the interpreter} +test bool TPsubtype1() = !subtype(Z(), A()); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/UseExtendBase.rsc| +module lang::rascal::tests::extends1::UseExtendBase + +extend lang::rascal::tests::extends1::Base; + +syntax S = "b"; + +test bool base1() { + S s = [S] "a"; + return s == [S] "a"; +} + +test bool base2() { + Sstar s = [Sstar] "aaa"; + return s == [Sstar] "aaa"; +} + +test bool base3() { + INTEGER n = 13; + return n == 13; +} + +test bool base4() { + D x = d1(); + return x == d1(); +} + +test bool base5() { + return ident(13) == 13; +} + +test bool base6() { + return f(0) == "zero"; +} + +test bool base7() { + return f(9) == "value"; +} + +// Extensions +test bool extend1() { + S s = [S] "b"; + return s == [S] "b"; +} + +test bool extend2() { + Sstar s = [Sstar] "abab"; + return s == [Sstar] "abab"; +} + +str f(2) = "two"; + +test bool extend3() { + return f(2) == "two"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/UseExtendBaseExtended.rsc| +module lang::rascal::tests::extends1::UseExtendBaseExtended + +syntax S = "c"; + +extend lang::rascal::tests::extends1::BaseExtended; + +test bool base1() { + S s = [S] "a"; + return s == [S] "a"; +} + +test bool base2() { + Sstar s = [Sstar] "aaa"; + return s == [Sstar] "aaa"; +} + +test bool base3() { + INTEGER n = 13; + return n == 13; +} + +test bool base4() { + D x = d1(); + return x == d1(); +} + +test bool base5() { + return ident(13) == 13; +} + +test bool base6() { + return f(0) == "zero"; +} + +test bool base7() { + return f(9) == "value"; +} + +test bool extendedBase1() { + S s = [S] "z"; + return s == [S] "z"; +} + +test bool extendedBase2() { + Sstar s = [Sstar] "aza"; + return s == [Sstar] "aza"; +} + +test bool extendedBase3() { + D x = d2(); + return x == d2(); +} + +test bool extendedBase4() { + E x = e(); + return x == e(); +} + +test bool extendedBase5() { + STRING s = "abc"; + return s == "abc"; +} + +test bool extendedBase6() { + LIST_INTEGER lst = [1, 2, 3]; + return lst == [1, 2, 3]; +} + +test bool extendedBase7() { + return ident("abc") == "abc"; +} + +test bool extendedBase8() { + return f(1) == "one"; +} + +// extensions +test bool extend1() { + S s = [S] "c"; + return s == [S] "c"; +} + +test bool extend2() { + Sstar s = [Sstar] "azc"; + return s == [Sstar] "azc"; +} + +str f(3) = "three"; + +test bool extend3() { + return f(3) == "three"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/UseImportBase.rsc| +module lang::rascal::tests::extends1::UseImportBase + +import lang::rascal::tests::extends1::Base; + +test bool base1() { + S s = [S] "a"; + return s == [S] "a"; +} + +test bool base2() { + Sstar s = [Sstar] "aaa"; + return s == [Sstar] "aaa"; +} + +test bool base3() { + INTEGER n = 13; + return n == 13; +} + +test bool base4() { + D x = d1(); + return x == d1(); +} + +test bool base5() { + return ident(13) == 13; +} + +test bool base6() { + return f(0) == "zero"; +} + +test bool base7() { + return f(9) == "value"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends1/UseImportBaseExtended.rsc| +module lang::rascal::tests::extends1::UseImportBaseExtended + +import lang::rascal::tests::extends1::BaseExtended; + +test bool base1() { + S s = [S] "a"; + return s == [S] "a"; +} + +test bool base2() { + Sstar s = [Sstar] "aaa"; + return s == [Sstar] "aaa"; +} + +test bool base3() { + INTEGER n = 13; + return n == 13; +} + +test bool base4() { + D x = d1(); + return x == d1(); +} + +test bool base5() { + return ident(13) == 13; +} + +test bool base6() { + return f(0) == "zero"; +} + +test bool base7() { + return f(9) == "value"; +} + +test bool extendedBase1() { + S s = [S] "z"; + return s == [S] "z"; +} + +test bool extendedBase2() { + Sstar s = [Sstar] "azaz"; + return s == [Sstar] "azaz"; +} + +test bool extendedBase3() { + D x = d2(); + return x == d2(); +} + +test bool extendedBase4() { + E x = e(); + return x == e(); +} + +test bool extendedBase5() { + STRING s = "abc"; + return s == "abc"; +} + +test bool extendedBase6() { + LIST_INTEGER lst = [1, 2, 3]; + return lst == [1, 2, 3]; +} + +test bool extendedBase7() { + return ident("abc") == "abc"; +} + +test bool extendedBase8() { + return f(1) == "one"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends2/CHECKER.rsc| +module lang::rascal::tests::extends2::CHECKER + +extend lang::rascal::tests::extends2::TYPEPAL; + +str collect(int _) { + return "CHECKER"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends2/COLLECTOR.rsc| +module lang::rascal::tests::extends2::COLLECTOR + +default str collect(value _) { + return "COLLECTOR"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends2/RUNCHECKER.rsc| +module lang::rascal::tests::extends2::RUNCHECKER + +extend lang::rascal::tests::extends2::CHECKER; + +str main() { + return doCollect(1); +} + +test bool collect1() = collect(1) == "CHECKER"; + +test bool doCollect1() = doCollect(1) == "CHECKER"; + +test bool indirectDoCollect1() = indirectDoCollect(1, collect) == "CHECKER"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends2/TYPEPAL.rsc| +module lang::rascal::tests::extends2::TYPEPAL + +extend lang::rascal::tests::extends2::COLLECTOR; + +str doCollect(int x) { + return collect(x); +} + +str indirectDoCollect(int x, str(int) my_collect) { + return my_collect(x); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends3/A.rsc| +module lang::rascal::tests::extends3::A + +layout L1 = " "+ !>> " "; +syntax A = "a"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends3/B.rsc| +module lang::rascal::tests::extends3::B + +extend lang::rascal::tests::extends3::A; +syntax B = A+; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends3/C.rsc| +module lang::rascal::tests::extends3::C + +import lang::rascal::tests::extends3::A; +syntax C = "{" A "}"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends3/D.rsc| +module lang::rascal::tests::extends3::D + +extend lang::rascal::tests::extends3::B; +syntax D = "[" A "]"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends3/E.rsc| +module lang::rascal::tests::extends3::E + +import lang::rascal::tests::extends3::B; +syntax E = "\<" A "\>"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends3/F.rsc| +module lang::rascal::tests::extends3::F + +import lang::rascal::tests::extends3::B; + +test bool match((B)`a a`) = true; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends3/Modules2DefinitionTest.rsc| +module lang::rascal::tests::extends3::Modules2DefinitionTest + +import lang::rascal::\syntax::Rascal; +import lang::rascal::grammar::definition::Parameters; +import lang::rascal::grammar::definition::Modules; +import lang::rascal::grammar::definition::Names; +import lang::rascal::grammar::definition::Layout; +import util::Reflective; +import Grammar; +import IO; + +@synopsis{Uses example modules A, B, C, D, E, and F to see how layout definitions propagate over import and extend dependencies.} +test bool layoutPropagationOverImportAndExtendTest() { + testModules + = {m + | m <- + |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends3/|.ls, /[A-F].rsc/ := m.file + }; + + trees = {parseModuleWithSpaces(m).top| m <- testModules, bprintln(m)}; + + for (Module m <- trees) { + str main = ""; + + def = modules2definition(main, trees); + + gr = resolve(fuse(layouts(def))); + + // the L1 layout definition should have propagated to all of the modules + assert layouts("L1") in gr.rules : "L1 is not in
: + ' "; + } + + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends4/A.rsc| +module lang::rascal::tests::extends4::A + +private int glob = 42 ; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends4/B.rsc| +module lang::rascal::tests::extends4::B + +extend lang::rascal::tests::extends4::A; + +data X = glob(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends4/C.rsc| +module lang::rascal::tests::extends4::C + +import lang::rascal::tests::extends4::B; +import IO; + +// issue 2513 +test bool privateExtendedGlobalShouldNotHidePublicConstructor() { + println(glob); + return X _ := glob(); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends5/BasicTModel.rsc| +module lang::rascal::tests::extends5::BasicTModel + +data Use = use(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends5/ConfigurableScopeGraph.rsc| +module lang::rascal::tests::extends5::ConfigurableScopeGraph + +// now we first go into B +import lang::rascal::tests::extends5::StringSimilarity; + +// we extend things +extend lang::rascal::tests::extends5::TModel; + +void f(Use use, X x) { +// nothing much +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends5/StringSimilarity.rsc| +module lang::rascal::tests::extends5::StringSimilarity + +// but B is cyclic with A, so we stop the recursion +import lang::rascal::tests::extends5::ConfigurableScopeGraph; + +// a use of Use which will fail with #2544 +data X = x(Use u); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends5/TModel.rsc| +module lang::rascal::tests::extends5::TModel + +extend lang::rascal::tests::extends5::BasicTModel; + +data SomethingExtra = extra(Use u); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/extends5/Top.rsc| +module lang::rascal::tests::extends5::Top + +// this starts the import algorithm at a place that triggers #2554 +import lang::rascal::tests::extends5::ConfigurableScopeGraph; + +set[value] nothing() = {f}; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Accumulating.rsc| +module lang::rascal::tests::functionality::Accumulating + +test bool testForWithNoAppend(){ + return { for(x <- [1,2,3]) { ; } } == []; +} + +test bool testForWithAppend() { + return {for (x <- [1,2,3]) append x; } == [1,2,3]; +} + +test bool testForWithAppendAfterSomethingElse() { + return { for (x <- [1,2,3]) { x += 1; append x; }} == [2,3,4]; +} + +test bool testForWithAppendAndLabel() { + return { y: for (x <- [1,2,3]) { append y: x; }} == [1,2,3]; +} + +test bool testForWithAppendAndLabelOuter() { + return { y: for (_ <- [1,2,3]) { for (i <- [1,2,3]) append y: i; }} == [1,2,3,1,2,3,1,2,3]; +} + +test bool testForWithAppendAndLabelOuterAndInner() { + // unused label for testing purposes + return { y: for (_ <- [1,2,3]) { z: for (i <- [1,2,3]) append y: i; }} == [1,2,3,1,2,3,1,2,3]; +} + +test bool testNestedAppend() { + return { for (_ <- [1,2,3]) append for (i <- [1,2,3]) append i; } == [[1,2,3],[1,2,3],[1,2,3]]; +} + +test bool testSimpleNestedFor() { + return {for (_ <- [1,2,3]) append for (y <- [1,2,3]) append y; } == [[1,2,3],[1,2,3],[1,2,3]]; +} + +test bool testWhileWithNoAppend() { + return {x = 3; while (x > 0) {x -= 1; }} == []; +} + +test bool testWhileWithAppend() { + return {x = 3; while (x > 0) { append x; x -= 1; }} == [3,2,1]; +} + +test bool testDoWhileWithNoAppend() { + return { x = 3; do { x -= 1; } while (x > 0);} == []; +} + +test bool testDoWhileWithAppend() { + return {x = 3; do { append x; x -= 1; } while (x > 0);} == [3,2,1]; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Aliasing.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +module lang::rascal::tests::functionality::Aliasing + +import Type; +import Exception; + +alias INTEGER0 = int; + +test bool usingAliases1() { + INTEGER0 I = 3; + return I == 3; +} +test bool usingAliases2() { + INTEGER0 I = 3; + INTEGER0 J = I; + return J == 3; +} +test bool usingAliases3() { + list[INTEGER0] LI = [1, 2, 3]; + return LI == [1, 2, 3]; +} +test bool usingAliases4() { + set[INTEGER0] SI = {1, 2, 3}; + return SI == {1, 2, 3}; +} +test bool usingAliases5() { + map[INTEGER0, INTEGER0] MI = (1 : 10, + 2 : 20); + return MI == (1 : 10, + 2 : 20); +} +test bool usingAliases6() { + rel[INTEGER0, INTEGER0] RI = {<1, 10 >, + <2, 20 >}; + return RI == {<1, 10 >, + <2, 20 >}; +} + +alias INTEGER1 = INTEGER0; + +test bool usingIndirectAliases1() { + INTEGER1 I = 3; + return I == 3; +} +test bool usingIndirectAliases2() { + INTEGER1 I = 3; + INTEGER1 J = I; + return J == 3; +} +test bool usingIndirectAliases3() { + list[INTEGER1] LI = [1, 2, 3]; + return LI == [1, 2, 3]; +} +test bool usingIndirectAliases4() { + set[INTEGER1] SI = {1, 2, 3}; + return SI == {1, 2, 3}; +} +test bool usingIndirectAliases5() { + map[INTEGER1, INTEGER1] MI = (1 : 10, + 2 : 20); + return MI == (1 : 10, + 2 : 20); +} +test bool usingIndirectAliases6() { + rel[INTEGER1, INTEGER1] RI = {<1, 10 >, + <2, 20 >}; + return RI == {<1, 10 >, + <2, 20 >}; +} + +alias INTEGER2 = INTEGER1; +test bool usingVeryIndirectAliases1() { + INTEGER2 I = 3; + return I == 3; +} +test bool usingVeryIndirectAliases2() { + INTEGER2 I = 3; + INTEGER2 J = I; + return J == 3; +} +test bool usingVeryIndirectAliases3() { + list[INTEGER2] LI = [1, 2, 3]; + return LI == [1, 2, 3]; +} +test bool usingVeryIndirectAliases4() { + set[INTEGER2] SI = {1, 2, 3}; + return SI == {1, 2, 3}; +} +test bool usingVeryIndirectAliases5() { + map[INTEGER2, INTEGER2] MI = (1 : 10, + 2 : 20); + return MI == (1 : 10, + 2 : 20); +} +test bool usingVeryIndirectAliases6() { + rel[INTEGER2, INTEGER2] RI = {<1, 10 >, + <2, 20 >}; + return RI == {<1, 10 >, + <2, 20 >}; +} + +alias INTEGER4 = INTEGER3; +alias INTEGER3 = int; + +test bool outofOrderDeclaration() { + INTEGER4 x = 0; + return x == 0; +} + +alias ADT0 = ADT1; +data ADT1 = f(int); + +test bool aliasAndADT1() { + ADT0 x = f(0); + return x == f(0); +} + +alias StateId = int; +alias Permutation = list[int]; +alias StatedId = int; +alias Sym = int; + +test bool aliasAndADT2() { + rel[StateId from, StateId to, Sym symbol] Transitions = {}; + Transitions = {<1, 2, 3 >}; + return true; +} + +alias trans = tuple[str, str, str]; +alias block = set[trans]; +alias partition = set[block]; + +data P[&T] = p(&T a); + +test bool transitiveAliasAcrossTuples() { + block aBlock = {<"a", "b", "c" >}; + return aBlock == {<"a", "b", "c" >}; +} + +@ignoreCompiler{Representation differs} +test bool reifiedAlias1() + = #partition == type(\set(\set(\tuple([\str(), \str(), \str()]))), ()); + +@ignoreCompiler{Representation differs} +test bool reifiedAlias2() + = #P[partition] == type(adt ( + "P", + [\set(\set(\tuple([\str(), \str(), \str()])))] + ), ( + adt ( + "P", + [parameter("T", \value())] + ) : choice( + adt ( + "P", + [parameter("T", \value())] + ), + {cons( + label("p", adt ( + "P", + [parameter("T", \value())] + )), + [label("a", parameter("T", \value()))], + [], + {} + )} + ) + )); + +alias LIST[&T] = list[&T]; + +@ignoreCompiler{Representation differs} +test bool reifiedAlias3() + = #LIST[LIST[int]].symbol == \list(\list(\int())); + +@ignoreCompiler{Representation differs} +test bool reifiedAlias4() + = #LIST[LIST[LIST[int]]].symbol == \list(\list(\list(\int()))); + +alias TUPLELIST[&T] = tuple[LIST[&T], LIST[&T]]; + +@ignoreCompiler{Representation differs} +test bool reifiedAlias5() + = #TUPLELIST[int].symbol == \tuple([\list(\int()), \list(\int())]); + +alias STRING = str; + +data DATA1 = d1(STRING s); + +@ignoreCompiler{Representation differs} +test bool reifiedAlias6() + = #DATA1 == type(adt ( + "DATA1", + [] + ), ( + adt ( + "DATA1", + [] + ) : choice( + adt ( + "DATA1", + [] + ), + {cons(label("d1", adt ( + "DATA1", + [] + )), [label("s", \str())], [], {})} + ) + )); + +data DATA2 = d2(DATA1(STRING) fun); + +@ignoreCompiler{Representation differs} +test bool reifiedAlias7() + = #DATA2 == type(adt ( + "DATA2", + [] + ), ( + adt ( + "DATA2", + [] + ) : choice( + adt ( + "DATA2", + [] + ), + {cons( + label("d2", adt ( + "DATA2", + [] + )), + [label("fun", func ( + adt ( + "DATA1", + [] + ), + [\str()], + [] + ))], + [], + {} + )} + ), + adt ( + "DATA1", + [] + ) : choice( + adt ( + "DATA1", + [] + ), + {cons(label("d1", adt ( + "DATA1", + [] + )), [label("s", \str())], [], {})} + ) + )); + +@ignoreCompiler{Representation differs} +test bool reifiedAlias8() + = #DATA2 == type(adt ( + "DATA2", + [] + ), ( + adt ( + "DATA2", + [] + ) : choice( + adt ( + "DATA2", + [] + ), + {cons( + label("d2", adt ( + "DATA2", + [] + )), + [label("fun", func ( + adt ( + "DATA1", + [] + ), + [\str()], + [] + ))], + [], + {} + )} + ), + adt ( + "DATA1", + [] + ) : choice( + adt ( + "DATA1", + [] + ), + {cons(label("d1", adt ( + "DATA1", + [] + )), [label("s", \str())], [], {})} + ) + )); + +alias SET[&T] = set[&T]; + +test bool setOrRel1() + = { + rel[int, int] R = {<1, 2 >}; + SET[tuple[int, int]] ST = {<1, 2 >}; + R == ST; + }; +test bool setOrRel2() + = { + rel[int, int] R = {<1, 2 >}; + SET[tuple[int, int]] ST = R; + R == ST; + }; +test bool setOrRel3() + = { + rel[int, int] R = {<1, 2 >}; + SET[tuple[int, int]] ST = {<3, 4 >}; + R + ST == {<1, 2 >, + <3, 4 >}; + }; +test bool setOrRel4() + = { + rel[int, int] R = {<1, 2 >}; + SET[tuple[int, int]] ST = {<3, 4 >}; + ST + R == {<1, 2 >, + <3, 4 >}; + }; +test bool setOrRel5() + = { + SET[tuple[int, int]] ST = {<3, 4 >}; + <1, 2> + ST == {<1, 2 >, + <3, 4 >}; + }; +test bool setOrRel6() + = { + SET[tuple[int, int]] ST = {<3, 4 >}; + ST + <1, 2> == {<1, 2 >, + <3, 4 >}; + }; +test bool setOrRel7() + = { + SET[tuple[int, int]] ST = {<3, 4 >}; + rel[int, int] R = ST + <1, 2>; + R == {<1, 2 >, + <3, 4 >}; + }; +test bool setOrRel8() + = { + SET[tuple[int, int]] ST = {<3, 4 >}; + rel[int, int] R = <1, 2> + ST; + R == {<1, 2 >, + <3, 4 >}; + }; + +test bool listOrLrel1() + = { + lrel[int, int] LR = [<1, 2 >]; + LIST[tuple[int, int]] LT = [<1, 2 >]; + LR == LT; + }; +test bool listOrLrel2() + = { + lrel[int, int] LR = [<1, 2 >]; + LIST[tuple[int, int]] LT = LR; + LR == LT; + }; +test bool listOrLrel3() + = { + lrel[int, int] LR = [<1, 2 >]; + LIST[tuple[int, int]] LT = [<3, 4 >]; + LR + LT == [<1, 2 >, + <3, 4 >]; + }; +test bool listOrLrel4() + = { + lrel[int, int] LR = [<1, 2 >]; + LIST[tuple[int, int]] LT = [<3, 4 >]; + LT + LR == [<3, 4 >, + <1, 2 >]; + }; +test bool listOrLrel5() + = { + LIST[tuple[int, int]] LT = [<3, 4 >]; + <1, 2> + LT == [<1, 2 >, + <3, 4 >]; + }; +test bool listOrLrel6() + = { + LIST[tuple[int, int]] LT = [<3, 4 >]; + LT + <1, 2> == [<3, 4 >, + <1, 2 >]; + }; +test bool listOrLrel7() + = { + LIST[tuple[int, int]] LT = [<3, 4 >]; + lrel[int, int] LR = LT + <1, 2>; + LR == [<3, 4 >, + <1, 2 >]; + }; +test bool listOrLrel8() + = { + LIST[tuple[int, int]] LT = [<3, 4 >]; + lrel[int, int] LR = <1, 2> + LT; + LR == [<1, 2 >, + <3, 4 >]; + }; + +@synopsis{this triggered issue #1595} +test bool enumerableAlias() { + SET[int] tmp = {1, 2, 3}; + + x = for (i <- tmp) + append i; + + return {*x} == tmp; +} + +alias T[&T] = tuple[&T, &T]; + +@synopsis{this triggered #1811} +test bool assignableTupleAlias() { + T[int] x = <0, 1>; + = x; + // this would throw an exception + return a == 0 && b == 1; +} + +@synopsis{this tests if the solution for #1811 still checks the arity of the tuple} +@ignoreCompiler{The assignment is flagged as a type error} +@expected{UnexpectedType} +test bool assignableTupleAliasError() { + T[int] x = <0, 1>; + = x; + // this should throw an exception + return a == 0 && b == 1; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Annotation.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +module lang::rascal::tests::functionality::Annotation + +import Exception; + +data F + = f() + | f(int n) + | g(int n) + | deep(F f) + ; +anno + int + F + @ + pos +; +data AN = an(int n); + +anno + int + F + @ + notThere +; + +// boolannotations +test bool boolannotations1() = true || /*documentation of old behavior: */ f()[@pos = 1] == f(); +test bool boolannotations2() = f()[@pos = 1]@pos == 1; +test bool boolannotations3() = f()[@pos = 1][@pos = 2]@pos == 2; + +// since annotations are simulated by kw params this is no longer true: +test bool boolannotations4() = true || /*documentation of old behavior: */ f(5)[@pos = 1] == f(5); +test bool boolannotations5() = true || /*documentation of old behavior: */ f(5)[@pos = 1]@pos == 1; +test bool boolannotations6() = true || /*documentation of old behavior: */ f(5)[@pos = 1][@pos = 2]@pos == 2; + +// since annotations are simulated by kw params this is no longer true +test bool boolannotations7() = true || /*documentation of old behavior: */ deep(f(5)[@pos = 1]) == deep(f(5)); +test bool boolannotations8() = true || /*documentation of old behavior: */ f(5)[@pos = 1] == f(5)[@pos = 2]; + +// annotationsInSets +// since annotations are simulated by kw params this is no longer true: +//test bool annotationsInSets1() = true || /*documentation of old behavior: */ {f() [@pos=1]} == {f()}; +//test bool annotationsInSets2() = true || /*documentation of old behavior: */ {f() [@pos=1], g(2) [@pos=2]} == {f(), g(2)}; +//test bool annotationsInSets3() = true || /*documentation of old behavior: */ {f() [@pos=1], g(2)} == {f(), g(2)[@pos=2]}; +//test bool annotationsInSets4() = true || /*documentation of old behavior: */ {deep(f(5) [@pos=1])} == {deep(f(5))}; +//test bool annotationsInSets5() = true || /*documentation of old behavior: */ {f() [@pos=1]} + {g(2) [@pos=2]} == {f(), g(2)}; +//test bool annotationsInSets6() = true || /*documentation of old behavior: */ {X = {f() [@pos=1]} + {f() [@pos=2]}; {F elem} := X && (elem@pos == 2 || elem@pos == 1);}; +@ignoreCompiler{Annotations are not supported as keyword field} +test bool accessAnnoAsKeywordField() { + F example = f(); + example @ pos = 1; + return example.pos == 1; +} + +@ignoreCompiler{Annotations are not supported as keyword field} +test bool accessAnnoUpdateAsKeywordField() { + F example = f(); + example @ pos = 1; + return example[@pos = 2].pos == 2; +} + +@ignoreCompiler{Annotations are not supported as keyword field} +test bool checkAnnoExistsAsKeywordField() { + F example = f(); + example @ pos = 1; + return example.pos?; +} + +@ignoreCompiler{Annotations are not supported as keyword field} +@ignoreInterpreter{TODO: JV this still fails} +test bool KeywordFieldUpdateVisibleAsAnno() { + F example = f(); + + // keyword updates are visible to anno projection + return example[pos = 3]@\pos == 3; +} + +@ignoreCompiler{Annotations are not supported as keyword field} +test bool KeywordAssignVisibleViaAnno1() { + F example = f(); + example @ pos = 1; + example.pos = 4; + return example@pos == 4; +} + +@ignoreCompiler{Annotations are not supported as keyword field} +test bool KeywordAssignVisibleViaAnno2() { + F example = f(); + example @ pos = 1; + example.pos += 4; + return example@pos == 5; +} + +test bool unavailableAnno1() { + F example = f(); + try { + example@notThere; + return false; + } + catch NoSuchAnnotation("notThere"): + return true; +} + +test bool unavailableAnno2() { + F example = f(); + + // default behavior for simulated annotations is to throw a comparable exception + try { + node x = example; + // must hide type to avoid static error + x.notThere; + return false; + } + catch NoSuchField("notThere"): + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Assignment.rsc| +module lang::rascal::tests::functionality::Assignment + +import Exception; + +// testSimple +test bool testSimple1() { + bool b = true; + return b == true; +} +test bool testSimple2() { + b = true; + return b == true; +} + +// testInteger +test bool testInteger1() { + int N = 3; + N += 2; + return N == 5; +} +test bool testInteger2() { + int N = 3; + N -= 2; + return N == 1; +} +test bool testInteger3() { + int N = 3; + N *= 2; + return N == 6; +} +test bool testInteger4() { + int N = 6; + N /= 2; + return N == 3; +} +test bool testInteger5() { + int N = 6; + N ?= 2; + return N == 6; +} + +// testTuple +test bool testTuple1() { + int a = 1; + int b = 2; + = ; + return (a == 2) && (b == 1); +} +test bool testTuple2() { + = <1, 2>; + return (a == 1) && (b == 2); +} +test bool testTuple3() { + tuple[str name, int cnt] T = <"abc", 1>; + T.name = "def"; + return T.name == "def"; +} +test bool testTuple4() { + tuple[str name, int cnt] T = <"abc", 1>; + return T[name = "def"] == <"def", 1>; +} + +// testList +test bool testList1() { + list[int] L = []; + return L == []; +} +test bool testList2() { + list[int] L = [0, 1, 2]; + L[1] = 10; + return L == [0, 10, 2]; +} +test bool testList3() { + L = [0, 1, 2]; + L[1] = 10; + return L == [0, 10, 2]; +} +test bool testList4() { + list[list[int]] L = [[0, 1], [2, 3]]; + L[1][0] = 20; + return L == [[0, 1], [20, 3]]; +} +test bool testList5() { + L = [[0, 1], [2, 3]]; + L[1][0] = 20; + return L == [[0, 1], [20, 3]]; +} + +test bool testList6() { + list[int] L = [1, 2, 3]; + L += [4]; + return L == [1, 2, 3, 4]; +} +test bool testList7() { + list[int] L = [1, 2, 3]; + L -= [2]; + return L == [1, 3]; +} +test bool testList8() { + list[int] L = [1, 2, 3]; + L ?= [4]; + return L == [1, 2, 3]; +} + +test bool testList10() { + list[list[int]] L = [[1, 2, 3], [10, 20, 30]]; + L[0] += [4]; + return L == [[1, 2, 3, 4], [10, 20, 30]]; +} +test bool testList11() { + list[list[int]] L = [[1, 2, 3], [10, 20, 30]]; + L[0] -= [2]; + return L == [[1, 3], [10, 20, 30]]; +} +test bool testList12() { + list[list[int]] L = [[1, 2, 3], [10, 20, 30]]; + L[0] ?= [4]; + return L == [[1, 2, 3], [10, 20, 30]]; +} + +// testMap +test bool testMap1() { + map[int, int] M = (); + return M == (); +} +test bool testMap2() { + map[int, int] M = (1 : 10, + 2 : 20); + return M == (1 : 10, + 2 : 20); +} + +test bool testMap3() { + map[int, int] M = (1 : 10, + 2 : 20); + M += (3 : 30); + return M == (1 : 10, + 2 : 20, + 3 : 30); +} +test bool testMap4() { + map[int, int] M = (1 : 10, + 2 : 20); + M -= (2 : 20); + return M == (1 : 10); +} +test bool testMap5() { + map[int, int] M = (1 : 10, + 2 : 20); + M ?= (3 : 30); + return M == (1 : 10, + 2 : 20); +} + +test bool testMap7() { + map[int, list[int]] M = (0 : [1, 2, 3], + 1 : [10, 20, 30]); + M[0] += [4]; + return M == (0 : [1, 2, 3, 4], + 1 : [10, 20, 30]); +} +test bool testMap8() { + map[int, list[int]] M = (0 : [1, 2, 3], + 1 : [10, 20, 30]); + M[0] -= [2]; + return M == (0 : [1, 3], + 1 : [10, 20, 30]); +} +test bool testMap9() { + map[int, list[int]] M = (0 : [1, 2, 3], + 1 : [10, 20, 30]); + M[0] ?= [4]; + return M == (0 : [1, 2, 3], + 1 : [10, 20, 30]); +} +test bool testMap10() { + map[int, list[int]] M = (0 : [1, 2, 3], + 1 : [10, 20, 30]); + M[2] ?= [4]; + return M == (0 : [1, 2, 3], + 1 : [10, 20, 30], + 2 : [4]); +} + +test bool testMap11() { + map[int, int] M = (1 : 10, + 2 : 20); + M[2] ? 0 += 30; + return M == (1 : 10, + 2 : 50); +} +test bool testMap12() { + map[int, int] M = (1 : 10, + 2 : 20); + M[3] ? 0 += 30; + return M == (1 : 10, + 2 : 20, + 3 : 30); +} + +// testSet +test bool testSet1() { + set[int] S = {}; + return S == {}; +} +test bool testSet2() { + set[int] S = {0, 1, 2}; + return S == {0, 1, 2}; +} + +test bool testSet3() { + set[int] L = {1, 2, 3}; + L += {4}; + return L == {1, 2, 3, 4}; +} +test bool testSet4() { + set[int] L = {1, 2, 3}; + L -= {2}; + return L == {1, 3}; +} +test bool testSet5() { + set[int] L = {1, 2, 3}; + L ?= {4}; + return L == {1, 2, 3}; +} + +// testRel +test bool testRel1() { + rel[int, str] R = {}; + return R == {}; +} +test bool testRelR2() { + rel[int, str] R = {<1, "a" >, + <2, "b" >, + <3, "c" >}; + return R == {<1, "a" >, + <2, "b" >, + <3, "c" >}; +} + +test bool testRel3() { + rel[int, str] R = {<1, "a" >, + <2, "b" >, + <3, "c" >}; + R += {<4, "d" >}; + return R == {<1, "a" >, + <2, "b" >, + <3, "c" >, + <4, "d" >}; +} +test bool testRel4() { + rel[int, str] R = {<1, "a" >, + <2, "b" >, + <3, "c" >}; + R -= {<2, "b" >}; + return R == {<1, "a" >, + <3, "c" >}; +} +test bool testRel5() { + rel[int, str] R = {<1, "a" >, + <2, "b" >, + <3, "c" >}; + R ?= {<4, "d" >}; + return R == {<1, "a" >, + <2, "b" >, + <3, "c" >}; +} + +// testADT +data D + = listfield(list[int] ints) + | intfield(int i) + ; + +test bool testADT1() { + D d = listfield([1, 2]); + d.ints += [3]; + return d == listfield([1, 2, 3]); +} + +test bool testADT2() { + D d = listfield([1, 2]); + d.ints -= [2]; + return d == listfield([1]); +} + +test bool testADT31() { + D d = intfield(2); + d.i += 3; + return d == intfield(5); +} +test bool testADT32() { + D d = intfield(5); + d.i -= 3; + return d == intfield(2); +} +test bool testADT33() { + D d = intfield(5); + d.i *= 3; + return d == intfield(15); +} +test bool testADT34() { + D d = intfield(6); + d.i /= 3; + return d == intfield(2); +} + +data F + = f() + | f(int n) + | g(int n) + | deep(F f) + ; +anno + int + F + @ + pos +; + +// testAnnotations +test bool testAnnotations1() { + F X = f(); + X @ pos = 1; + return X@pos == 1; +} + +test bool testAnnotations2() { + X = f(); + X @ pos = 2; + X @ pos += 3; + return X@pos == 5; +} + +test bool testAnnotations3() { + X = f(); + X @ pos = 3; + X @ pos -= 2; + return X@pos == 1; +} + +test bool testAnnotations4() { + X = f(); + X @ pos = 2; + X @ pos *= 3; + return X@pos == 6; +} + +test bool testAnnotations5() { + X = f(); + X @ pos = 6; + X @ pos /= 3; + return X@pos == 2; +} + +test bool testAnnotations6() { + X = f(); + X @ pos = 6; + X @ pos ?= 3; + return X@pos == 6; +} + +test bool testAnnotations7() { + X = f(); + X @ pos ?= 3; + return X@pos == 3; +} + +// assigningClosureToVariableBug877 +test bool assigningClosureToVariableBug8771() { + bool() x = bool (){ return true; }; + return x() == true; +} + +data FK (int kw1 = 0) = h( int w = -1); + +test bool testKwParams1() { + FK X = h(); + assert X.w == -1; + X.w *= 2; + return X.w == -2; +} + +test bool testKwParams2() { + FK X = h(); + assert X.kw1 == 0; + X.kw1 = 2; + return X.kw1 == 2; +} + +@ignoreCompiler{Remove-after-transtion-to-compiler: Exception differs} +@expected{UninitializedVariable} +test bool testUnInitAssignment1() { + map[int, int] m = (); + m[0] += 1; +} + +@ignoreInterpreter{Exception differs} +@expected{NoSuchKey} +test bool testUnInitAssignment2() { + map[int, int] m = (); + m[0] += 1; + return false; +} + +test bool testInitAssignment3() { + map[int, int] m = (); + m[0] ? 0 += 1; + return m[0] == 1; +} + +@ignoreCompiler{Remove-after-transtion-to-compiler: Exception differs} +@expected{UninitializedVariable} +test bool testUnInitAssignment4() { + map[int, int] m = (); + m[0] -= 1; + return false; +} + +@ignoreInterpreter{Exception differs} +@expected{NoSuchKey} +test bool testUnInitAssignment5() { + map[int, int] m = (); + m[0] -= 1; + return false; +} + +@ignoreCompiler{Remove-after-transtion-to-compiler: Exception differs} +@expected{UninitializedVariable} +test bool testUnInitAssignment6() { + map[int, int] m = (); + m[0] *= 1; + return false; +} + +@ignoreInterpreter{Exception differs} +@expected{NoSuchKey} +test bool testUnInitAssignment7() { + map[int, int] m = (); + m[0] *= 1; + return false; +} + +@ignoreCompiler{Remove-after-transtion-to-compiler: Exception differs} +@expected{UninitializedVariable} +test bool testUnInitAssignment8() { + map[int, int] m = (); + m[0] /= 1; + return false; +} + +@ignoreInterpreter{Exception differs} +@expected{NoSuchKey} +test bool testUnInitAssignment9() { + map[int, int] m = (); + m[0] /= 1; + return false; +} + +@expected{IndexOutOfBounds} +test bool testUnInitAssignment10() { + list[int] m = []; + m[0] += 1; + return false; +} + +test bool testSetIfUndefinedKeyIssue1713() { + myKey = "testkey"; + map[str, set[int]] tableSet = (); + + // this would throw an exception (static) + tableSet[myKey] ? {} += {1}; + + return tableSet == ("testkey" : {1}); +} + +test bool testListIfUndefinedKeyIssue1713() { + myKey = "testkey"; + map[str, list[int]] tableSet = (); + + // this would throw an exception (static) + tableSet[myKey] ? [] += [1]; + + return tableSet == ("testkey" : [1]); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Backtracking.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::functionality::Backtracking + +// testSimple +test bool testSimple1() = int i <- [1, 4] && int k := i && k >= 4; +test bool testSimple2() = int i <- [1, 4] && int j <- [2, 1] && int k := i + j && k >= 5; + +// testRange +test bool testRange1() = int i <- [1..10] && int k := i && k >= 4; +test bool testRange2() = int i <- [1..10] && int j <- [2, 1] && int k := i + j && k >= 5; +test bool testRange3() = int i <- [1,3.. 10] && int k := i && k >= 4; +test bool testRange4() = int i <- [1,3.. 10] && int j <- [2, 1] && int k := i + j && k >= 5; + +// testList +test bool testList1() + = ([*int L1, int N, *int L2] := [1, 2, 3, 4]) + && (N == 3) + && (L1 == [1, 2]) + && (N == 3) + && (L2 == [4]); + +test bool testList2() + = ([*int L1, int N, *int L2] := [1, 2, 3, 4]) + && ((N == 3) || (N == 4)) + && (L1 == [1, 2]) + && (N == 3) + && (L2 == [4]); + +test bool testList3() + = ([*int _, int N, *int _] := [1, 2, 3, 4]) + && ([*int _, int M, *int _] := [3, 4]) + && (N > M) + && (N == 4); + +test bool testList4() = [1, [*int _, int _, *int _], 3] := [1, [1, 2, 3, 2], 3]; +test bool testList5() = [*int _, int N, *int _] := [1, 2, 3, 2] && N > 1; + +test bool testList6() = [1, [*int _, int N, *int _], 3] := [1, [10, 20], 3] && N > 10; + +// testSet +test bool testSet1() = ({*int _, int N} := {1, 2, 3, 4}) && (N == 1); +test bool testSet2() = ({*int _, int N} := {1, 2, 3, 4}) && (N == 2); +test bool testSet3() = ({*int _, int N} := {1, 2, 3, 4}) && (N == 3); +test bool testSet4() = ({*int _, int N} := {1, 2, 3, 4}) && (N == 4); + +test bool testSet5() = ({*int _, int N, *int _} := {1, 2, 3, 4}) && (N == 1); +test bool testSet6() = ({*int _, int N, *int _} := {1, 2, 3, 4}) && (N == 2); +test bool testSet7() = ({*int _, int N, *int _} := {1, 2, 3, 4}) && (N == 3); +test bool testSet8() = ({*int _, int N, *int _} := {1, 2, 3, 4}) && (N == 4); + +test bool testSet9() = {1, {*int _, int _}, 3} := {1, {1, 2, 3}, 3}; +test bool testSet10() = {*int _, int N} := {1, 2, 3, 2} && N == 1; +test bool testSet11() = {*int _, int N} := {1, 2, 3, 2} && N == 2; +test bool testSet12() = {*int _, int N} := {1, 2, 3, 2} && N == 3; + +test bool testSet13() = {1, {*int _, int _, *int _}, 3} := {1, {1, 2, 3}, 3}; +test bool testSet14() = {1, {*int _, int N, *int _}, 3} := {1, {1, 2, 3}, 3} && N == 1; +test bool testSet15() = {1, {*int _, int N, *int _}, 3} := {1, {1, 2, 3}, 3} && N == 2; +test bool testSet16() = {1, {*int _, int N, *int _}, 3} := {1, {1, 2, 3}, 3} && N == 3; + +// checking for issue #1197 +test bool testSet17() = {| {int a, int b} := {1, 2}} == {<1, 2 >, + <2, 1 >}; + +// and +test bool and1() = int i <- [0, 1] && ["a", "b"][i] == "a"; +test bool and2() = int i <- [0, 1] && ["a", "b"][i] == "b"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Call.rsc| +@license{ + Copyright (c) 2009-2014 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::functionality::Call + +import List; + +import lang::rascal::tests::functionality::CallAux; + +// voidFun +test bool voidFun() { + void f() { + } + f(); + return true; +} + +int fac(int n) { + return (n <= 0) ? 1 : (n * fac(n - 1)); +} + +test bool testFac() + = fac(0) == 1 && fac(1) == 1 && fac(2) == 2 && fac(3) == 6 && fac(4) == 24; + +int facNT(int n) { + if (n == 0) { + return 1; + } + int z = facNT(n - 1); + return z * n; +} + +// facNotTailRec +test bool facNotTailRec() + = facNT(0) == 1 && facNT(1) == 1 && facNT(2) == 2 && facNT(3) == 6 && facNT(4) == 24; + +// formalsAreLocal +test bool formalsAreLocal() { + int fac(int n) { + if (n == 0) { + return 1; + } + int z = n; + int m = fac(n - 1); + return z * m; + } + return fac(0) == 1 && fac(1) == 1 && fac(2) == 2 && fac(3) == 6 && fac(4) == 24; +} + +// higherOrder +test bool higherOrder() { + int add(int a, int b) { + return a + b; + } + ; + int doSomething(int(int a, int b) F) { + return F(1, 2); + } + ; + int sub(int a, int b) { + return a - b; + } + if (doSomething(add) != 3) + return false; + if (doSomething(sub) != -1) + return false; + return true; +} + +// closures +test bool closures1() { + int f(int(int i) g, int j) { + return g(j); + } + if (f(int (int i){ return i + 1; }, 0) != 1) + return false; + return true; +} + +test bool closures2() { + int x = 1; + int f(int(int i) g, int j) { + return g(j); + } + if (f(int (int i){ x = x * 2; + return i + x; }, 1) != 3 || (x != 2)) + return false; + return true; +} + +// closuresVariables +bool() x = bool (){ return false; } ; + +void changeX(bool() newX) { + x = newX; +} + +bool getX() = x(); + +test bool closureVariables() { + x = bool (){ return false; }; + b1 = getX() == false; + changeX(bool( ) { + return true; + }); + return b1 && getX(); +} + +// varArgs0 +test bool varArgs01() { + int add(int i...) { + return 0; + } + return add() == 0; +} + +test bool varArgs02() { + int add(int i...) { + return 0; + } + return add([]) == 0; +} + +test bool varArgs03() { + int add(int i...) { + return 0; + } + return add(0) == 0; +} + +test bool varArgs04() { + int add(int i...) { + return 0; + } + return add([0]) == 0; +} + +test bool varArgs05() { + int add(int i...) { + return 0; + } + return add(0, 1, 2) == 0; +} + +test bool varArgs06() { + int add(int i...) { + return 0; + } + return add([0, 1, 2]) == 0; +} + +// varArgs1 +test bool varArgs11() { + int add(int i...) { + return i[0]; + } + return add(0) == 0; +} + +test bool varArgs12() { + int add(int i...) { + return i[0]; + } + return add([0]) == 0; +} + +test bool varArgs13() { + int add(int i...) { + return i[0]; + } + return add(0, 1, 2) == 0; +} + +test bool varArgs14() { + int add(int i...) { + return i[0]; + } + return add([0, 1, 2]) == 0; +} + +// varArgs2 +test bool varArgs21() { + int add(int i, int j...) { + return i + j[0]; + } + return add(1, 2) == 3; +} +test bool varArgs22() { + int add(int i, int j...) { + return i + j[0]; + } + return add ( + 1, + [2] + ) == 3; +} +test bool varArgs23() { + int add(int i, int j...) { + return i + j[0]; + } + return add(1, 2, 3) == 3; +} + +test bool varArgs24() { + int add(int i, int j...) { + return i + j[0]; + } + return add ( + 1, + [2, 3] + ) == 3; +} + +// varArgs3 +test bool varArgs31() { + list[int] add(int i, int j...) = j; + return add(1) == []; +} + +test bool varArgs32() { + list[int] add(int i, int j...) = j; + return add(1, 2) == [2]; +} + +test bool varArgs33() { + list[int] add(int i, int j...) = j; + return add(1, 2, 3) == [2, 3]; +} + +// sideEffect +test bool sideEffect1() { + int called = 0; + // changed order, since forward refs are no longer allowed + void One() { + called = called + 1; + return ; + } + One(); + One(); + One(); + return called == 3; +} + +// max1 +test bool max1() { + int max(int a, int b) { + return a > b ? a : b; + } + return max(3, 4) == 4; +} + +test bool max2() { + int max(int a, int b) { + return a > b ? a : b; + } + real max(real a, real b) { + return a > b ? a : b; + } + return max(3, 4) == 4 && max(3.0, 4.0) == 4.0; +} + +/*changed: overloading is ambiguous *///test bool max3() { +// int max(int a, int b) { return a > b ? a : b; } +// real max(real a, real b) { return a > b ? a : b; } +// &T max(&T a, &T b) { return a > b ? a : b; } +// return max(3,4) == 4 && max(3.0,4.0) == 4.0 +// && max("abc","def")=="def"; +// } +// ident +test bool ident1() { + &T ident(&T x) { + return x; + } + return ident(true) == true; +} + +test bool ident2() { + &T ident(&T x) { + return x; + } + return ident(4) == 4; +} + +test bool ident3() { + &T ident(&T x) { + return x; + } + return ident(4.5) == 4.5; +} + +test bool ident4() { + &T ident(&T x) { + return x; + } + return ident("abc") == "abc"; +} + +data DATA = f(int n); + +test bool ident5() { + &T ident(&T x) { + return x; + } + return ident(f(1)) == f(1); +} + +test bool ident6() { + &T ident(&T x) { + return x; + } + return ident([1, 2, 3]) == [1, 2, 3]; +} + +test bool ident7() { + &T ident(&T x) { + return x; + } + return ident({1, 2, 3}) == {1, 2, 3}; +} + +test bool ident8() { + &T ident(&T x) { + return x; + } + return ident((1 : 10, + 2 : 20, + 3 : 30)) == (1 : 10, + 2 : 20, + 3 : 30); +} + +// map +test bool map1() { + map[&K, &V] put(map[&K, &V] m, &K k, &V v) { + m[k] = v; + return m; + } + return put((), 1, "1") == (1 : "1"); +} + +data X + = xx() + | yy() + | zz() + ; + +// dispatchTest1 +test bool dispatchTest1() { + int f1(xx()) = 1; + int f1(yy()) = 2; + int f1(zz()) = 3; + return [f1(xx()), f1(yy()), f1(zz())] == [1, 2, 3]; +} + +test bool dispatchTest2() { + int f2(xx()) = 1; + int f2(yy()) = 2; + int f2(zz()) = 3; + default int f2(int x) = x; + return [f2(xx()), f2(yy()), f2(zz()), f2(4)] == [1, 2, 3, 4]; +} + +test bool dispatchTest3() { + int f3(/[a-z]+/) = 1; + int f3(/[0-9]+/) = 2; + return f3("abc") == 1 && f3("123") == 2; +} + +test bool dispatchTest4() { + str f4(/XY/) = v; + str f4(/XY/) = v; + return f4("XabcY") == "abc" && f4("X123Y") == "123"; +} + +// Indirect calls +@ignoreInterpreter +test bool indirect1() { + bool isLF(int c) = c == 0x000A; + l = [isLF]; + elem = l[0]; + return !elem(0); +} + +// keywordTest +test bool keywordTest1() { + int incr(int x, int delta = 1) = x + delta; + return incr(3) == 4 && incr(3, delta = 2) == 5; +} + +test bool keywordTest2() { + int sum(int x = 0, int y = 0) = x + y; + return sum() == 0 && sum(x = 5, y = 7) == 5 + 7 && sum(y = 7, x = 5) == 5 + 7; +} + +test bool keywordTest3() { + list[int] varargs(int x, int y..., int z = 0, str q = "a") = y; + return varargs(1, 2, 3, 4) == [2, 3, 4]; +} +test bool keywordTest4() { + list[int] varargs(int x, int y..., int z = 0, str q = "a") = y; + return varargs(1, 2, 3, 4, q = "b") == [2, 3, 4]; +} +test bool keywordTest5() { + list[int] varargs(int x, int y..., int z = 0, str q = "a") = y; + return varargs(1, 2, 3, 4, z = 5) == [2, 3, 4]; +} +test bool keywordTest6() { + list[int] varargs(int x, int y..., int z = 0, str q = "a") = y; + return varargs(1, 2, 3, 4, q = "b", z = 5) == [2, 3, 4]; +} +test bool keywordTest7() { + int vol(int x, int y, int z, int area = x * y, int volume = area * z) = volume; + return vol(1, 2, 3) == 6; +} + +data Figure (real shrink = 1.0, str fillColor = "white", str lineColor = "black") + = emptyFigure() + | ellipse( Figure inner = emptyFigure()) + | box( Figure inner = emptyFigure()) + | volume( + int width, int height, int depth, int area = width * height, int volume = area * depth + ) + ; + +test bool keywordTest8() = emptyFigure().fillColor == "white"; + +test bool keywordTest9() = emptyFigure(shrink = 0.5).fillColor == "white"; + +test bool keywordTest10() + = emptyFigure(lineColor = "red").fillColor == "white"; + +test bool keywordTest11() + = emptyFigure(lineColor = "red", shrink = 0.5).fillColor == "white"; + +test bool keywordTest12() + = emptyFigure(fillColor = "red").fillColor == "red"; + +test bool keywordTest13() + = emptyFigure(shrink = 0.5, fillColor = "red").fillColor == "red"; + +test bool keywordTest14() + = emptyFigure(shrink = 0.5, fillColor = "red", lineColor = "black").fillColor == "red"; + +test bool keywordTest15() + = emptyFigure(lineColor = "red", shrink = 0.5).fillColor == "white"; + +test bool keywordTest16() = ellipse().fillColor == "white"; + +test bool keywordTest17() + = volume(2, 3, 4).area == 6 && volume(2, 3, 4).volume == 24; + +test bool keywordTest18() = volume(2, 3, 4, area = 0).volume == 0; + +test bool keywordTest19() = volume(2, 3, 4, volume = 0).area == 6; + +test bool keywordTest20() + = ellipse(inner = emptyFigure(fillColor = "red")).fillColor == "white"; + +test bool keywordTest21() + = ellipse(inner = emptyFigure(fillColor = "red")).inner.fillColor == "red"; + +data D = d(int x, int y = 3); + +data POINT = point(int x, int y, str color = "red"); + +// keywordMatchTest1 +test bool keywordMatchTest1() = point(_, _,color = _) := point(1, 2); +test bool keywordMatchTest2() = point(_, _,color = "red") := point(1, 2); +test bool keywordMatchTest3() + = point(_, _,color = "green") !:= point(1, 2, color = "red"); +test bool keywordMatchTest4() + = point(_, _,color = "green") := point(1, 2, color = "green"); +test bool keywordMatchTest5() = point(1, 2) := point(1, 2); +test bool keywordMatchTest6() = point(1, 2) !:= point(1, 3); +test bool keywordMatchTest7() = point(1, 2) := point(1, 2, color = "red"); +test bool keywordMatchTest8() + = point(1, 2,color = "red") := point(1, 2, color = "red"); +test bool keywordMatchTest9() = point(1, 2,color = "green") !:= point(1, 2); +test bool keywordMatchTest10() = point(1, 2,color = "green") !:= point(1, 2); + +data POINT1 = point1(int x, int y, int z = 3, list[str] colors = []); + +// keywordMatchTest2 +test bool keywordMatchTest11() + = point1(_, _,colors = ["blue"]) := point1(1, 2, colors = ["blue"]); + +test bool keywordMatchTest12() + = point1(_, _,colors = [*_, "blue", *_]) := + point1(1, 2, colors = ["red", "green", "blue"]); + +test bool keywordMatchTest13() + = point1(_, _,colors = [*_, *X, *_, *X, *_]) := + point1(1, 2, colors = ["red", "blue", "green", "blue"]); + +data Expr (int depth = 0) = id(str x); +data Expr (int width = 1) = number(num n); + +test bool genericKwParams1() = number(1).depth == 0; + +test bool genericKwParams2() = id("tommie").width == 1; + +test bool genericKwParamsBack1() = number(1).q == 4; + +data Expr (int p = 2, int q = 2 * p) = a(Expr l, Expr r, int z = p * q); + +test bool genericKwParams3() = a(id("x"), id("y")).z == 8; + +test bool genericKwParams4() = a(id("x"), id("y"), p = 3).z == 18; + +// defaults +int f01n(0) = 10; +int f01n(1) = 11; +default int f01n(int n) = 100; + +test bool f01n1() = f01n(0) == 10; +test bool f01n2() = f01n(1) == 11; +test bool f01n3() = f01n(2) == 100; + +data E + = e0() + | e1(int n) + ; + +E trans("e0", []) = e0(); +default E trans(str _, list[value] vals) = e1(0); + +test bool trans1() = trans ( + "e0", + [] + ) == e0(); +test bool trans2() = trans ( + "abc", + [] + ) == e1(0); +test bool trans3() = trans ( + "abc", + [1, 2] + ) == e1(0); + +int translateConstantCall(str name, list[value] args) = tcc(name, args); + +private int tcc("value", []) = 0; +private int tcc("value", list[int] L) = 1 + when size(L) == 1; +private int tcc("value", list[int] L) = 2 + when size(L) == 2; + +private default int tcc(str name, list[value] args) { + return -1; +} + +test bool tcc1() = translateConstantCall ( + "value", + [] + ) == 0; +test bool tcc2() = translateConstantCall ( + "value", + [1] + ) == 1; +test bool tcc3() = translateConstantCall ( + "value", + [1, 2] + ) == 2; +test bool tcc4() = translateConstantCall ( + "xxx", + [] + ) == -1; +test bool tcc5() = translateConstantCall ( + "xxx", + [1] + ) == -1; + +// backtracking tests, also uses an alternative from CallTestsAux +C c(int i) { + if (i == 0 || i mod 3 != 0) + fail c; + else + return c(i / 3); +} + +C c(int i) { + if (i == 0 || i mod 2 != 0) + fail c; + else + return c(i / 2); +} + +C c(int i) = c(i / 7) + when i mod 7 == 0, i != 0; + +test bool bt1() = c(7 * 5 * 3 * 2) == c(1); +test bool bt2() = c(5 * 3 * 2) == c(1); +test bool bt3() = c(3 * 2) == c(1); +test bool bt(int i) = (j := i mod 100) && c(xxx) := c(j) && xxx <= j; + +// when clauses +int fw(int n) = 10 + when 0 !:= n; +default int fw(int n) = -1; + +test bool negativeMatch1() = fw(0) == -1; +test bool negativeMatch2() = fw(1) == 10; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CallAux.rsc| +module lang::rascal::tests::functionality::CallAux + +data C = c(int i); + +bool() x = bool (){ return false; } ; + +public void changeX(bool() newX) { + x = newX; +} + +public bool getX() = x(); + +C c(int i) { + if (i == 0 || i mod 5 != 0) + fail c; + else + return c(i / 5); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameter4/A.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameter4::A + +import lang::rascal::tests::functionality::CommonKeywordParameter4::B; + +test bool testA1() = d2("a").m == d1(-3); +//test bool testA2() = d2("a", nn=4).m == d1(4); +//test bool testA3() = d2("a").p == d1(-4); +//test bool testA4() = d2("a",mm=5).p == d1(5); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameter4/B.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameter4::B + +extend lang::rascal::tests::functionality::CommonKeywordParameter4::C; + +data D(int nn = -3, D m = d1(nn)); + +data D(int mm = -4, D p = d2("b", nn = mm).m); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameter4/C.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameter4::C + +data D + = d1(int n) + | d2(str s) + ; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport1/DiamondBottom.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport1::DiamondBottom + +import lang::rascal::tests::functionality::CommonKeywordParameterImport1::DiamondLeft; +import lang::rascal::tests::functionality::CommonKeywordParameterImport1::DiamondRight; +import lang::rascal::tests::functionality::CommonKeywordParameterImport1::DiamondTop; + +test bool diamondTest() = x().left == x().right; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport1/DiamondLeft.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport1::DiamondLeft + +import lang::rascal::tests::functionality::CommonKeywordParameterImport1::DiamondTop; + +data X(int left = 0); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport1/DiamondRight.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport1::DiamondRight + +import lang::rascal::tests::functionality::CommonKeywordParameterImport1::DiamondTop; + +data X(int right = 0); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport1/DiamondTop.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport1::DiamondTop + +data X = x(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport2/DiamondBottom.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport2::DiamondBottom + +import lang::rascal::tests::functionality::CommonKeywordParameterImport2::DiamondLeft; +import lang::rascal::tests::functionality::CommonKeywordParameterImport2::DiamondRight; +import lang::rascal::tests::functionality::CommonKeywordParameterImport2::DiamondTop; + +test bool diamondTest() = x().left == x().right && x().leftsq == x().rightsq; + +test bool Bottom_Top_x_has_no_left() = Top_x_has_no_left(); +test bool Bottom_Top_x_has_no_leftsq() = Top_x_has_no_leftsq(); +test bool Bottom_Top_x_has_no_right() = Top_x_has_no_right(); +test bool Bottom_Top_x_has_no_rightsq() = Top_x_has_no_rightsq(); + +test bool Bottom_Left_x_has_no_right() = Left_x_has_no_right(); +test bool Bottom_Left_x_has_no_rightsq() = Left_x_has_no_rightsq(); + +test bool Bottom_Right_x_has_no_left() = Right_x_has_no_left(); +test bool Bottom_Right_x_has_no_leftsq() = Right_x_has_no_leftsq(); + +test bool Bottom_x_left1() = x() has left; +test bool Bottom_x_left2() = !x().left?; +test bool Bottom_x_left3() = x().left == 10; +test bool Bottom_x_left4() = x(left = 20).left == 20; +@ignoreCompiler{INCOMPATIBILITY: Is defined operator `?` can only be applied to subscript, keyword parameter, field access, field project or get annotation} +test bool Bottom_x_left5() = x(left = 20)?; +test bool Bottom_x_left6() = x(left = 20).left == 20; + +test bool Bottom_x_leftsq1() = x() has leftsq; +test bool Bottom_x_leftsq2() = !x().leftsq?; +test bool Bottom_x_leftsq3() = x().leftsq == 100; +test bool Bottom_x_leftsq4() = !(x(left = 20).leftsq?); +test bool Bottom_x_leftsq5() = x(left = 20).leftsq == 400; + +test bool Bottom_x_right1() = x() has right; +test bool Bottom_x_right2() = !x().right?; +test bool Bottom_x_right3() = x().right == 10; +test bool Bottom_x_right4() = x(right = 20).right == 20; +@ignoreCompiler{INCOMPATIBILITY: Is defined operator `?` can only be applied to subscript, keyword parameter, field access, field project or get annotation} +test bool Bottom_x_right5() = x(right = 20)?; +test bool Bottom_x_right6() = x(right = 20).right == 20; + +test bool Bottom_x_rightsq1() = x() has rightsq; +test bool Bottom_x_rightsq2() = !x().rightsq?; +test bool Bottom_x_rightsq3() = x().rightsq == 100; +test bool Bottom_x_rightsq4() = !(x(right = 20).rightsq?); +test bool Bottom_x_rightsq5() = x(right = 20).rightsq == 400; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport2/DiamondLeft.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport2::DiamondLeft + +import lang::rascal::tests::functionality::CommonKeywordParameterImport2::DiamondTop; + +data X(int left = 10, int leftsq = left * left); + +test bool Left_Top_x_has_no_left() = Top_x_has_no_left(); +test bool Left_Top_x_has_no_leftsq() = Top_x_has_no_leftsq(); +test bool Left_Top_x_has_no_right() = Top_x_has_no_right(); +test bool Left_Top_x_has_no_rightsq() = Top_x_has_no_rightsq(); + +test bool Left_x_has_no_right() = !(x() has right); +test bool Left_x_has_no_rightsq() = !(x() has rightsq); + +test bool Left_x_left1() = x() has left; +test bool Left_x_left2() = !x().left?; +test bool Left_x_left3() = x().left == 10; +test bool Left_x_left4() = x(left = 20).left == 20; +@ignoreCompiler{INCOMPATIBILITY: Is defined operator `?` can only be applied to subscript, keyword parameter, field access, field project or get annotation} +test bool Left_x_left5() = x(left = 20)?; +test bool Left_x_left6() = x(left = 20).left == 20; + +test bool Left_x_leftsq1() = x() has leftsq; +test bool Left_x_leftsq2() = !x().leftsq?; +test bool Left_x_leftsq3() = x().leftsq == 100; +test bool Left_x_leftsq4() = !(x(left = 20).leftsq?); +test bool Left_x_leftsq5() = x(left = 20).leftsq == 400; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport2/DiamondRight.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport2::DiamondRight + +import lang::rascal::tests::functionality::CommonKeywordParameterImport2::DiamondTop; + +data X(int right = 10, int rightsq = right * right); + +test bool Right_Top_x_has_no_left() = Top_x_has_no_left(); +test bool Right_Top_x_has_no_leftsq() = Top_x_has_no_leftsq(); +test bool Right_Top_x_has_no_right() = Top_x_has_no_right(); +test bool Right_Top_x_has_no_rightsq() = Top_x_has_no_rightsq(); + +test bool Right_x_has_no_left() = !(x() has left); +test bool Right_x_has_no_leftsq() = !(x() has leftsq); + +test bool Right_x_right1() = x() has right; +test bool Right_x_right2() = !x().right?; +test bool Right_x_right3() = x().right == 10; +test bool Right_x_right4() = x(right = 20).right == 20; + +//test bool Right_x_right5() = x(right = 20)?; +test bool Right_x_right6() = x(right = 20).right == 20; + +test bool Right_x_rightsq1() = x() has rightsq; +test bool Right_x_rightsq2() = !x().rightsq?; +test bool Right_x_rightsq3() = x().rightsq == 100; +test bool Right_x_rightsq4() = !(x(right = 20).rightsq?); +test bool Right_x_rightsq5() = x(right = 20).rightsq == 400; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport2/DiamondTop.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport2::DiamondTop + +data X = x(); + +test bool Top_x_has_no_left() = !(x() has left); +test bool Top_x_has_no_leftsq() = !(x() has leftsq); +test bool Top_x_has_no_right() = !(x() has right); +test bool Top_x_has_no_rightsq() = !(x() has rightsq); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport3/A.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport3::A + +data X (int y = 0) = x1(); + +test bool Ax1_y1() = x1() has y; +test bool Ax1_y2() = !x1().y?; +test bool Ax1_y3() = x1().y == 0; +test bool Ax1_y4() = x1(y = 10).y?; +test bool Ax1_y5() = x1(y = 10).y == 10; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport3/B.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport3::B + +data X = x2(); + +test bool B_x2_y1() = !(x2() has y || x2() has z); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport3/C.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport3::C + +import lang::rascal::tests::functionality::CommonKeywordParameterImport3::A; +import lang::rascal::tests::functionality::CommonKeywordParameterImport3::B; + +data X(str z = "abc"); + +test bool Cx1_y1() = x1() has y; +test bool Cx1_y2() = !x1().y?; +test bool Cx1_y3() = x1().y == 0; +test bool Cx1_y4() = x1(y = 10).y?; +test bool Cx1_y5() = x1(y = 10).y == 10; + +test bool Cx1_z1() = x1() has z; +test bool Cx1_z2() = !x1().z?; +test bool Cx1_z3() = x1().z == "abc"; +@ignoreCompiler{INCOMPATIBILITY: Is defined operator `?` can only be applied to subscript, keyword parameter, field access, field project or get annotation} +test bool Cx1_z4() = x1(z = "def")?; +test bool Cx1_z5() = x1(z = "def").z == "def"; + +test bool Cx2_y1() = x2() has y; +test bool Cx2_y2() = !x2().y?; +test bool Cx2_y3() = x2().y == 0; +test bool Cx2_y4() = x2(y = 10).y?; +test bool Cx2_y5() = x2(y = 10).y == 10; + +test bool Cx2_z1() = x2() has z; +test bool Cx2_z2() = !x2().z?; +test bool Cx2_z3() = x2().z == "abc"; +@ignoreCompiler{INCOMPATIBILITY: Is defined operator `?` can only be applied to subscript, keyword parameter, field access, field project or get annotation} +test bool Cx2_z4() = x2(z = "def")?; +test bool Cx2_z5() = x2(z = "def").z == "def"; + +X normalX() = x1(); +X extendedX() = x2(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport3/D.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport3::D + +import lang::rascal::tests::functionality::CommonKeywordParameterImport3::A; +import lang::rascal::tests::functionality::CommonKeywordParameterImport3::C; + +test bool Dx1_y1() = x1() has y; +test bool Dx1_y2() = !x1().y?; +test bool Dx1_y3() = x1().y == 0; +test bool Dx1_y4() = x1(y = 10).y?; +test bool Dx1_y5() = x1(y = 10).y == 10; + +test bool Dx1_z1() = x1() has z; +test bool Dx1_z2() = !x1().z?; +test bool Dx1_z3() = x1().z == "abc"; +test bool Dx1_z4() = x1(z = "def").z?; +test bool Dx1_z5() = x1(z = "def").z == "def"; + +test bool normal() = normalX().y == 0; +test bool extended() = extendedX().y == 0; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/CommonKeywordParameterImport3/Tests.rsc| +module lang::rascal::tests::functionality::CommonKeywordParameterImport3::Tests + +import lang::rascal::tests::functionality::CommonKeywordParameterImport3::D; + +// https://github.com/cwi-swat/rascal/issues/933 +test bool keywordParametersLost933() = normal() && extended(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Comprehension.rsc| +@license{ + Copyright (c) 2009-2011 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html + } +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::functionality::Comprehension + +import List; + +// set comprehension +test bool emptySetGeneratorError1() = {X| int X <- {}} == {}; + +test bool emptySetGeneratorError2() = {X| int X <- []} == {}; + +test bool setComprehension1() = {X| int X <- {1}} == {1}; +test bool setComprehension2() = {X| int X <- [1]} == {1}; + +test bool setComprehension3() = {X| X <- {1}} == {1}; +test bool setComprehension4() = {X| X <- [1]} == {1}; + +test bool setComprehension5() = {X| int X <- {1, 2}} == {1, 2}; +test bool setComprehension6() = {X| int X <- [1, 2]} == {1, 2}; + +test bool setComprehension7() = {X| X <- {1, 2}} == {1, 2}; +test bool setComprehension8() = {X| X <- [1, 2]} == {1, 2}; + +test bool setComprehension9() = {X| int X <- {1, 1, 1}} == {1}; +test bool setComprehension10() = {X| int X <- [1, 1, 1]} == {1}; + +test bool setComprehension11() = {1| int _ <- {1, 2, 3}} == {1}; +test bool setComprehension12() = {1| int _ <- [1, 2, 3]} == {1}; + +test bool setComprehension13() = {1| int _ <- {1, 2, 3}, true} == {1}; +test bool setComprehension14() = {1| int _ <- [1, 2, 3], true} == {1}; + +test bool setComprehension15() = {1| int _ <- {1, 2, 3}, false} == {}; +test bool setComprehension16() = {1| int _ <- [1, 2, 3], false} == {}; + +test bool setComprehension17() = {X| int X <- {1, 2, 3}} == {1, 2, 3}; +test bool setComprehension18() = {X| int X <- [1, 2, 3]} == {1, 2, 3}; + +test bool setComprehension19() = {X| int X <- {1, 2, 3}, true} == {1, 2, 3}; +test bool setComprehension20() = {X| int X <- [1, 2, 3], true} == {1, 2, 3}; + +test bool setComprehension21() = {X| int X <- {1, 2, 3}, false} == {}; +test bool setComprehension22() = {X| int X <- [1, 2, 3], false} == {}; + +test bool setComprehension23() = {X| int X <- {1, 2, 3}, X >= 2, X < 3} == {2}; +test bool setComprehension24() = {X| int X <- [1, 2, 3], X >= 2, X < 3} == {2}; + +test bool setComprehension25() + = {X, 10 * X| int X <- [1, 2, 3]} == {1, 2, 3, 10, 20, 30}; +test bool setComprehension26() + = {X, 10 * X, 100 * X| int X <- [1, 2, 3]} == {1, 2, 3, 10, 20, 30, 100, 200, 300}; + +test bool setComprehension27() = {{}| int _ <- {1, 2, 3}} == {{}}; +test bool setComprehension28() = {{}| int _ <- [1, 2, 3]} == {{}}; + +test bool setComprehension29() = {{}| int _ <- {1, 2, 3}, true} == {{}}; +test bool setComprehension30() = {{}| int _ <- [1, 2, 3], true} == {{}}; + +test bool setComprehension31() = {{}| int _ <- {1, 2, 3}, false} == {}; +test bool setComprehension32() = {{}| int _ <- [1, 2, 3], false} == {}; + +test bool setComprehension33() = {<1, 2, 3>| int _ <- {1, 2, 3}} == {<1, 2, 3 >}; +test bool setComprehension34() = {<1, 2, 3>| int _ <- [1, 2, 3]} == {<1, 2, 3 >}; + +test bool setComprehension35() + = {<1, 2, 3>| int _ <- {1, 2, 3}, true} == {<1, 2, 3 >}; +test bool setComprehension36() + = {<1, 2, 3>| int _ <- [1, 2, 3], true} == {<1, 2, 3 >}; + +test bool setComprehension37() + = {<1, 2, 3>| int _ <- {1, 2, 3}, true, true} == {<1, 2, 3 >}; +test bool setComprehension38() + = {<1, 2, 3>| int _ <- [1, 2, 3], true, true} == {<1, 2, 3 >}; + +test bool setComprehension39() = {<1, 2, 3>| int _ <- {1, 2, 3}, false} == {}; +test bool setComprehension40() = {<1, 2, 3>| int _ <- [1, 2, 3], false} == {}; + +test bool setComprehension41() + = {Y| list[int] Y <- [[1, 2, 3], [10, 20, 30], [100, 200, 300]]} == {[1, 2, 3], [10, 20, 30], [100, 200, 300]}; +test bool setComprehension42() = {1| 3 > 2} == {1}; +test bool setComprehension43() = {1| 2 > 3} == {}; + +// any +test bool any1() = any(int X <- {1, 2, 3}, X > 2); +test bool any2() = any(X <- {1, 2, 3}, X > 2); +test bool any3() = any(int X <- {1, 2, 3}, X > 2, X < 10); +test bool any4() = any(int X <- {1, 2, 3}, X > 2 && X < 10); +test bool any5() = any(X <- {1, 2, 3}, X > 2 && X < 10); + +test bool any6() = any(int X <- [1, 2, 3], X > 2); +test bool any7() = any(int X <- [1, 2, 3], X > 2, X < 10); +test bool any8() = any(int X <- [1, 2, 3], X > 2 && X < 10); + +test bool any9() = !(any(int X <- {1, 2, 3}, X > 10)); +test bool any10() = !(any(int X <- [1, 2, 3], X > 10)); + +test bool any11() = any(int X <- [1, 2, 3], int Y <- [10, 20, 30], X > 0, Y > 0, X < Y); +test bool any12() = !any(int X <- [1, 2, 3], int Y <- [10, 20, 30], X > 0, Y < 0, X < Y); + +test bool any13() = any(<int X, int Y> <- {<1, 10 >, + <30, 3 >, + <2, 20 >}, X > Y); +test bool any14() = any(<int X, int Y> <- [<1, 10 >, + <30, 3 >, + <2, 20 >], X > Y); + +test bool any15() = !(any(<int X, int Y> <- {<1, 10 >, + <30, 3 >, + <2, 20 >}, X > 100 * Y)); +test bool any16() = !(any(<int X, int Y> <- [<1, 10 >, + <30, 3 >, + <2, 20 >], X > 100 * Y)); + +test bool any17() = any([int X, int Y] <- {[1, 10], [30, 3], [2, 20]}, X > Y); +test bool any18() = any([int X, int Y] <- [[1, 10], [30, 3], [2, 20]], X > Y); + +test bool any19() = !(any([int X, int Y] <- {[1, 10], [30, 3], [2, 20]}, X > 100 * Y)); +test bool any20() = !(any([int X, int Y] <- [[1, 10], [30, 3], [2, 20]], X > 100 * Y)); + +test bool any21() = !(any(_ <- [])); +test bool any22() = !(any(_ <- {})); +test bool any23() = !(any(_ <- ())); + +test bool any24() = any(int X <- [10, 10, 10], 10 := X); +test bool any25() = any(int X <- [10, 20, 30], 20 := X); +test bool any26() = !any(int X <- [10, 20, 30], 25 := X); + +test bool any27() = any(int X <- [10, 10, 10], 10 := X) || 13 == 14; +test bool any28() = any(int X <- [10, 10, 10], 11 := X) || 13 == 13; +test bool any29() = any(int X <- [10, 10, 10], 10 := X) && 13 == 13; +test bool any30() = 13 == 14 || any(int X <- [10, 10, 10], 10 := X); +test bool any31() = 13 == 13 && any(int X <- [10, 10, 10], 10 := X); + +test bool any32() = all(int X <- [10, 10, 10], 10 := X) || 13 == 14; +test bool any33() = all(int X <- [10, 10, 10], 11 := X) || 13 == 13; +test bool any34() = all(int X <- [10, 10, 10], 10 := X) && 13 == 13; +test bool any35() = 13 == 14 || all(int X <- [10, 10, 10], 10 := X); +test bool any36() = 13 == 13 && all(int X <- [10, 10, 10], 10 := X); + +// all +test bool all1() = all(int X <- {1, 2, 3}, X >= 1); +test bool all2() = all(int X <- {1, 2, 3}, X >= 1, X < 10); +test bool all3() = all(int X <- {1, 2, 3}, X >= 1 && X < 10); +test bool all4() = all(int X <- [1, 2, 3], X >= 1); +test bool all5() = all(int X <- {1, 2, 3}, X >= 1, X < 10); +test bool all6() = all(int X <- {1, 2, 3}, X >= 1 && X < 10); + +test bool all7() = !(all(int X <- {1, 2, 3}, X >= 2)); +test bool all8() = !(all(int X <- {1, 2, 3}, X >= 2, X <= 2)); +test bool all9() = !(all(int X <- {1, 2, 3}, X >= 2 && X <= 2)); +test bool all10() = !(all(int X <- [1, 2, 3], X >= 2)); +test bool all11() = !(all(int X <- [1, 2, 3], X >= 2, X <= 2)); +test bool all12() = !(all(int X <- [1, 2, 3], X >= 2 && X <= 2)); + +test bool all13() = all(int X <- [1, 2, 3], int Y <- [10, 20, 30], X > 0, Y > 0, X < Y); +test bool all14() = !all(int X <- [1, 2, 3], int Y <- [10, 20, 30], X > 0, Y < 0, X < Y); + +test bool all15() = all(<int X, int Y> <- {<1, 10 >, + <3, 30 >, + <2, 20 >}, X < Y); + +test bool all16() = !(all(<int X, int Y> <- {<1, 10 >, + <30, 3 >, + <2, 20 >}, X < Y)); + +@ignoreInterpreter{Unexplained failure} +test bool all17() = all([int X, int Y] <- {[1, 10], [3, 30], [2, 20]}, X < Y); + +test bool all18() = !(all([int X, int Y] <- {[1, 10], [30, 3], [2, 20]}, X < Y)); +test bool all19() = !(all([int X, int Y] <- [[1, 10], [30, 3], [2, 20]], X < Y)); + +test bool all20() = all(int i <- [0, 1] && [0, 1][i] == i); + +// Top-level enumerators/all/any +test bool toplevelEnum1a() = 1 <- [0, 1, 2]; +test bool toplevelEnum1b() = !(1 <- [0, 1, 2]) == false; + +test bool toplevelEnum1c() { + b = 1 <- [0, 1, 2]; + return b; +} + +test bool toplevelEnum2a() = (1 <- [0, 0, 2]) == false; +test bool toplevelEnum2b() = !(1 <- [0, 0, 2]); + +test bool toplevelEnum2c() { + b = !(1 <- [0, 0, 2]); + return b; +} + +bool bidentity(bool b) = b; + +test bool toplevelEnumAsArg1() = bidentity(1 <- [0, 1, 2]); +@ignoreCompiler{To be investigated} +test bool toplevelEnumAsArg2() = !bidentity(!(1 <- [0, 1, 2])); +test bool toplevelEnumAsArg3() = !bidentity(1 <- [0, 0, 2]); +test bool toplevelEnumAsArg4() = bidentity(!(1 <- [0, 0, 2])); + +test bool assignAny1() { + b = any(int X <- {1, 2, 3}, X > 2); + return b; +} + +test bool assignAny2() { + b = any(int X <- {1, 2, 3}, X > 10); + return !b; +} + +test bool anyAsArg1() = bidentity(any(int X <- {1, 2, 3}, X > 2)); +test bool anyAsArg2() = !bidentity(any(int X <- {1, 2, 3}, X > 10)); + +test bool assignAll1() { + b = all(int X <- {1, 2, 3}, X > 0); + return b; +} + +test bool assignAll2() { + b = all(int X <- {1, 2, 3}, X > 10); + return !b; +} + +test bool allAsArg1() = bidentity(all(int X <- {1, 2, 3}, X > 0)); +test bool allAsArg2() = !bidentity(all(int X <- {1, 2, 3}, X > 10)); + +//TODO: Settle this +//@ignore{Changed semantics} +//test bool all20a() = all(_ <- []) == true; +@ignoreCompiler{Remove-after-transtion-to-compiler: Changed semantics} +test bool all20b() = all(_ <- []) == false; + +//@ignore{Changed semantics} +//test bool all21a() = all(_ <- {}) == true; +@ignoreCompiler{Remove-after-transtion-to-compiler: Changed semantics} +test bool all21b() = all(_ <- {}) == false; + +//@ignore{Changed semantics} +//test bool all22a() = all(_ <- ()) == true; +@ignoreCompiler{Remove-after-transtion-to-compiler: Changed semantics} +test bool all22b() = all(_ <- ()) == false; + +@ignoreInterpreter{Gives wrong answer} +test bool all23() = all(k <- [1, 2, 3], (k % 2 == 0 || k % 2 == 1)); + +test bool all24() = all(k <- [1, 2, 3], (k % 2 == 0 || k % 2 == 1) ? true : false); + +test bool all25() = all(k <- [10, 10, 10], 10 := k); +test bool all26() = !all(k <- [10, 20, 30], 20 := k); + +test bool all27() { + x = [1, 2]; + y = [2, 1]; + return all(e <- x, e in y); +} + +test bool all28() { + x = [1, 2]; + y = [1, 3]; + return !all(e <- x, e in y); +} + +// setComprehension +test bool setComprehension44() = {X + 1| int X <- {1, 2, 3}} == {2, 3, 4}; +test bool setComprehension45() = {X + 1| int X <- [1, 2, 3]} == {2, 3, 4}; + +test bool setComprehension46() = {X| int X <- {1, 2, 3}, X + 1 < 3} == {1}; +test bool setComprehension47() = {X| int X <- [1, 2, 3], X + 1 < 3} == {1}; + +test bool setComprehension48() = {X - 1| int X <- {1, 2, 3}} == {0, 1, 2}; +test bool setComprehension49() = {X - 1| int X <- [1, 2, 3]} == {0, 1, 2}; + +test bool setComprehension50() = {X| int X <- {1, 2, 3}, X - 1 < 3} == {1, 2, 3}; +test bool setComprehension51() = {X| int X <- [1, 2, 3], X - 1 < 3} == {1, 2, 3}; + +test bool setComprehension52() = {X * 2| int X <- {1, 2, 3}} == {2, 4, 6}; +test bool setComprehension53() = {X * 2| int X <- [1, 2, 3]} == {2, 4, 6}; + +test bool setComprehension54() = {*[X * 2]| int X <- {1, 2, 3}} == {2, 4, 6}; +test bool setComprehension55() + = {*[X * 2, X * 2 + 1]| int X <- {1, 2, 3}} == {2, 3, 4, 5, 6, 7}; + +set[int] fset(int n) { + return {n, 3 * n}; +} + +// setComprehension +test bool setComprehension56() + = {fset(n)| n <- [1..4]} == {{1, 3}, {2, 6}, {3, 9}}; +test bool setComprehension57() = {*fset(n)| n <- [1..4]} == {1, 3, 2, 6, 3, 9}; + +test bool setComprehension58() + = {{n, 3 * n}| n <- [1..4]} == {{1, 3}, {2, 6}, {3, 9}}; +test bool setComprehension59() = {*{n, 3 * n}| n <- [1..4]} == {1, 3, 2, 6, 3, 9}; +test bool setComprehension60() = {n, 3 * n| n <- [1..4]} == {1, 3, 2, 6, 3, 9}; + +test bool setComprehension61() + = {{5 * n, fset(n)}| n <- [1..4]} == {{5, {1, 3}}, {10, {2, 6}}, {15, {3, 9}}}; +test bool setComprehension62() + = {{5 * n, *fset(n)}| n <- [1..4]} == {{5, 1, 3}, {10, 2, 6}, {15, 3, 9}}; +test bool setComprehension63() + = {5 * n, fset(n)| n <- [1..4]} == {5, {1, 3}, 10, {2, 6}, 15, {3, 9}}; +test bool setComprehension64() + = {5 * n, *fset(n)| n <- [1..4]} == {5, 1, 3, 10, 2, 6, 15, 3, 9}; + +test bool setComprehension65() + = {{5 * n, fset(n)}| n <- [1..4]} == {{5, {1, 3}}, {10, {2, 6}}, {15, {3, 9}}}; +test bool setComprehension66() + = {{5 * n, *fset(n)}| n <- [1..4]} == {{5, 1, 3}, {10, 2, 6}, {15, 3, 9}}; +test bool setComprehension67() + = {5 * n, fset(n)| n <- [1..4]} == {5, {1, 3}, 10, {2, 6}, 15, {3, 9}}; +test bool setComprehension68() + = {5 * n, *fset(n)| n <- [1..4]} == {5, 1, 3, 10, 2, 6, 15, 3, 9}; + +test bool setComprehension69() + = { + m = (1 : 10, + 2 : 20); + return {m[n]| n <- {1, 2, 3}, m[n]?} == {10, 20}; + }; + +// setComprehensionNested +test bool setComprehensionNested1() + = {{X + y| int y <- [1..X + 1]}| int X <- {1, 2, 3}} == {{2}, {3, 4}, {4, 5, 6}}; +test bool setComprehensionNested2() + = {*{X + y| int y <- [1..X + 1]}| int X <- {1, 2, 3}} == {2, 3, 4, 5, 6}; +test bool setComprehensionNested3() + = {{X + y| int y <- [1..X + 1], X < 2}| int X <- [1, 2, 3]} == {{2}, {}}; +test bool setComprehensionNested4() + = {*{X + y| int y <- [1..X + 1], X < 2}| int X <- [1, 2, 3]} == {2}; +test bool setComprehensionNested5() + = {{X + y| int y <- [1..X + 1], X > 2}| int X <- [1, 2, 3]} == {{}, {4, 5, 6}}; +test bool setComprehensionNested6() + = {*{X + y| int y <- [1..X + 1], X > 2}| int X <- [1, 2, 3]} == {4, 5, 6}; + +test bool setComprehensionNestedGenerator() + = {y| <_, y> <- {| a <- [1, 2, 3]}, y > 10} == {20, 30}; + +test bool setComprehensionNestedRange1() = {i| int i <- [10..12]} == {10, 11}; + +// emptySetGeneratorError +test bool emptySetGeneratorError3() = [X | int X <- {}] == []; + +// emptyListGeneratorError1 +test bool emptyListGeneratorError1() = [X | int X <- []] == []; + +// emptyListGeneratorError2 +@ignoreCompiler{Rejected by type checker} +test bool emptyListGeneratorError2() = [X | X <- []] == []; + +// listComprehension1 +test bool listComprehension1() = [X | int X <- {1}] == [1]; +test bool listComprehension2() = [X | int X <- [1]] == [1]; +test bool listComprehension3() = [X | X <- [1]] == [1]; + +test bool listComprehension4() + = { + L = [X | int X <- {1, 2}]; + (L == [1, 2])||(L == [2, 1]); + }; +test bool listComprehension5() = [X | int X <- [1, 2]] == [1, 2]; +test bool listComprehension6() = [X | X <- [1, 2]] == [1, 2]; + +test bool listComprehension7() = [X | int X <- {1, 1, 1}] == [1]; +test bool listComprehension8() = [X | int X <- [1, 1, 1]] == [1, 1, 1]; + +test bool listComprehension9() = [1 | int _ <- {1, 2, 3}] == [1, 1, 1]; +test bool listComprehension10() = [1 | int _ <- [1, 2, 3]] == [1, 1, 1]; + +test bool listComprehension11() = [1 | int _ <- {1, 2, 3}, true] == [1, 1, 1]; +test bool listComprehension12() = [1 | int _ <- [1, 2, 3], true] == [1, 1, 1]; + +test bool listComprehension13() = [1 | int _ <- {1, 2, 3}, false] == []; +test bool listComprehension14() = [1 | int _ <- [1, 2, 3], false] == []; + +test bool listComprehension15() + = { + L = [X | int X <- {1, 2}]; + (L == [1, 2])||(L == [2, 1]); + }; +test bool listComprehension16() = [X | int X <- [1, 2, 3]] == [1, 2, 3]; + +test bool listComprehension17() + = { + L = [X | int X <- {1, 2}, true]; + (L == [1, 2])||(L == [2, 1]); + }; +test bool listComprehension18() = [X | int X <- [1, 2, 3], true] == [1, 2, 3]; + +test bool listComprehension19() = [X | int X <- {1, 2, 3}, false] == []; +test bool listComprehension20() = [X | int X <- [1, 2, 3], false] == []; + +test bool listComprehension21() = [X | int X <- {1, 2, 3}, X >= 2, X < 3] == [2]; +test bool listComprehension22() = [X | int X <- [1, 2, 3], X >= 2, X < 3] == [2]; + +test bool listComprehension23() + = [X, 10 * X | int X <- [1, 2, 3]] == [1, 10, 2, 20, 3, 30]; +test bool listComprehension24() + = [X, 10 * X, 100 * X | int X <- [1, 2, 3]] == [1, 10, 100, 2, 20, 200, 3, 30, 300]; + +// listComprehension +test bool listComprehension25() = [[] | int _ <- {1, 2, 3}] == [[], [], []]; +test bool listComprehension26() = [[] | int _ <- [1, 2, 3]] == [[], [], []]; + +test bool listComprehension27() = [[] | int _ <- {1, 2, 3}, true] == [[], [], []]; +test bool listComprehension28() = [[] | int _ <- [1, 2, 3], true] == [[], [], []]; + +test bool listComprehension29() = [[] | int _ <- {1, 2, 3}, false] == []; +test bool listComprehension30() = [[] | int _ <- [1, 2, 3], false] == []; + +test bool listComprehension31() + = [<1, 2, 3> | int _ <- {1, 2, 3}] == [<1, 2, 3 >, + <1, 2, 3 >, + <1, 2, 3 >]; +test bool listComprehension32() + = [<1, 2, 3> | int _ <- [1, 2, 3]] == [<1, 2, 3 >, + <1, 2, 3 >, + <1, 2, 3 >]; + +test bool listComprehension33() + = [<1, 2, 3> | int _ <- {1, 2, 3}, true] == [<1, 2, 3 >, + <1, 2, 3 >, + <1, 2, 3 >]; +test bool listComprehension34() + = [<1, 2, 3> | int _ <- [1, 2, 3], true] == [<1, 2, 3 >, + <1, 2, 3 >, + <1, 2, 3 >]; + +test bool listComprehension35() + = [<1, 2, 3> | int _ <- {1, 2, 3}, true, true] == [<1, 2, 3 >, + <1, 2, 3 >, + <1, 2, 3 >]; +test bool listComprehension36() + = [<1, 2, 3> | int _ <- [1, 2, 3], true, true] == [<1, 2, 3 >, + <1, 2, 3 >, + <1, 2, 3 >]; + +test bool listComprehension37() = [<1, 2, 3> | int _ <- {1, 2, 3}, false] == []; +test bool listComprehension38() = [<1, 2, 3> | int _ <- [1, 2, 3], false] == []; + +// listComprehension +test bool listComprehension39() + = [[Y] | list[int] Y <- [[1, 2, 3], [10, 20, 30], [100, 200, 300]]] == [[[1, 2, 3]], [[10, 20, 30]], [[100, 200, 300]]]; +test bool listComprehension40() + = [Y | list[int] Y <- [[1, 2, 3], [10, 20, 30], [100, 200, 300]]] == [[1, 2, 3], [10, 20, 30], [100, 200, 300]]; +test bool listComprehension41() + = [*Y | list[int] Y <- [[1, 2, 3], [10, 20, 30], [100, 200, 300]]] == [1, 2, 3, 10, 20, 30, 100, 200, 300]; + +test bool listComprehension42() = [1 | 3 > 2] == [1]; +test bool listComprehension43() = [1 | 2 > 3] == []; + +test bool listComprehension44() + = { + L = [X + 1 | int X <- {1, 2}]; + (L == [2, 3])||(L == [3, 2]); + }; +test bool listComprehension45() = [X + 1 | int X <- [1, 2, 3]] == [2, 3, 4]; + +test bool listComprehension46() = [X | int X <- {1, 2, 3}, X + 1 < 3] == [1]; +test bool listComprehension47() = [X | int X <- [1, 2, 3], X + 1 < 3] == [1]; + +test bool listComprehension48() + = { + L = [X - 1 | int X <- {1, 2}]; + (L == [0, 1])||(L == [1, 0]); + }; +test bool listComprehension49() = [X - 1 | int X <- [1, 2, 3]] == [0, 1, 2]; + +test bool listComprehension50() + = { + L = [X | int X <- {2, 3}, X - 1 < 3]; + (L == [2, 3])||(L == [3, 2]); + }; +test bool listComprehension51() = [X | int X <- [1, 2, 3], X - 1 < 3] == [1, 2, 3]; + +test bool listComprehension52() + = { + L = [X * 2 | int X <- {2, 3}]; + (L == [4, 6])||(L == [6, 4]); + }; +test bool listComprehension53() = [X * 2 | int X <- [1, 2, 3]] == [2, 4, 6]; + +test bool listComprehension54() = [*{X * 2} | int X <- [1, 2, 3]] == [2, 4, 6]; + +test bool listComprehension55() + = toSet([*{X * 2, X * 2 + 1} | int X <- [1, 2, 3]]) == {2, 3, 4, 5, 6, 7}; + +list[int] flist(int n) { + return [n, 3 * n]; +} + +// listComprehension +test bool listComprehension56() + = [flist(n) | n <- [1..4]] == [[1, 3], [2, 6], [3, 9]]; +test bool listComprehension57() = [*flist(n) | n <- [1..4]] == [1, 3, 2, 6, 3, 9]; + +test bool listComprehension58() + = [[n, 3 * n] | n <- [1..4]] == [[1, 3], [2, 6], [3, 9]]; + +test bool listComprehension59() + = [5 * n, flist(n) | n <- [1..4]] == [5, [1, 3], 10, [2, 6], 15, [3, 9]]; +test bool listComprehension60() + = [5 * n, *flist(n) | n <- [1..4]] == [5, 1, 3, 10, 2, 6, 15, 3, 9]; + +test bool listComprehension61() + = [[5 * n, flist(n)] | n <- [1..4]] == [[5, [1, 3]], [10, [2, 6]], [15, [3, 9]]]; +test bool listComprehension62() + = [[5 * n, *flist(n)] | n <- [1..4]] == [[5, 1, 3], [10, 2, 6], [15, 3, 9]]; + +test bool listComprehension63() + = { + m = (1 : 10, + 2 : 20); + return [m[n] | n <- [1, 2, 3], m[n]?] == [10, 20]; + }; + +// listComprehensionNested +test bool listComprehensionNested1() + = [[y | int y <- [0..X + 1]] | int X <- [1, 2, 3]] == [[0, 1], [0, 1, 2], [0, 1, 2, 3]]; +test bool listComprehensionNested2() + = [*[y | int y <- [0..X + 1]] | int X <- [1, 2, 3]] == [0, 1, 0, 1, 2, 0, 1, 2, 3]; +test bool listComprehensionNested3() + = [[y | int y <- [0..X + 1], X < 2] | int X <- [1, 2, 3]] == [[0, 1], [], []]; +test bool listComprehensionNested4() + = [*[y | int y <- [0..X + 1], X < 2] | int X <- [1, 2, 3]] == [0, 1]; +test bool listComprehensionNested5() + = [[y | int y <- [0..X + 1], X > 2] | int X <- [1, 2, 3]] == [[], [], [0, 1, 2, 3]]; +test bool listComprehensionNested6() + = [*[y | int y <- [0..X + 1], X > 2] | int X <- [1, 2, 3]] == [0, 1, 2, 3]; + +test bool listComprehensionNestedGenerator() + = [y | <_, y> <- [ | a <- [1, 2, 3]], y > 10] == [20, 30]; + +test bool setComprehensionNestedRange2() = [i | int i <- [10..12]] == [10..12]; + +// emptyTupleGenerator +test bool emptyTupleGeneratorError3() = {| int X <- {}, int Y <- {}} == {}; +test bool emptyTupleGeneratorError4() = {| int X <- [], int Y <- []} == {}; + +// relationComprehension +test bool relationComprehension1() + = {| int X <- {1}, int Y <- {2}} == {<1, 2 >}; +test bool relationComprehension2() + = {| int X <- [1, 1, 1], int Y <- [2, 2, 2]} == {<1, 2 >}; + +test bool relationComprehension3() = {<1, 2>| int _ <- {1, 2, 3}} == {<1, 2 >}; +test bool relationComprehension4() = {<1, 2>| int _ <- [1, 2, 3]} == {<1, 2 >}; + +test bool relationComprehension5() + = {| int X <- {1, 2, 3}, int Y <- {2, 3, 4}} == {<1, 2 >, + <1, 3 >, + <1, 4 >, + <2, 2 >, + <2, 3 >, + <2, 4 >, + <3, 2 >, + <3, 3 >, + <3, 4 >}; +test bool relationComprehension6() + = {| int X <- [1, 2, 3], int Y <- [2, 3, 4]} == {<1, 2 >, + <1, 3 >, + <1, 4 >, + <2, 2 >, + <2, 3 >, + <2, 4 >, + <3, 2 >, + <3, 3 >, + <3, 4 >}; + +test bool relationComprehension7() + = {| int X <- {1, 2, 3}, int Y <- {2, 3, 4}, true} == {<1, 2 >, + <1, 3 >, + <1, 4 >, + <2, 2 >, + <2, 3 >, + <2, 4 >, + <3, 2 >, + <3, 3 >, + <3, 4 >}; +test bool relationComprehension8() + = {| int X <- [1, 2, 3], int Y <- [2, 3, 4], true} == {<1, 2 >, + <1, 3 >, + <1, 4 >, + <2, 2 >, + <2, 3 >, + <2, 4 >, + <3, 2 >, + <3, 3 >, + <3, 4 >}; + +test bool relationComprehension9() + = {| int X <- {1, 2, 3}, int Y <- {2, 3, 4}, false} == {}; +test bool relationComprehension10() + = {| int X <- [1, 2, 3], int Y <- [2, 3, 4], false} == {}; + +test bool relationComprehension11() + = {| int X <- {1, 2, 3}, int Y <- {2, 3, 4}, X >= Y} == {<2, 2 >, + <3, 2 >, + <3, 3 >}; +test bool relationComprehension12() + = {| int X <- [1, 2, 3], int Y <- [2, 3, 4], X >= Y} == {<2, 2 >, + <3, 2 >, + <3, 3 >}; + +test bool relationComprehension13() + = {| int X <- {1, 2, 3}, <- {<1, 10 >, + <7, 70 >, + <3, 30 >, + <5, 50 >}} == {<1, 10 >, + <3, 30 >}; +test bool relationComprehension14() + = {| int X <- [1, 2, 3], <- [<1, 10 >, + <7, 70 >, + <3, 30 >, + <5, 50 >]} == {<1, 10 >, + <3, 30 >}; + +test bool relationComprehension15() + = {| int X <- {1, 2, 3}, <- {<1, "a" >, + <7, "b" >, + <3, "c" >, + <5, "d" >}} == {<1, "a" >, + <3, "c" >}; +test bool relationComprehension16() + = {| int X <- [1, 2, 3], <- [<1, "a" >, + <7, "b" >, + <3, "c" >, + <5, "d" >]} == {<1, "a" >, + <3, "c" >}; + +// emptyMapGeneratorError +test bool emptyMapGeneratorError1() = (X: 2 * X| int X <- {}) == (); + +test bool emptyMapGeneratorError2() = (X: 2 * X| int X <- []) == (); + +// mapComprehension +test bool mapComprehension1() = (X: 2 * X| int X <- {1}) == (1 : 2); +test bool mapComprehension2() = (X: 2 * X| int X <- [1]) == (1 : 2); + +test bool mapComprehension3() = (X: 2 * X| int X <- {1, 2}) == (1 : 2, + 2 : 4); +test bool mapComprehension4() = (X: 2 * X| int X <- [1, 2]) == (1 : 2, + 2 : 4); + +test bool mapComprehension5() = (X: 2 * X| int X <- [1, 2, 3]) == (1 : 2, + 2 : 4, + 3 : 6); + +test bool mapComprehension6() + = { + m = (1 : 10, + 2 : 20); + return (100 * n: m[n]| n <- [1, 2, 3], m[n]?) == (100 : 10, + 200 : 20); + }; + +// mapComprehensionNested +test bool mapComprehensionNested1() + = (X: (2 * X+y: y| int y <- [1..X + 1])| int X <- [1, 2, 3]) == (1 : (3 : 1), + 2 : (5 : 1, + 6 : 2), + 3 : (7 : 1, + 8 : 2, + 9 : 3)); +test bool mapComprehensionNested2() + = (X: (2 * X+y: y| int y <- [1..X + 1], X < 2)| int X <- [1, 2, 3]) == (1 : (3 : 1), + 2 : (), + 3 : ()); +test bool mapComprehensionNested3() + = (X: (2 * X+y: y| int y <- [1..X + 1], X > 2)| int X <- [1, 2, 3]) == (1 : (), + 2 : (), + 3 : (7 : 1, + 8 : 2, + 9 : 3)); + +test bool mapComprehensionNestedGenerator() + = (x: y| <- {| a <- [1, 2, 3]}, y > 10) == (2 : 20, + 3 : 30); + +data TREE + = i(int N) + | f(TREE a, TREE b) + | g(TREE a, TREE b) + ; + +// nodeGenerator() +test bool nodeGenerator1() = [X | /int X <- f(i(1), g(i(2), i(3)))] == [1, 2, 3]; + +test bool nodeGenerator2() + = [X | /value X <- f(i(1), g(i(2), i(3)))] == [1, i(1), 2, i(2), 3, i(3), g(i(2), i(3))]; +test bool nodeGenerator3() + = [X | value X <- f(i(1), g(i(2), i(3)))] == [i(1), g(i(2), i(3))]; + +test bool nodeGenerator4() = [N | /value N <- f(i(1), i(2))] == [1, i(1), 2, i(2)]; +test bool nodeGenerator5() = [N | value N <- f(i(1), i(2))] == [i(1), i(2)]; + +test bool nodeGenerator6() = [N | /TREE N <- f(i(1), i(2))] == [i(1), i(2)]; +test bool nodeGenerator7() = [N | TREE N <- f(i(1), i(2))] == [i(1), i(2)]; + +test bool nodeGenerator8() = [N | /int N <- f(i(1), i(2))] == [1, 2]; + +test bool nodeGenerator9() + = [N | /value N <- f(i(1), g(i(2), i(3)))] == [1, i(1), 2, i(2), 3, i(3), g(i(2), i(3))]; +test bool nodeGenerator10() + = [N | value N <- f(i(1), g(i(2), i(3)))] == [i(1), g(i(2), i(3))]; + +test bool nodeGenerator11() + = [N | /TREE N <- f(i(1), g(i(2), i(3)))] == [i(1), i(2), i(3), g(i(2), i(3))]; +test bool nodeGenerator12() + = [N | TREE N <- f(i(1), g(i(2), i(3)))] == [i(1), g(i(2), i(3))]; + +test bool nodeGenerator13() = [N | /int N <- f(i(1), g(i(2), i(3)))] == [1, 2, 3]; + +// regularGenerators +test bool regularGenerators1() + = [S | /@@/ <- ["@abc@", "@def@"]] == ["abc", "def"]; +test bool regularGenerators2() + = {S| /@@/ <- ["@abc@", "@def@"]} == {"abc", "def"}; +test bool regularGenerators3() + = {S| /@@/ <- {"@abc@", "@def@"}} == {"abc", "def"}; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/DataDeclaration.rsc| +@license{ +Copyright (c) 2009-2015 CWI +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::functionality::DataDeclaration + +import Exception; + +data Bool + = btrue() + | bfalse() + | band(Bool left, Bool right) + | bor(Bool left, Bool right) + ; +data Exp + = let(str name, Exp exp1, Exp exp2) + | var(str name) + | \int(int intVal) + ; +data Exp1[&T] + = tval(&T tval) + | tval2(&T tval1, &T tval2) + | ival(int x) + ; +alias Var2 = str; +data Exp2 + = let(Var2 var, Exp2 exp1, Exp2 exp2) + | var2(Var2 var) + | \int2(int intVal) + ; +data Maybe[&T] + = None() + | Some(&T t) + ; + +// bool +test bool bool1() { + Bool b = btrue(); + return b == Bool::btrue(); +} +test bool bool2() { + Bool b = bfalse(); + return b == Bool::bfalse(); +} +test bool bool3() { + Bool b = band(btrue(), bfalse()); + return b == Bool::band(Bool::btrue(), Bool::bfalse()); +} +test bool bool4() { + Bool b = bor(btrue(), bfalse()); + return b == bor(btrue(), bfalse()); +} +test bool bool5() = band(btrue(), bfalse()).left == btrue(); +test bool bool6() = band(btrue(), bfalse()).right == bfalse(); +test bool bool7() = bor(btrue(), bfalse()).left == btrue(); +test bool bool8() = bor(btrue(), bfalse()).right == bfalse(); +test bool bool9() { + Bool b = band(btrue(), bfalse()).left; + return b == btrue(); +} +test bool bool10() { + Bool b = band(btrue(), bfalse()).right; + return b == bfalse(); +} + +@expected{NoSuchField} +test bool bool11() { + Bool b = btrue(); + return b.left == btrue(); +} + +// boolFieldUpdate +test bool boolFieldUpdate1() { + Bool b = bor(btrue(), bfalse()); + return b[left = bfalse()] == bor(bfalse(), bfalse()); +} +test bool boolFieldUpdate2() { + Bool b = bor(btrue(), bfalse()); + return b[right = btrue()] == bor(btrue(), btrue()); +} +test bool boolFieldUpdate3() { + Bool b = bor(btrue(), bfalse()); + b.left = bfalse(); + return b == bor(bfalse(), bfalse()); +} +test bool boolFieldUpdate4() { + Bool b = bor(btrue(), bfalse()); + b.right = btrue(); + return b == bor(btrue(), btrue()); +} +test bool boolFieldUpdate5() { + Bool b = bor(bfalse(), bfalse()); + b.left = btrue(); + b.right = btrue(); + return b == bor(btrue(), btrue()); +} + +@expected{NoSuchField} +test bool boolFieldUpdate6() { + Bool b = btrue(); + return b.left == btrue(); +} + +// let +test bool let1() { + Exp e = \int(1); + return e == \int(1); +} +test bool let2() { + Exp e = var("a"); + return e == var("a"); +} +test bool let3() { + Exp e = let("a", \int(1), var("a")); + return e == let("a", \int(1), var("a")); +} + +// parameterized +test bool parameterized1() { + Exp1[int] e = tval(1); + return e == tval(1); +} +test bool parameterized2() { + Exp1[str] f = tval("abc"); + return f == tval("abc"); +} +test bool parameterized3() { + set[Exp1[value]] g = {tval(1), tval("abc")}; + return g == {tval(1), tval("abc")}; +} + +// if the parameter is not bound by a constructor, the instantiated type equals the bound of the parameter, +// any smaller types, like Exp1[int] would result in a type error +test bool parameterized4() { + a = tval(1); + return a == tval(1); +} +test bool parameterized4a() { + b = tval("abc"); + return b == tval("abc"); +} + +test bool parameterized5() { + Exp1[int] e = tval(1); + return e == tval(1); +} +test bool parameterized6() { + Exp1[str] f = tval("abc"); + return f == tval("abc"); +} +test bool parameterized7() { + set[Exp1[value]] g = {tval(1), tval("abc")}; + return g == {tval(1), tval("abc")}; +} + +// if the parameter is not bound by a constructor, the instantiated type equals the bound of the parameter, +// any smaller types, like Exp1[int] would result in a type error +test bool parameterized8() { + Exp1[value] h = ival(3); + return h == ival(3); +} + +test bool parameterized9() { + j = tval2("abc", "def"); + return j == tval2("abc", "def"); +} +test bool parameterized10() { + k = tval2("abc", "def"); + return k.tval1 == "abc"; +} +test bool parameterized11() { + l = tval2("abc", "def"); + return l.tval2 == "def"; +} +test bool parameterized12() { + m = tval2("abc", "def"); + str s2 = m.tval2; + return s2 == "def"; +} +test bool parameterized13() { + Exp1[value] h = ival(3); + return h == ival(3); +} + +test bool parameterized14() { + j = tval2("abc", "def"); + return j == tval2("abc", "def"); +} +test bool parameterized15() { + k = tval2("abc", "def"); + return k.tval1 == "abc"; +} +test bool parameterized16() { + l = tval2("abc", "def"); + return l.tval2 == "def"; +} +test bool parameterized17() { + m = tval2("abc", "def"); + str s2 = m.tval2; + return s2 == "def"; +} + +// parameterizedErrorTest +test bool parameterizedErrorTest1() { + Exp1[int] h = ival(3); + return h == ival(3); +} + +// unboundTypeVar +test bool unboundTypeVar1() { + Maybe[void] x = None(); + return x == None(); +} +test bool unboundTypeVar2() { + x = None(); + x = Some(0); + return x == Some(0); +} + +test bool unequalParameterType1() { + Exp1[value] x = tval2(3, "abc"); + return _ := x; +} + +// let +test bool let4() { + Exp2 e = \int2(1); + return e == \int2(1); +} +test bool let5() { + Exp2 e = var2("a"); + return e == var2("a"); +} +test bool let6() { + Exp2 e = let("a", \int2(1), var2("a")); + return e == let("a", \int2(1), var2("a")); +} +test bool let7() = Var2 _ := "a"; + +// escaped constructor and field names +data DwithEscapes + = \f() + | \g(int \lft, str \rht) + ; + +test bool escape1a() = \f() == \f(); +test bool escape1b() = \g(1, "a").\lft == 1; +test bool escape1c() = \g(1, "a").\rht == "a"; +test bool escape1d() = \g(1, "a") is \g; +test bool escape1e() = \g(1, "a") has \lft; + +// Overloading +int f(Exp1[str] e) = 1; +int f(Exp1[int] e) = 3; +default int f(Exp1[&T] e) = 10; + +test bool overload1() = f(tval("abc")) == 1; +test bool overload2() = f(tval(1)) == 3; +test bool overload3() = f(tval(true)) == 10; +//// has +// +//data D = d(int n) | d(str s) | d(int n, str s); +// +//test bool has1() = d(0) has n; +//test bool has2() = !(d(0) has s); +// +//test bool has3() = d("abc") has s; +//test bool has4() = !(d("abc") has n); +// +//test bool has5() = d(0, "abc") has n; +//test bool has6() = d(0, "abc") has s; +// +//// is +// +//test bool is1() = d(0) is d; +//test bool is2() = d("abc") is d; +//test bool is3() = d(0, "abc") is d; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/DataType.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::functionality::DataType + +import Exception; +import List; + +// bool +test bool testBool1() = true == true; +test bool testBool2() = !(true == false); +test bool testBool3() = true != false; + +test bool testBool4() = (!true) == false; +test bool testBool5() = (!false) == true; + +test bool testBool6() = (true && true) == true; +test bool testBool7() = (true && false) == false; +test bool testBool8() = (false && true) == false; +test bool testBool9() = (false && false) == false; + +test bool testBool10() = (true || true) == true; +test bool testBool11() = (true || false) == true; +test bool testBool12() = (false || true) == true; +test bool testBool13() = (false || false) == false; + +test bool testBool14() = (true ==> true) == true; +test bool testBool15() = (true ==> false) == false; +test bool testBool16() = (false ==> true) == true; +test bool testBool17() = (false ==> false) == true; + +test bool testBool18() = (true <==> true) == true; +test bool testBool19() = (true <==> false) == false; +test bool testBool20() = (false <==> true) == false; +test bool testBool21() = (false <==> false) == true; + +test bool testBool22() = false <= false; +test bool testBool23() = false <= true; +test bool testBool24() = !(true <= false); +test bool testBool25() = true <= true; + +test bool testBool26() = !(false < false); +test bool testBool27() = false < true; +test bool testBool28() = !(true < false); +test bool testBool29() = !(true < true); + +test bool testBool30() = false >= false; +test bool testBool31() = true >= false; +test bool testBool32() = !(false >= true); +test bool testBool33() = true >= true; + +test bool testBool34() = !(false > false); +test bool testBool35() = true > false; +test bool testBool36() = !(false > true); +test bool testBool37() = !(true > true); + +// testInt +test bool testInt1() = 1 == 1; +test bool testInt2() = 1 != 2; + +test bool testInt3() = -1 == -1; +test bool testInt4() = -1 != 1; + +test bool testInt5() = 1 + 1 == 2; +test bool testInt6() = -1 + 2 == 1; +test bool testInt7() = 1 + (-2) == -1; + +test bool testInt8() = 2 - 1 == 1; +test bool testInt9() = 2 - 3 == -1; +test bool testInt10() = 2 - -1 == 3; +test bool testInt11() = -2 - 1 == -3; + +test bool testInt12() = 2 * 3 == 6; +test bool testInt13() = -2 * 3 == -6; +test bool testInt14() = 2 * (-3) == -6; +test bool testInt15() = -2 * (-3) == 6; + +test bool testInt16() = 8 / 4 == 2; +test bool testInt17() = -8 / 4 == -2; +test bool testInt18() = 8 / -4 == -2; +test bool testInt19() = -8 / -4 == 2; + +test bool testInt20() = 7 / 2 == 3; +test bool testInt21() = -7 / 2 == -3; +test bool testInt22() = 7 / -2 == -3; +test bool testInt23() = -7 / -2 == 3; + +test bool testInt24() = 0 / 5 == 0; +test bool testInt25() = 5 / 1 == 5; + +test bool testInt26() = 5 % 2 == 1; +test bool testInt27() = -5 % 2 == -1; +test bool testInt28() = 5 % -2 == 1; + +test bool testInt29() = -2 <= -1; +test bool testInt30() = -2 <= 1; +test bool testInt31() = 1 <= 2; +test bool testInt32() = 2 <= 2; +test bool testInt33() = !(2 <= 1); + +test bool testInt34() = -2 < -1; +test bool testInt35() = -2 < 1; +test bool testInt36() = 1 < 2; +test bool testInt37() = !(2 < 2); + +test bool testInt38() = -1 >= -2; +test bool testInt39() = 1 >= -1; +test bool testInt40() = 2 >= 1; +test bool testInt41() = 2 >= 2; +test bool testInt42() = !(1 >= 2); + +test bool testInt43() = -1 > -2; +test bool testInt44() = 1 > -1; +test bool testInt45() = 2 > 1; +test bool testInt46() = !(2 > 2); +test bool testInt47() = !(1 > 2); + +test bool testInt48() = (3 > 2 ? 3 : 2) == 3; + +// valueEquals +test bool valueEquals() { + value x = 1.0; + value y = 2; + return x != y; +} + +// testReal +test bool testReal1() = 1.0 == 1.0; +test bool testReal2() = 1.0 != 2.0; + +test bool testReal3() = -1.0 == -1.0; +test bool testReal4() = -1.0 != 1.0; + +test bool testReal5() = 1.0 == 1; +test bool testReal6() = 1.00 == 1.0; +test bool testReal7() = 1 == 1.0; + +test bool testReal8() { + value x = 1.0; + value y = 1; + return x == y; +} +test bool testReal9() { + value x = 1.0; + value y = 2; + return x != y; +} + +test bool testReal10() = 1.0 + 1.0 == 2.0; +test bool testReal11() = -1.0 + 2.0 == 1.0; +test bool testReal12() = 1.0 + (-2.0) == -1.0; + +test bool testReal13() = 1.0 + 1 == 2.0; +test bool testReal14() = -1 + 2.0 == 1.0; +test bool testReal15() = 1.0 + (-2) == -1.0; + +test bool testReal16() = 2.0 - 1.0 == 1.0; +test bool testReal17() = 2.0 - 3.0 == -1.0; +test bool testReal18() = 2.0 - -1.0 == 3.0; +test bool testReal19() = -2.0 - 1.0 == -3.0; + +test bool testReal20() = 2.0 - 1 == 1.0; +test bool testReal21() = 2 - 3.0 == -1.0; +test bool testReal22() = 2.0 - -1 == 3.0; +test bool testReal23() = -2 - 1.0 == -3.0; + +test bool testReal24() = 2.0 * 3.0 == 6.00; +test bool testReal25() = -2.0 * 3.0 == -6.00; +test bool testReal26() = 2.0 * (-3.0) == -6.00; +test bool testReal27() = -2.0 * (-3.0) == 6.00; + +test bool testReal28() = 2.0 * 3 == 6.0; +test bool testReal29() = -2 * 3.0 == -6.0; +test bool testReal30() = 2.0 * (-3) == -6.0; +test bool testReal31() = -2 * (-3.0) == 6.0; + +test bool testReal32() = 8.0 / 4.0 == 2e0; +test bool testReal33() = -8.0 / 4.0 == -2e0; +test bool testReal34() = 8.0 / -4.0 == -2e0; +test bool testReal35() = -8.0 / -4.0 == 2e0; + +// TODO, I don't get it, why does the previous have 1 digit precision and this +// one two digits +test bool testReal36() = 7.0 / 2.0 == 3.5; +test bool testReal37() = -7.0 / 2.0 == -3.5; +test bool testReal38() = 7.0 / -2.0 == -3.5; +test bool testReal39() = -7.0 / -2.0 == 3.5; + +test bool testReal40() = 0.0 / 5.0 == 0.0; +test bool testReal41() = 5.0 / 1.0 == 5.0; + +test bool testReal42() = 7 / 2.0 == 3.5; +test bool testReal43() = -7.0 / 2 == -3.5; +test bool testReal44() = 7 / -2.0 == -3.5; +test bool testReal45() = -7.0 / -2 == 3.5; + +test bool testReal46() = -2.0 <= -1.0; +test bool testReal47() = -2.0 <= 1.0; +test bool testReal48() = 1.0 <= 2.0; +test bool testReal49() = 2.0 <= 2.0; +test bool testReal50() = !(2.0 <= 1.0); + +test bool testReal51() = -2 <= -1.0; +test bool testReal52() = -2.0 <= 1; +test bool testReal53() = 1 <= 2.0; +test bool testReal54() = 2.0 <= 2; +test bool testReal55() = !(2 <= 1.0); + +test bool testReal56() = -2.0 < -1.0; +test bool testReal57() = -2.0 < 1.0; +test bool testReal58() = 1.0 < 2.0; +test bool testReal59() = !(2.0 < 2.0); + +test bool testReal60() = -2 < -1.0; +test bool testReal61() = -2.0 < 1; +test bool testReal62() = 1 < 2.0; +test bool testReal63() = !(2.0 < 2); + +test bool testReal64() = -1.0 >= -2.0; +test bool testReal65() = 1.0 >= -1.0; +test bool testReal66() = 2.0 >= 1.0; +test bool testReal67() = 2.0 >= 2.0; +test bool testReal68() = !(1.0 >= 2.0); + +test bool testReal69() = -1 >= -2.0; +test bool testReal70() = 1.0 >= -1; +test bool testReal71() = 2 >= 1.0; +test bool testReal72() = 2.0 >= 2; +test bool testReal73() = !(1 >= 2.0); + +test bool testReal74() = -1.0 > -2.0; +test bool testReal75() = 1.0 > -1.0; +test bool testReal76() = 2.0 > 1.0; +test bool testReal77() = !(2.0 > 2.0); +test bool testReal78() = !(1.0 > 2.0); + +test bool testReal79() = -1 > -2.0; +test bool testReal80() = 1.0 > -1; +test bool testReal81() = 2 > 1.0; +test bool testReal82() = !(2.0 > 2); +test bool testReal83() = !(1 > 2.0); + +test bool testReal84() = ((3.5 > 2.5) ? 3.5 : 2.5) == 3.5; + +test bool testReal85() = ((3.5 > 2) ? 3.5 : 2) == 3.5; +test bool testReal86() = ((3.5 > 4) ? 3.5 : 2) == 2; + +// testNumber +test bool testNumber1() { + num n = 1; + return n == 1; +} +test bool testNumber2() { + num n = 1; + return 1 == n; +} + +test bool testNumber3() { + num n = 1; + return n != 2; +} +test bool testNumber4() { + num n = 1; + return 2 != n; +} + +test bool testNumber5() { + num n = 1; + return n + 1 == 2; +} +test bool testNumber6() { + num n = 1; + return 1 + n == 2; +} + +test bool testNumber7() { + num n = 2; + return n - 1 == 1; +} +test bool testNumber8() { + num n = 2; + return 1 - n == -1; +} + +test bool testNumber9() { + num n = 2; + return n * 3 == 6; +} +test bool testNumber10() { + num n = 2; + return 3 * n == 6; +} + +test bool testNumber11() { + num n = 8; + return n / 4 == 2; +} +test bool testNumber12() { + num n = 4; + return 8 / n == 2; +} + +test bool testNumber13() { + num n = 1; + return n <= 2; +} +test bool testNumber14() { + num n = 1; + return 0 <= n; +} + +test bool testNumber15() { + num n = 1; + return n < 2; +} +test bool testNumber16() { + num n = 1; + return 0 < n; +} + +test bool testNumber17() { + num n = 2; + return n >= 1; +} +test bool testNumber18() { + num n = 1; + return 2 >= n; +} + +test bool testNumber19() { + num n = 2; + return n > 1; +} +test bool testNumber20() { + num n = 1; + return 2 > n; +} + +test bool testNumber21() { + num n = 1; + return 2 > n; +} + +test bool testNumber22() { + num n35 = 3.5; + num n2 = 2; + return ((n35 > n2) ? 3.5 : 2) == 3.5; +} + +// testString +test bool testString1() = "" == ""; +test bool testString2() = "abc" != ""; +test bool testString3() = "abc" == "abc"; +test bool testString4() = "abc" != "def"; + +test bool testString5() = "abc" + "" == "abc"; +test bool testString6() = "abc" + "def" == "abcdef"; + +test bool testString7() = "" <= ""; +test bool testString8() = "" <= "abc"; +test bool testString9() = "abc" <= "abc"; +test bool testString10() = "abc" <= "def"; + +test bool testString11() = !("" < ""); +test bool testString12() = "" < "abc"; +test bool testString13() = !("abc" < "abc"); +test bool testString14() = "abc" < "def"; + +test bool testString15() = "" >= ""; +test bool testString16() = "abc" >= ""; +test bool testString17() = "abc" >= "abc"; +test bool testString18() = "def" >= "abc"; + +test bool testString19() = !("" > ""); +test bool testString20() = "abc" > ""; +test bool testString21() = !("abc" > "abc"); +test bool testString22() = "def" > "abc"; + +// stringEscapes +test bool testStringEscapes1a() = "\\b" == "\\b"; +test bool testStringEscapes1b() = "\\" + "b" == "\\b"; + +test bool testStringEscapes2a() = "\\t" == "\\t"; +test bool testStringEscapes2b() = "\\" + "t" == "\\t"; + +test bool testStringEscapes3a() = "\\n" == "\\n"; +test bool testStringEscapes3b() = "\\" + "n" == "\\n"; + +test bool testStringEscapes4a() = "\\f" == "\\f"; +test bool testStringEscapes4b() = "\\" + "f" == "\\f"; + +test bool testStringEscapes5a() = "\\r" == "\\r"; +test bool testStringEscapes5b() = "\\" + "r" == "\\r"; + +test bool testStringEscapes6a() = "\"\"" == "\"\""; +test bool testStringEscapes6b() = "\"" + "\"" == "\"\""; + +test bool testStringEscapes7a() = "\\\'" == "\\\'"; +test bool testStringEscapes7b() = "\\" + "\'" == "\\\'"; + +test bool testStringEscapes8a() = "\\\\" == "\\\\"; +test bool testStringEscapes8b() = "\\" + "\\" == "\\\\"; + +test bool testStringEscapes9a() = "\"\<" == "\"\<"; +test bool testStringEscapes9b() = "\"" + "\<" == "\"\<"; + +test bool testStringEscapes10a() = "\"\>" == "\"\>"; +test bool testStringEscapes10b() = "\"" + "\>" == "\"\>"; + +test bool testStringEscapes11() = "\a20" == " "; +test bool testStringEscapes12() = "\U01F35D" == "🍝"; +test bool testStringEscapes13() = "\u2713" == "✓"; + +// stringInterpolation +test bool testStringInterpolation1() { + str a = "abc"; + return "12" == "1abc2"; +} +test bool testStringInterpolation2() { + int a = 789; + return "12" == "17892"; +} + +test bool testStringInterpolation3() { + str a = "a\\bc"; + return "12" == "1a\\bc2"; +} +test bool testStringInterpolation4() { + str a = "a\\tc"; + return "12" == "1a\\tc2"; +} +test bool testStringInterpolation5() { + str a = "a\\nc"; + return "12" == "1a\\nc2"; +} +test bool testStringInterpolation6() { + str a = "a\\fc"; + return "12" == "1a\\fc2"; +} +test bool testStringInterpolation7() { + str a = "a\\rc"; + return "12" == "1a\\rc2"; +} + +test bool testStringInterpolation8() { + str a = "a\\\"c"; + return "12" == "1a\\\"c2"; +} +test bool testStringInterpolation9() { + str a = "a\\\'c"; + return "12" == "1a\\\'c2"; +} +test bool testStringInterpolation10() { + str a = "a\\\\c"; + return "12" == "1a\\\\c2"; +} + +test bool testStringInterpolation11() { + str a = "a\2" == "1a\2" == "1a\>c2"; +} + +test bool testStringInterpolation13() { + str a = "abc"; + return "1\\2" == "1\\abc2"; +} +test bool testStringInterpolation14() { + str a = "abc"; + return "1\\2" == "1abc\\2"; +} +test bool testStringInterpolation15() { + str a = "abc"; + return "1\<2" == "1\\>2" == "1abc\>2"; +} + +test bool testStringInterpolation17() { + str a = "a\\bc"; + return "1\\2" == "1\\a\\bc2"; +} +test bool testStringInterpolation18() { + str a = "a\\bc"; + return "1\\2" == "1a\\bc\\2"; +} +test bool testStringInterpolation19() { + str a = "a\\bc"; + return "1\<2" == "1\\>2" == "1a\\bc\>2"; +} + +loc Loc = |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 5>) ; +loc Loc2 = |file:///home/paulk/pico2.trm|(0, 1, <2, 3>, <4, 5>) ; + +// testLocation +test bool testLocation1() { + Loc; + return true; +} +test bool testLocation2() = Loc == Loc; +test bool testLocation3() = Loc != Loc2; + +test bool testLocationFieldUse1() + = Loc.uri == "file:///home/paulk/pico.trm"; +test bool testLocationFieldUse2() = Loc.offset == 0; +test bool testLocationFieldUse3() = Loc.length == 1; +test bool testLocationFieldUse5() = Loc.begin.line == 2; +test bool testLocationFieldUse6() = Loc.begin.column == 3; +test bool testLocationFieldUse7() = Loc.end.line == 4; +test bool testLocationFieldUse8() = Loc.end.column == 5; +test bool testLocationFieldUse9() = Loc.path == "/home/paulk/pico.trm"; + +test bool testLocationFieldUpdate1() { + loc l = Loc; + l.uri = "file:///home/paulk/pico2.trm"; + return l.uri == "file:///home/paulk/pico2.trm"; +} +test bool testLocationFieldUpdate2() { + loc l = Loc; + l.offset = 10; + return l.offset == 10; +} +test bool testLocationFieldUpdate3() { + loc l = Loc; + l.length = 11; + return l.length == 11; +} +test bool testLocationFieldUpdate4() { + loc l = Loc; + l.end.line = 14; + return l.end.line == 14; +} +test bool testLocationFieldUpdate5() { + loc l = Loc; + l.begin.line = 1; + return l.begin.line == 1; +} +test bool testLocationFieldUpdate6() { + loc l = Loc; + l.begin.column = 13; + return l.begin.column == 13; +} +test bool testLocationFieldUpdate7() { + loc l = Loc; + l.end.column = 15; + return l.end.column == 15; +} + +test bool testLocationFieldUpdate8() { + loc l = Loc[uri = "file:///home/paulk/pico.trm"]; + return l == |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 5>); +} +test bool testLocationFieldUpdate9() { + loc l = Loc[offset = 10]; + return l == |file:///home/paulk/pico.trm|(10, 1, <2, 3>, <4, 5>); +} +test bool testLocationFieldUpdate10() { + loc l = Loc[length = 11]; + return l == |file:///home/paulk/pico.trm|(0, 11, <2, 3>, <4, 5>); +} +test bool testLocationFieldUpdate12() { + loc l = Loc[begin = <1, 4>]; + return l == |file:///home/paulk/pico.trm|(0, 1, <1, 4>, <4, 5>); +} +test bool testLocationFieldUpdate13() { + loc l = Loc[end = <14, 38>]; + return l == |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <14, 38>); +} + +test bool testLocation12() + = |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 5>) == |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 5>); +test bool testLocation13() + = !(|file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 5>) == |file:///home/paulk/pico.trm|(0, 2, <2, 3>, <4, 5>)); +test bool testLocation14() + = !(|file:///home/paulk/pico1.trm|(0, 1, <2, 3>, <4, 5>) == |file:///home/paulk/pico2.trm|(0, 1, <2, 3>, <4, 5>)); + +test bool testLocation15() + = |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 5>) + != |file:///home/paulk/pico2.trm|(0, 1, <2, 3>, <4, 5>); +test bool testLocation16() + = |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 5>) + != |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 7>); +test bool testLocation17() + = !(|file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 5>) + != + |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 5>)); +test bool testLocation18() + = |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>) + != |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 7>); + +test bool testLocation19() + = !(|file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>) < |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>)); +test bool testLocation20() + = |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>) < |file:///home/paulk/pico.trm|(2, 2, <2, 3>, <4, 5>); +test bool testLocation21() + = !(|file:///home/paulk/pico.trm|(1, 1, <2, 3>, <4, 5>) < |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>)); +test bool testLocation22() + = !(|file:///home/paulk/pico.trm|(1, 2, <2, 3>, <4, 5>) < |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>)); + +test bool testLocation23() + = |file:///home/paulk/pico1.trm|(2, 1, <2, 3>, <4, 5>) <= |file:///home/paulk/pico2.trm|(2, 1, <2, 3>, <4, 5>); +test bool testLocation24() + = !(|file:///home/paulk/pico2.trm|(2, 1, <2, 3>, <4, 5>) <= |file:///home/paulk/pico1.trm|(2, 1, <2, 3>, <4, 5>)); +test bool testLocation25() + = |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>) <= |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>); +test bool testLocation26() + = |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>) <= |file:///home/paulk/pico.trm|(2, 2, <2, 3>, <4, 5>); +test bool testLocation27() + = !(|file:///home/paulk/pico.trm|(2, 2, <2, 3>, <4, 5>) <= |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>)); + +test bool testLocation28() + = |file:///home/paulk/pico2.trm|(2, 1, <2, 3>, <4, 5>) > |file:///home/paulk/pico1.trm|(2, 1, <2, 3>, <4, 5>); +test bool testLocation29() + = !(|file:///home/paulk/pico1.trm|(2, 1, <2, 3>, <4, 5>) > |file:///home/paulk/pico2.trm|(2, 1, <2, 3>, <4, 5>)); +test bool testLocation30() + = !(|file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>) > |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>)); +test bool testLocation31() + = |file:///home/paulk/pico.trm|(2, 2, <2, 3>, <4, 5>) > |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>); +test bool testLocation32() + = !(|file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>) > |file:///home/paulk/pico.trm|(2, 2, <2, 3>, <4, 5>)); + +test bool testLocation33() + = |file:///home/paulk/pico2.trm|(2, 1, <2, 3>, <4, 5>) >= |file:///home/paulk/pico1.trm|(2, 1, <2, 3>, <4, 5>); +test bool testLocation34() + = !(|file:///home/paulk/pico1.trm|(2, 1, <2, 3>, <4, 5>) >= |file:///home/paulk/pico2.trm|(2, 1, <2, 3>, <4, 5>)); +test bool testLocation35() + = |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>) >= |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>); +test bool testLocation36() + = |file:///home/paulk/pico.trm|(2, 2, <2, 3>, <4, 5>) >= |file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>); +test bool testLocation37() + = !(|file:///home/paulk/pico.trm|(2, 1, <2, 3>, <4, 5>) >= |file:///home/paulk/pico.trm|(2, 2, <2, 3>, <4, 5>)); + +test bool testLocation38() + = |file:///xxx|(45, 1, <1, 45>, <1, 46>) <= |file:///xxx|(40, 6, <1, 40>, <1, 46>); +test bool testLocation39() + = |file:///xxx|(45, 1, <1, 45>, <1, 46>) <= |file:///xxx|(40, 7, <1, 40>, <1, 47>); + +test bool testLocation40() + = |project://rascal/xxx.rsc|(5667, 18, <160, 16>, <160, 34>) < |project://rascal/xxx.rsc|(5661, 24, <160, 10>, <160, 34>); + +test bool testLocation41() + = |project://rascal/xxx.rsc|(5667, 18, <160, 16>, <160, 34>) <= |project://rascal/xxx.rsc|(5661, 24, <160, 10>, <160, 34>); + +test bool testLocation42() + = |project://rascal/xxx.rsc|(5661, 24, <160, 10>, <160, 34>) > |project://rascal/xxx.rsc|(5667, 18, <160, 16>, <160, 34>); + +test bool testLocation43() + = |project://rascal/xxx.rsc|(5661, 24, <160, 10>, <160, 34>) >= |project://rascal/xxx.rsc|(5667, 18, <160, 16>, <160, 34>); + +// testList +test bool testList1() = [] == []; +test bool testList2() = [] != [1]; +test bool testList3() = [1] == [1]; +test bool testList4() = [1] != [2]; +test bool testList5() = [1, 2] == [1, 2]; +test bool testList6() = [1, 2] != [2, 1]; + +test bool testList7() = [] + [] == []; +test bool testList8() = [1, 2, 3] + [] == [1, 2, 3]; +test bool testList9() = [] + [1, 2, 3] == [1, 2, 3]; +test bool testList10() = [1, 2] + [3, 4, 5] == [1, 2, 3, 4, 5]; + +test bool testList11() = ([1, 2] + [3, 4]) + [5] == [1, 2, 3, 4, 5]; +test bool testList12() = [1, 2] + ([3, 4] + [5]) == [1, 2, 3, 4, 5]; +test bool testList13() = [1, 2] + [3, 4] + [5] == [1, 2, 3, 4, 5]; + +test bool testList14() = [1, 2] + 3 == [1, 2, 3]; +test bool testList15() = 1 + [2, 3] == [1, 2, 3]; + +test bool testList16() = [1, 2, 1, 2, 3, 4, 3, 4, 5] - 1 == [2, 1, 2, 3, 4, 3, 4, 5]; +test bool testList17() = [1, 2, 1, 2, 3, 4, 3, 4, 5] - 2 == [1, 1, 2, 3, 4, 3, 4, 5]; +test bool testList18() = [1, 2, 1, 2, 3, 4, 3, 4, 5] - 5 == [1, 2, 1, 2, 3, 4, 3, 4]; + +test bool testList19() = [1, 2, 1, 2, 3, 4, 3, 4, 5] - [1] == [2, 1, 2, 3, 4, 3, 4, 5]; +test bool testList20() = [1, 2, 1, 2, 3, 4, 3, 4, 5] - [2] == [1, 1, 2, 3, 4, 3, 4, 5]; +test bool testList21() = [1, 2, 1, 2, 3, 4, 3, 4, 5] - [5] == [1, 2, 1, 2, 3, 4, 3, 4]; + +test bool testList22() = [1, 2, 1, 2, 3, 4, 3, 4, 5] - [1, 1] == [2, 2, 3, 4, 3, 4, 5]; +test bool testList23() = [1, 2, 1, 2, 3, 4, 3, 4, 5] - [1, 1, 1] == [2, 2, 3, 4, 3, 4, 5]; + +test bool testList24() = [1, 2, 1, 2, 3, 4, 3, 4, 5] - [1, 2] == [1, 2, 3, 4, 3, 4, 5]; +test bool testList25() = [1, 2, 1, 2, 3, 4, 3, 4, 5] - [2, 3] == [1, 1, 2, 4, 3, 4, 5]; + +test bool testList26() = [] & [1, 2, 4] == []; +test bool testList27() = [1, 2, 3] & [] == []; +test bool testList28() = [1, 2, 3, 4, 5, 4, 3, 2, 1] & [1, 2, 4] == [1, 2, 4, 4, 2, 1]; + +test bool testList29() = [] <= []; +test bool testList30() = [] <= [1]; + +/*TODO:REMOVE?*/// These commented out tests assume that <= etc. are ("half") ordering operations +// Currently they are strictly subset implementations. +// test bool testList() = [2, 1, 0] <= [2, 3]; +// test bool testList() = [2, 1] <= [2, 3, 0]; +test bool testList31() = [2, 1] <= [2, 1]; +test bool testList32() = [2, 1] <= [2, 1, 0]; + +test bool testList33() = [] < [1]; + +// test bool testList() = [2, 1, 0] < [2, 3]; +// test bool testList() = [2, 1] < [2, 3, 0]; +test bool testList34() = [2, 1] < [2, 1, 0]; + +test bool testList35() = [] >= []; + +// test bool testList() = [1] >= []; +// test bool testList() = [2, 3] >= [2, 1, 0]; +// test bool testList() = [2, 3, 0] >= [2, 1]; +test bool testList36() = [2, 1] >= [2, 1]; +test bool testList37() = [2, 1, 0] >= [2, 1]; + +test bool testList38() = [1] > []; + +// test bool testList() = [2, 3] > [2, 1, 0]; +// test bool testList() = [2, 3, 0] > [2, 1]; +test bool testList39() = [2, 1, 0] > [2, 1]; + +test bool testList40() = [] * [] == []; +test bool testList41() = [1] * [9] == [<1, 9 >]; +test bool testList42() = [1, 2] * [9] == [<1, 9 >, + <2, 9 >]; +test bool testList43() = [1, 2, 3] * [9] == [<1, 9 >, + <2, 9 >, + <3, 9 >]; +test bool testList44() + = [1, 2, 3] * [9, 10] == [<1, 9 >, + <1, 10 >, + <2, 9 >, + <2, 10 >, + <3, 9 >, + <3, 10 >]; + +test bool testList45() = 2 in [1, 2, 3]; +test bool testList46() = 3 notin [2, 4, 6]; + +test bool testList47() = (2 > 3 ? [1, 2] : [1, 2, 3]) == [1, 2, 3]; + +@ignoreInterpreter{Not implemented} +test bool testList48() = 1 >> [2, 3] == [1, 2, 3]; + +@ignoreInterpreter{Not implemented} +test bool testList49() = [2, 3] << 4 == [2, 3, 4]; + +@expected{IndexOutOfBounds} +test bool SubscriptError11() { + [1, 2][5]; + return false; +} + +// listSplicing +@ignoreCompiler{INCOMPATIBILITY: Splicing no longer allowed on arbitrary types} +test bool testListSplicing1() = [1, 2, 3] == [1, 2, 3]; +@ignoreCompiler{INCOMPATIBILITY: Splicing no longer allowed on arbitrary types} +test bool testListSplicing2() = [*1, 2, 3] == [1, 2, 3]; +@ignoreCompiler{INCOMPATIBILITY: Splicing no longer allowed on arbitrary types} +test bool testListSplicing3() = [1, *2, 3] == [1, 2, 3]; +@ignoreCompiler{INCOMPATIBILITY: Splicing no longer allowed on arbitrary types} +test bool testListSplicing4() = [1, 2, *3] == [1, 2, 3]; +@ignoreCompiler{INCOMPATIBILITY: Splicing no longer allowed on arbitrary types} +test bool testListSplicing5() = [*1, *2, 3] == [1, 2, 3]; + +test bool testListSplicing6() { + list[int] L1 = [1, 2]; + return [L1] == [[1, 2]]; +} +test bool testListSplicing7() { + list[int] L1 = [1, 2]; + return [*L1] == [1, 2]; +} + +test bool testListSplicing8() { + list[int] L1 = [1, 2]; + return [L1, 3] == [[1, 2], 3]; +} +test bool testListSplicing9() { + list[int] L1 = [1, 2]; + return [*L1, 3] == [1, 2, 3]; +} + +test bool testListSplicing10() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [L1, L2] == [[1, 2], [3, 4]]; +} +test bool testListSplicing11() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [*L1, L2] == [1, 2, [3, 4]]; +} +test bool testListSplicing12() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [L1, *L2] == [[1, 2], 3, 4]; +} +test bool testListSplicing13() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [*L1, *L2] == [1, 2, 3, 4]; +} + +test bool testListSplicing14() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [L1, L2, 5] == [[1, 2], [3, 4], 5]; +} +test bool testListSplicing15() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [*L1, L2, 5] == [1, 2, [3, 4], 5]; +} +test bool testListSplicing16() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [L1, *L2, 5] == [[1, 2], 3, 4, 5]; +} +test bool testListSplicing17() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [*L1, *L2, 5] == [1, 2, 3, 4, 5]; +} + +test bool testListSplicing18() { + list[int] L1 = [1, 2]; + return [[L1]] == [[[1, 2]]]; +} +test bool testListSplicing19() { + list[int] L1 = [1, 2]; + return [[*L1]] == [[1, 2]]; +} + +test bool testListSplicing20() { + list[int] L1 = [1, 2]; + return [[L1], 3] == [[[1, 2]], 3]; +} +test bool testListSplicing21() { + list[int] L1 = [1, 2]; + return [[*L1], 3] == [[1, 2], 3]; +} + +test bool testListSplicing22() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [[L1], [L2]] == [[[1, 2]], [[3, 4]]]; +} +test bool testListSplicing23() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [[*L1], [L2]] == [[1, 2], [[3, 4]]]; +} + +test bool testListSplicing24() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [[L1], [*L2]] == [[[1, 2]], [3, 4]]; +} +test bool testListSplicing25() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [[*L1], [*L2]] == [[1, 2], [3, 4]]; +} + +test bool testListSplicing26() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [*[*L1], [*L2]] == [1, 2, [3, 4]]; +} +test bool testListSplicing27() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [[*L1], *[*L2]] == [[1, 2], 3, 4]; +} +test bool testListSplicing28() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [*[*L1], *[*L2]] == [1, 2, 3, 4]; +} + +test bool testListSplicing29() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [L1, [L2]] == [[1, 2], [[3, 4]]]; +} +test bool testListSplicing30() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [*L1, [L2]] == [1, 2, [[3, 4]]]; +} +test bool testListSplicing31() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [L1, [*L2]] == [[1, 2], [3, 4]]; +} +test bool testListSplicing32() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [*L1, [*L2]] == [1, 2, [3, 4]]; +} +test bool testListSplicing33() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [*L1, *[*L2]] == [1, 2, 3, 4]; +} + +test bool testListSplicing34() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [L1, [L2], 5] == [[1, 2], [[3, 4]], 5]; +} +test bool testListSplicing35() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [*L1, [L2], 5] == [1, 2, [[3, 4]], 5]; +} +test bool testListSplicing36() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + return [L1, [*L2], 5] == [[1, 2], [3, 4], 5]; +} + +test bool testListSplicing37() { + list[int] L1 = [1, 2]; + list[list[list[int]]] L3 = [[L1]]; + return L3 == [[[1, 2]]]; +} +test bool testListSplicing38() { + list[int] L1 = [1, 2]; + list[value] L3 = [[L1], 3]; + return L3 == [[[1, 2]], 3]; +} +test bool testListSplicing39() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + list[list[list[int]]] L3 = [[L1], [L2]]; + return L3 == [[[1, 2]], [[3, 4]]]; +} +test bool testListSplicing40() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + list[value] L3 = [L1, [L2]]; + return L3 == [[1, 2], [[3, 4]]]; +} +test bool testListSplicing41() { + list[int] L1 = [1, 2]; + list[int] L2 = [3, 4]; + list[value] L3 = [L1, [L2], 5]; + return L3 == [[1, 2], [[3, 4]], 5]; +} + +// testSetInListSplicing +test bool testSetInListSplicing1() { + list[int] L1 = [1, 2]; + set[int] S2 = {3, 4}; + return [L1, S2, 5] == [[1, 2], {3, 4}, 5]; +} +test bool testSetInListSplicing2() { + list[int] L1 = [1, 2]; + set[int] S2 = {3, 4}; + return [*L1, S2, 5] == [1, 2, {3, 4}, 5]; +} +test bool testSetInListSplicing3() { + list[int] L1 = [1, 2]; + set[int] S2 = {3, 4}; + return ([L1, *S2, 5] == [[1, 2], 3, 4, 5]) || ([L1, *S2, 5] == [[1, 2], 4, 3, 5]); +} +test bool testSetInListSplicing4() { + list[int] L1 = [1, 2]; + set[int] S2 = {3, 4}; + return ([*L1, *S2, 5] == [1, 2, 3, 4, 5]) || ([*L1, *S2, 5] == [1, 2, 4, 3, 5]); +} + +// testRange +test bool testRange1() = [1..1] == []; +test bool testRange2() = [1..2] == [1]; +test bool testRange3() = [1..-1] == [1, 0]; +test bool testRange4() = [1,2.. 10] == [1, 2, 3, 4, 5, 6, 7, 8, 9]; +test bool testRange5() = [1,3.. 10] == [1, 3, 5, 7, 9]; +test bool testRange6() = [1,-2.. 10] == []; +test bool testRange7() = [1,-3.. -10] == [1, -3, -7]; + +// testSet +test bool testSet1() = {} == {}; +test bool testSet2() = {} != {1}; +test bool testSet3() = {1} == {1}; +test bool testSet4() = {1} != {2}; +test bool testSet5() = {1, 2} == {1, 2}; +test bool testSet6() = {1, 2} == {2, 1}; +test bool testSet7() = {1, 2, 3, 1, 2, 3} == {3, 2, 1}; + +test bool testSet8() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} == {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; +test bool testSet9() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} == {10, 2, 3, 4, 5, 6, 7, 8, 9, 1}; +test bool testSet10() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} == {10, 9, 3, 4, 5, 6, 7, 8, 2, 1}; +test bool testSet11() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} == {10, 9, 7, 4, 5, 6, 3, 8, 2, 1}; +test bool testSet12() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} == {10, 9, 7, 6, 5, 4, 3, 8, 2, 1}; + +test bool testSet13() = {{1}, {2}} == {{2}, {1}}; +test bool testSet14() = {{}} == {{}}; +test bool testSet15() = {{}, {}} == {{}}; +test bool testSet16() = {{}, {}, {}} == {{}}; + +test bool testSet17() = {{1, 2}, {3, 4}} == {{2, 1}, {4, 3}}; + +test bool testSet18() = {} + {} == {}; +test bool testSet19() = {1, 2, 3} + {} == {1, 2, 3}; +test bool testSet20() = {} + {1, 2, 3} == {1, 2, 3}; +test bool testSet21() = {1, 2} + {3, 4, 5} == {1, 2, 3, 4, 5}; +test bool testSet22() = {1, 2, 3, 4} + {3, 4, 5} == {1, 2, 3, 4, 5}; +test bool testSet23() = {{1, 2}, {3, 4}} + {{5, 6}} == {{1, 2}, {3, 4}, {5, 6}}; +test bool testSet24() = 1 + {2, 3} == {1, 2, 3}; +test bool testSet25() = {1, 2} + 3 == {1, 2, 3}; + +test bool testSet26() = {} - {} == {}; +test bool testSet27() = {1, 2, 3} - {} == {1, 2, 3}; +test bool testSet28() = {} - {1, 2, 3} == {}; +test bool testSet29() = {1, 2, 3} - {3, 4, 5} == {1, 2}; +test bool testSet30() = {1, 2, 3, 4} - {1, 2, 3, 4, 5} == {}; +test bool testSet31() = {{1, 2}, {3, 4}, {5, 6}} - {{3, 4}} == {{1, 2}, {5, 6}}; +test bool testSet32() = {1, 2, 3} - 3 == {1, 2}; + +test bool testSet33() = {} & {} == {}; +test bool testSet34() = {1, 2, 3} & {} == {}; +test bool testSet35() = {} & {1, 2, 3} == {}; +test bool testSet36() = {1, 2, 3} & {3, 4, 5} == {3}; +test bool testSet37() = {1, 2, 3, 4} & {3, 4, 5} == {3, 4}; +test bool testSet38() + = {{1, 2}, {3, 4}, {5, 6}} & {{2, 1}, {8, 7}, {6, 5}} == {{1, 2}, {5, 6}}; + +test bool testSet39() = {} <= {}; +test bool testSet40() = {} <= {1}; +test bool testSet41() = {2, 1} <= {1, 2}; +test bool testSet42() = {2, 1} <= {1, 2, 3}; +test bool testSet43() = {2, 1} <= {2, 1, 0}; + +test bool testSet44() = {} < {1}; +test bool testSet45() = {2, 1} < {2, 1, 3}; + +test bool testSet46() = {} >= {}; +test bool testSet47() = {1} >= {}; +test bool testSet48() = {2, 3} >= {2}; + +test bool testSet49() = {1} > {}; +test bool testSet50() = {2, 1, 3} > {2, 3}; + +test bool testSet51() = {} * {} == {}; +test bool testSet52() = {1} * {9} == {<1, 9 >}; +test bool testSet53() = {1, 2} * {9} == {<1, 9 >, + <2, 9 >}; +test bool testSet54() = {1, 2, 3} * {9} == {<1, 9 >, + <2, 9 >, + <3, 9 >}; +test bool testSet55() + = {1, 2, 3} * {9, 10} == {<1, 9 >, + <1, 10 >, + <2, 9 >, + <2, 10 >, + <3, 9 >, + <3, 10 >}; + +test bool testSet56() = 2 in {1, 2, 3}; +test bool testSet57() = {4, 3} in {{1, 2}, {3, 4}, {5, 6}}; + +test bool testSet58() = 5 notin {1, 2, 3}; +test bool testSet59() = {7, 8} notin {{1, 2}, {3, 4}, {5, 6}}; + +test bool testSet60() = ((3 > 2) ? {1, 2} : {1, 2, 3}) == {1, 2}; + +test bool testSet61() = {<"a", [1, 2] >, + <"b", [] >, + <"c", [4, 5, 6] >} != {}; + +// Some nested set patterns to test backtracking behaviour. +data TYPESET + = SET(str name) + | SUBTYPES(TYPESET tset) + | INTERSECT(set[TYPESET] tsets) + ; + +// testSet +test bool testSet62() + = {INTERSECT({TYPESET _, *TYPESET _}), TYPESET _} := + {INTERSECT({SET("a"), SET("b")}), SET("c")}; +test bool testSet63() + = {INTERSECT({TYPESET t1, *TYPESET _}), t1} := + {INTERSECT({SET("a"), SET("b")}), SET("a")}; +test bool testSet64() + = {INTERSECT({TYPESET t1, *TYPESET _}), t1} := + {INTERSECT({SET("b"), SET("a")}), SET("a")}; + +test bool testSet65() + = { + | INTERSECT({TYPESET t1, *TYPESET t2}) := INTERSECT({SET("b"), SET("a")}) + } == {, + }; + +test bool testSet66() + = { + | {INTERSECT({TYPESET t1, *TYPESET rest}), t2} + := + {INTERSECT({SET("a"), SET("b")}), SET("b")} + } == {, + }; + +test bool testSet67() + = { + | {INTERSECT({TYPESET t1, *TYPESET rest}), t1} + := + {INTERSECT({SET("a"), SET("b")}), SET("b")} + } == {}; + +// TYPESET tests moved to SetMatch[12] +// testSetMultiVariable +test bool testSetMultiVariable1() + = {*value S1, *value S2} := {} && (S1 == {}) && (S2 == {}); +test bool testSetMultiVariable2() = {*S1, *S2} := {} && (S1 == {}) && (S2 == {}); + +test bool testSetMultiVariable3() + = {*int S1, *int S2} := {100} && ((S1 == {100} && S2 == {}) || (S1 == {} && S2 == {100})); +test bool testSetMultiVariable4() + = {*S1, *S2} := {100} && ((S1 == {100} && S2 == {}) || (S1 == {} && S2 == {100})); + +test bool testSetMultiVariable5() { + R = for ({*int S1, *int S2} := {100}) + append ; + return toSet(R) == {<{100}, {} >, + <{}, {100} >}; +} +test bool testSetMultiVariable6() { + R = for ({*S1, *S2} := {100}) + append ; + return toSet(R) == {<{100}, {} >, + <{}, {100} >}; +} + +test bool testSetMultiVariable7() { + R = for ({*S1, *S2} := {100}) + append ; + return toSet(R) == {<{100}, {} >, + <{}, {100} >}; +} + +// +// TODO: the following test requires a specific implementation specific +// set representation and, thus, should be refactored. To check +// splicing, without taking order into account, the list 'R' is now +// converted to a set. +// +test bool testSetMultiVariable8() { + R = for ({*S1, *S2} := {100, 200}) + append ; + return + toSet(R) == {<{200, 100}, {} >, + <{200}, {100} >, + <{100}, {200} >, + <{}, {200, 100} >}; +} +test bool testSetMultiVariable9() { + R = for ({*int S1, *S2} := {100, "a"}) + append ; + return toSet(R) == {<{100}, {"a"} >, + <{}, {100, "a"} >}; +} +test bool testSetMultiVariable10() { + R = for ({*int S1, *str S2} := {100, "a"}) + append ; + return toSet(R) == {<{100}, {"a"} >}; +} + +test bool testSetMultiVariable11() { + R = for ({*str S1, *S2} := {100, "a"}) + append ; + return toSet(R) == {<{"a"}, {100} >, + <{}, {100, "a"} >}; +} +test bool testSetMultiVariable12() { + R = for ({*str S1, *int S2} := {100, "a"}) + append ; + return toSet(R) == {<{"a"}, {100} >}; +} + +test bool testSetMultiVariable13() = !({*str _, *str _} := {100, "a"}); +test bool testSetMultiVariable14() = !({*int _, *int _} := {100, "a"}); + +test bool addSetError1() { + return {1, 2, 3} + true == {1, 2, 3, true}; +} + +// setSplicing +test bool testSetSplicing1() { + set[int] S1 = {1, 2}; + return {S1} == {{1, 2}}; +} +test bool testSetSplicing2() { + set[int] S1 = {1, 2}; + return {*S1} == {1, 2}; +} + +test bool testSetSplicing3() { + set[int] S1 = {1, 2}; + return {S1, 3} == {{1, 2}, 3}; +} +test bool testSetSplicing4() { + set[int] S1 = {1, 2}; + return {*S1, 3} == {1, 2, 3}; +} + +test bool tetestSetSplicing5() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {S1, S2} == {{1, 2}, {3, 4}}; +} +test bool testSetSplicing6() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {*S1, S2} == {1, 2, {3, 4}}; +} +test bool testSetSplicing7() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {S1, *S2} == {{1, 2}, 3, 4}; +} +test bool testSetSplicing8() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {*S1, *S2} == {1, 2, 3, 4}; +} + +test bool testSetSplicing9() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {S1, S2, 5} == {{1, 2}, {3, 4}, 5}; +} +test bool testSetSplicing10() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {*S1, S2, 5} == {1, 2, {3, 4}, 5}; +} +test bool testSetSplicing11() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {S1, *S2, 5} == {{1, 2}, 3, 4, 5}; +} +test bool testSetSplicing12() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {*S1, *S2, 5} == {1, 2, 3, 4, 5}; +} + +test bool testSetSplicing13() { + set[int] S1 = {1, 2}; + return {{S1}} == {{{1, 2}}}; +} + +test bool testSetSplicing14() { + set[int] S1 = {1, 2}; + return {{*S1}} == {{1, 2}}; +} +test bool testSetSplicing15() { + set[int] S1 = {1, 2}; + return {*{*S1}} == {1, 2}; +} + +test bool testSetSplicing16() { + set[int] S1 = {1, 2}; + return {{S1}, 3} == {{{1, 2}}, 3}; +} +test bool testSetSplicing17() { + set[int] S1 = {1, 2}; + return {*{S1}, 3} == {{1, 2}, 3}; +} +test bool testSetSplicing18() { + set[int] S1 = {1, 2}; + return {*{*S1}, 3} == {1, 2, 3}; +} + +test bool testSetSplicing19() { + set[int] S1 = {1, 2}; + return {*{*S1}, 2} == {1, 2}; +} + +test bool testSetSplicing20() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {{S1}, {S2}} == {{{1, 2}}, {{3, 4}}}; +} +test bool testSetSplicing21() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {*{S1}, {S2}} == {{1, 2}, {{3, 4}}}; +} +test bool testSetSplicing22() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {{S1}, *{S2}} == {{{1, 2}}, {3, 4}}; +} +test bool testSetSplicing23() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {*{S1}, *{S2}} == {{1, 2}, {3, 4}}; +} +test bool testSetSplicing24() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {*{*S1}, *{*S2}} == {1, 2, 3, 4}; +} + +test bool testSetSplicing25() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {S1, {S2}} == {{1, 2}, {{3, 4}}}; +} +test bool testSetSplicing26() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {S1, {*S2}} == {{1, 2}, {3, 4}}; +} +test bool testSetSplicing27() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {S1, *{*S2}} == {{1, 2}, 3, 4}; +} + +test bool testSetSplicing28() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {S1, {S2}, 5} == {{1, 2}, {{3, 4}}, 5}; +} +test bool testSetSplicing29() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {S1, {*S2}, 5} == {{1, 2}, {3, 4}, 5}; +} +test bool testSetSplicing30() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {S1, *{*S2}, 5} == {{1, 2}, 3, 4, 5}; +} +test bool testSetSplicing31() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + return {*S1, *{*S2}, 5} == {1, 2, 3, 4, 5}; +} + +test bool testSetSplicing32() { + set[int] S1 = {1, 2}; + set[set[set[int]]] S3 = {{S1}}; + return S3 == {{{1, 2}}}; +} + +test bool testSetSplicing33() { + set[int] S1 = {1, 2}; + set[value] S3 = {{S1}, 3}; + return S3 == {{{1, 2}}, 3}; +} +test bool testSetSplicing34() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + set[set[set[int]]] S3 = {{S1}, {S2}}; + return S3 == {{{1, 2}}, {{3, 4}}}; +} +test bool testSetSplicing35() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + set[value] S3 = {S1, {S2}}; + return S3 == {{1, 2}, {{3, 4}}}; +} +test bool testSetSplicing36() { + set[int] S1 = {1, 2}; + set[int] S2 = {3, 4}; + set[value] S3 = {S1, {S2}, 5}; + return S3 == {{1, 2}, {{3, 4}}, 5}; +} + +// testListInSetSplicing +test bool testListInSplicing1() { + list[int] L1 = [1, 2]; + set[int] S2 = {3, 4}; + return {L1, S2, 5} == {[1, 2], {3, 4}, 5}; +} +test bool testListInSplicing2() { + list[int] L1 = [1, 2]; + set[int] S2 = {3, 4}; + return {*L1, S2, 5} == {1, 2, {3, 4}, 5}; +} +test bool testListInSplicing3() { + list[int] L1 = [1, 2]; + set[int] S2 = {3, 4}; + return {L1, *S2, 5} == {[1, 2], 3, 4, 5}; +} +test bool testListInSplicing4() { + list[int] L1 = [1, 2]; + set[int] S2 = {3, 4}; + return {*L1, *S2, 5} == {1, 2, 3, 4, 5}; +} + +// testMap +test bool testMap1() = () == (); +test bool testMap2() = (1 : 10) != (); +test bool testMap3() = (1 : 10) == (1 : 10); +test bool testMap4() = (1 : 10) != (2 : 20); + +test bool testMap5() = () + () == (); +test bool testMap6() = (1 : 10) + () == (1 : 10); +test bool testMap7() = (1 : 10) + (2 : 20) == (1 : 10, + 2 : 20); +test bool testMap8() = (1 : 10, + 2 : 20) + (2 : 25) == (1 : 10, + 2 : 25); + +test bool testMap9() = () - () == (); +test bool testMap10() = (1 : 10, + 2 : 20) - () == (1 : 10, + 2 : 20); +test bool testMap11() = (1 : 10, + 2 : 20) - (2 : 20) == (1 : 10); +test bool testMap12() = (1 : 10, + 2 : 20) - (2 : 25) == (1 : 10); + +// This is current behaviour; is this ok? +test bool testMap13() = () & () == (); +test bool testMap14() = (1 : 10) & () == (); +test bool testMap15() = (1 : 10, + 2 : 20, + 3 : 30, + 4 : 40) & (2 : 20, + 4 : 40, + 5 : 50) == (2 : 20, + 4 : 40); +test bool testMap16() = (1 : 10, + 2 : 20, + 3 : 30, + 4 : 40) & (5 : 50, + 6 : 60) == (); + +test bool testMap17() = () <= (); +test bool testMap18() = () <= (1 : 10); +test bool testMap19() = (1 : 10) <= (1 : 10); +test bool testMap20() = (1 : 10) <= (1 : 10, + 2 : 20); + +test bool testMap21() = !(() < ()); +test bool testMap22() = () < (1 : 10); +test bool testMap23() = !((1 : 10) < (1 : 10)); +test bool testMap24() = (1 : 10) < (1 : 10, + 2 : 20); + +test bool testMap25() = () >= (); +test bool testMap26() = (1 : 10) >= (); +test bool testMap27() = (1 : 10) >= (1 : 10); +test bool testMap28() = (1 : 10, + 2 : 20) >= (1 : 10); + +test bool testMap29() = !(() > ()); +test bool testMap30() = (1 : 10) > (); +test bool testMap31() = !((1 : 10) > (1 : 10)); +test bool testMap32() = (1 : 10, + 2 : 20) > (1 : 10); + +test bool testMap33() = 1 in (1 : 10, + 2 : 20); +test bool testMap34() = !(3 in (1 : 10, + 2 : 20)); + +test bool testMap35() = 3 notin (1 : 10, + 2 : 20); +test bool testMap36() = !(2 notin (1 : 10, + 2 : 20)); + +test bool testMap37() { + map[str, list[int]] m = ("a" : [1, 2], + "b" : [], + "c" : [4, 5, 6]); + return m["a"] == [1, 2]; +} + +//@expected{NoSuchKey} +//test bool NoKeyError1(){ (1:10, 2:20)[3]; return true;} +//@expected{MultipleKey} +//test bool MultipleKeyError1(){ (1:10, 1:10); return true; } +// testTuple +test bool testTuple1() = <1, 2.5, true> == <1, 2.5, true>; +test bool testTuple2() = <1, 2.5, true> != <0, 2.5, true>; +test bool testTuple3() = <{1, 2}, 3> == <{2, 1}, 3>; +test bool testTuple4() = <1, {2, 3}> == <1, {3, 2}>; +test bool testTuple5() = <{1, 2}, {3, 4}> == <{2, 1}, {4, 3}>; + +test bool testTuple6() = <1> >= <1>; +test bool testTuple7() = <2> >= <1>; +test bool testTuple8() = <1, 2> >= <1>; +test bool testTuple9() = <1, 2> >= <1, 2>; +test bool testTuple10() = <1, 2> >= <1, 1>; +test bool testTuple11() = <1, "def"> >= <1, "abc">; +test bool testTuple12() = <1, [2, 3, 4]> >= <1, [2, 3]>; +test bool testTuple13() = <1, [2, 3]> >= <1, [2, 3]>; + +test bool testTuple14() = !(<1> > <1>); +test bool testTuple15() = <2> > <1>; +test bool testTuple16() = <1, 2> > <1>; +test bool testTuple17() = !(<1, 2> > <1, 2>); +test bool testTuple18() = <1, 2> > <1, 1>; +test bool testTuple19() = <1, "def"> > <1, "abc">; +test bool testTuple20() = <1, [2, 3, 4]> > <1, [2, 3]>; +test bool testTuple21() = !(<1, [2, 3]> > <1, [2, 3]>); + +test bool testTuple22() = <1> <= <1>; +test bool testTuple23() = <1> <= <2>; +test bool testTuple24() = <1> <= <1, 2>; +test bool testTuple25() = <1, 2> <= <1, 2>; +test bool testTuple26() = <1, 1> <= <1, 2>; +test bool testTuple27() = <1, "abc"> <= <1, "def">; +test bool testTuple28() = <1, [2, 3]> <= <1, [2, 3, 4]>; +test bool testTuple29() = <1, [2, 3]> <= <1, [2, 3]>; + +test bool testTuple30() = !(<1> < <1>); +test bool testTuple31() = <1> < <2>; +test bool testTuple32() = <1> < <1, 2>; +test bool testTuple33() = !(<1, 2> < <1, 2>); +test bool testTuple34() = <1, 1> < <1, 2>; +test bool testTuple35() = <1, "abc"> < <1, "def">; +test bool testTuple36() = <1, [2, 3]> < <1, [2, 3, 4]>; +test bool testTuple37() = !(<1, [2, 3]> < <1, [2, 3]>); + +test bool testTuple38() + = <1, "a", true> + <1.5, "def"> == <1, "a", true> + <1.5, "def">; + +// namedTuple +test bool testNamedTuple1() { + tuple[int key, str val] T = <1, "abc">; + return T.key == 1; +} +test bool testNamedTuple2() { + tuple[int key, str val] T = <1, "abc">; + return T.val == "abc"; +} + +// testRelation +test bool testRelation1() = {} == {}; +test bool testRelation2() = {<1, 10 >} == {<1, 10 >}; +test bool testRelation3() = {<1, 2, 3 >} == {<1, 2, 3 >}; +test bool testRelation4() = {<1, 10 >, + <2, 20 >} == {<1, 10 >, + <2, 20 >}; +test bool testRelation5() = {<1, 10 >, + <2, 20 >, + <3, 30 >} == {<1, 10 >, + <2, 20 >, + <3, 30 >}; +test bool testRelation6() = {<1, 2, 3 >, + <4, 5, 6 >} == {<4, 5, 6 >, + <1, 2, 3 >}; +test bool testRelation7() = {<1, 2, 3, 4 >, + <4, 5, 6, 7 >} == {<4, 5, 6, 7 >, + <1, 2, 3, 4 >}; + +test bool testRelation8() = {} != {<1, 2 >, + <3, 4 >}; +test bool testRelation9() = !({<1, 2 >, + <3, 4 >} == {}); + +test bool testRelation10() + = {<1, {1, 2, 3} >, + <2, {2, 3, 4} >} == {<1, {1, 2, 3} >, + <2, {2, 3, 4} >}; +test bool testRelation11() + = {<1, {1, 2, 3} >, + <2, {2, 3, 4} >} == {<2, {2, 3, 4} >, + <1, {1, 2, 3} >}; +test bool testRelation12() + = {<1, {1, 2, 3} >, + <2, {2, 3, 4} >} == {<2, {4, 3, 2} >, + <1, {2, 1, 3} >}; + +test bool testRelation13() = {<1, 10 >} + {} == {<1, 10 >}; +test bool testRelation14() = {} + {<1, 10 >} == {<1, 10 >}; +test bool testRelation15() = {<1, 10 >} + {<2, 20 >} == {<1, 10 >, + <2, 20 >}; +test bool testRelation16() + = {<1, 10 >, + <2, 20 >} + {<3, 30 >} == {<1, 10 >, + <2, 20 >, + <3, 30 >}; +test bool testRelation17() + = {<1, 10 >, + <2, 20 >} + {<2, 20 >, + <3, 30 >} == {<1, 10 >, + <2, 20 >, + <3, 30 >}; +test bool testRelation17a() + = {<1, 10 >, + <2, 20 >} + <3, 30> == {<1, 10 >, + <2, 20 >, + <3, 30 >}; +test bool testRelation17b() + = <1, 10> + {<2, 20 >, + <3, 30 >} == {<1, 10 >, + <2, 20 >, + <3, 30 >}; + +test bool testRelation18() = {<1, 10 >} - {} == {<1, 10 >}; +test bool testRelation19() = {} - {<1, 10 >} == {}; +test bool testRelation20() = {<1, 10 >, + <2, 20 >} - {<2, 20 >, + <3, 30 >} == {<1, 10 >}; + +test bool testRelation21() = {<1, 10 >} & {} == {}; +test bool testRelation22() = {} & {<1, 10 >} == {}; +test bool testRelation23() = {<1, 10 >, + <2, 20 >} & {<2, 20 >, + <3, 30 >} == {<2, 20 >}; +test bool testRelation24() + = {<1, 2, 3, 4 >, + <2, 3, 4, 5 >} & {<2, 3, 4, 5 >, + <3, 4, 5, 6 >} == {<2, 3, 4, 5 >}; + +test bool testRelation25() = <2, 20> in {<1, 10 >, + <2, 20 >, + <3, 30 >}; +test bool testRelation26() = <1, 2, 3> in {<1, 2, 3 >, + <4, 5, 6 >}; + +test bool testRelation27() = <4, 40> notin {<1, 10 >, + <2, 20 >, + <3, 30 >}; +test bool testRelation28() = <1, 2, 4> notin {<1, 2, 3 >, + <4, 5, 6 >}; + +test bool testRelation29() = {} o {} == {}; +test bool testRelation30() = {<1, 10 >, + <2, 20 >} o {} == {}; +test bool testRelation31() = {} o {<10, 100 >, + <20, 200 >} == {}; +test bool testRelation32() + = {<1, 10 >, + <2, 20 >} o {<10, 100 >, + <20, 200 >} == {<1, 100 >, + <2, 200 >}; + +test bool testRelation33() + = {<1, "a" >, + <2, "b" >} * {<false, 0 >, + <true, 1 >} == {<<1, "a">, <false, 0> >, + <<2, "b">, <false, 0> >, + <<1, "a">, <true, 1> >, + <<2, "b">, <true, 1> >}; + +test bool testRelation34() = {<1, 2 >} join {<2, 3 >} == {<1, 2, 2, 3 >}; +test bool testRelation35() = {<1, 2 >} join {} == {}; +test bool testRelation36() = {} join {<2, 3 >} == {}; +test bool testRelation37() = {} join {} == {}; +test bool testRelation38() = {<1, 2 >} join {3} == {<1, 2, 3 >}; +test bool testRelation39() = {<1, 2 >} join {3, 4} == {<1, 2, 3 >, + <1, 2, 4 >}; +test bool testRelation40() = {1} join {2} == {<1, 2 >}; +test bool testRelation41() = {1, 2, 3} join {2} == {<1, 2 >, + <2, 2 >, + <3, 2 >}; + +test bool testRelation42() = {}+ == {}; +test bool testRelation43() = {}* == {}; + +test bool testRelation44() + = {<1, 2 >, + <2, 3 >, + <3, 4 >}+ == {<1, 2 >, + <2, 3 >, + <3, 4 >, + <1, 3 >, + <2, 4 >, + <1, 4 >}; + +test bool testRelation45() + = {<1, 2 >, + <2, 3 >, + <3, 4 >}* == {<1, 2 >, + <2, 3 >, + <3, 4 >, + <1, 3 >, + <2, 4 >, + <1, 4 >, + <1, 1 >, + <2, 2 >, + <3, 3 >, + <4, 4 >}; + +test bool testRelation46() + = {<1, 2 >, + <2, 3 >, + <3, 4 >, + <4, 2 >, + <4, 5 >}+ == {<1, 2 >, + <2, 3 >, + <3, 4 >, + <4, 2 >, + <4, 5 >, + <1, 3 >, + <2, 4 >, + <3, 2 >, + <3, 5 >, + <4, 3 >, + <1, 4 >, + <2, 2 >, + <2, 5 >, + <3, 3 >, + <4, 4 >, + <1, 5 >}; + +test bool testRelation47() + = {<1, 2 >, + <2, 3 >, + <3, 4 >, + <4, 2 >, + <4, 5 >}* == {<1, 2 >, + <2, 3 >, + <3, 4 >, + <4, 2 >, + <4, 5 >, + <1, 3 >, + <2, 4 >, + <3, 2 >, + <3, 5 >, + <4, 3 >, + <1, 4 >, + <2, 2 >, + <2, 5 >, + <3, 3 >, + <4, 4 >, + <1, 5 >, + <1, 1 >, + <5, 5 >}; + +test bool testRelation48() = {<1, 2 >, + <2, 3 >, + <3, 4 >, + <4, 2 >, + <4, 5 >}[7] == {}; +test bool testRelation49() = {<1, 2 >, + <2, 3 >, + <3, 4 >, + <4, 2 >, + <4, 5 >}[2] == {3}; +test bool testRelation50() = {<1, 2 >, + <2, 3 >, + <3, 4 >, + <4, 2 >, + <4, 5 >}[4] == {2, 5}; +test bool testRelation51() = {<1, 2 >, + <2, 3 >, + <3, 4 >, + <4, 2 >, + <4, 5 >}[_] == {2, 3, 4, 5}; + +test bool testRelation52() + = {<1, 2, 3 >, + <3, 4, 5 >, + <5, 6, 7 >, + <5, 4, 3 >}[_] == {<4, 3 >, + <6, 7 >, + <4, 5 >, + <2, 3 >}; +test bool testRelation53() + = {<1, 2, 3 >, + <3, 4, 5 >, + <5, 6, 7 >, + <5, 4, 3 >}[_, _] == {3, 5, 7}; +test bool testRelation54() = {<1, 2, 3 >, + <3, 4, 5 >, + <5, 6, 7 >, + <5, 4, 3 >}[_, 4] == {3, 5}; + +test bool testRelation55() = {<1, 2 >, + <2, 3 >, + <3, 4 >, + <4, 2 >, + <4, 5 >}[{2, 3}] == {3, 4}; +test bool testRelation56() + = {<1, 2, 3 >, + <3, 4, 5 >, + <5, 6, 7 >, + <5, 4, 3 >}[_, {2, 5}] == {3}; +test bool testRelation57() + = {<1, 2, 3 >, + <3, 4, 5 >, + <5, 6, 7 >, + <5, 4, 3 >}[{3, 5}, {1, 4}] == {5, 3}; + +test bool testRelation58() + = {<{1, 2, 3}, 4 >, + <{3, 4, 5}, 6 >, + <{5, 6, 7}, 8 >, + <{1, 2, 3}, 2 >, + <{1, 2, 3}, 3 >}[{1, 2, 3}] == {2, 3, 4}; +test bool testRelation59() + = {<{1, 2, 3}, 4, 4 >, + <{3, 4, 5}, 6, 7 >, + <{5, 6, 7}, 8, 9 >, + <{1, 2, 3}, 2, 4 >, + <{1, 2, 3}, 3, 0 >}[{1, 2, 3}] == {<2, 4 >, + <3, 0 >, + <4, 4 >}; +test bool testRelation60() + = {<{1, 2, 3}, 4, 4 >, + <{3, 4, 5}, 6, 7 >, + <{5, 6, 7}, 8, 9 >, + <{1, 2, 3}, 2, 4 >, + <{1, 2, 3}, 3, 0 >}[{1, 2, 3}, {4, 6}] == {4}; + +// namedRelation +test bool namedRelation1() { + rel[int from, int to] R = {<1, 10 >, + <2, 20 >}; + return R.from == {1, 2}; +} +test bool namedRelation2() { + rel[int from, int to] R = {<1, 10 >, + <2, 20 >}; + return R.to == {10, 20}; +} + +/* TODO: Issue :f constructor overlaps with NODE *//* + data NODE1 = val(value V) | f() | f1(NODE1 a); + test bool good1() { + return f1(val(1)) == f1(val(1)); + } + */data NODE + = i(int I) + | s(str x) + | st(set[NODE] s) + | l(list[NODE]) + | m(map[NODE, NODE] m) + | f() + | f(NODE a) + | f(NODE a, NODE b) + | g() + | g(NODE a) + | g(NODE a, NODE b) + ; + +// node +test bool node1() = f() == f(); +test bool node2() = f() != g(); +test bool node3() { + NODE n = f(); + NODE m = g(); + return n != m; +} +test bool node4() = f(i(1)) == f(i(1)); +test bool node5() = f(i(1)) != g(i(1)); +test bool node6() { + NODE n = f(i(1)); + NODE m = g(i(1)); + return n != m; +} +test bool node7() = f(i(1), i(2)) == f(i(1), i(2)); +test bool node8() = f(i(1), i(2)) != f(i(1), i(3)); +test bool node9() { + NODE n = f(i(1), i(2)); + NODE m = f(i(1), i(3)); + return n != m; +} +test bool node10() = f(i(1), g(i(2), i(3))) == f(i(1), g(i(2), i(3))); +test bool node11() = f(i(1), g(i(2), i(3))) != f(i(1), g(i(2), i(4))); +test bool node12() { + NODE n = f(i(1), g(i(2), i(3))); + NODE m = f(i(1), g(i(2), i(4))); + return n != m; +} +test bool node13() + = f(i(1), g(i(2), st({i(3), i(4), i(5)}))) == f(i(1), g(i(2), st({i(3), i(4), i(5)}))); +test bool node14() { + NODE n = f(i(1), g(i(2), st({i(3), i(4), i(5)}))); + NODE m = f(i(1), g(i(2), st({i(3), i(4), i(5), i(6)}))); + return n != m; +} +test bool node15() + = f(i(1), g(i(2), l([i(3), i(4), i(5)]))) == f(i(1), g(i(2), l([i(3), i(4), i(5)]))); +test bool node16() { + NODE n = f(i(1), g(i(2), l([i(3), i(4), i(5)]))); + NODE m = f(i(1), g(i(2), l([i(3), i(4), i(5), i(6)]))); + return n != m; +} +test bool node17() + = f(i(1), g(i(2), m((i(3) : i(3), + i(4) : i(4), + i(5) : i(5))))) == f(i(1), g(i(2), m((i(3) : i(3), + i(4) : i(4), + i(5) : i(5))))); +test bool node18() { + NODE n = f(i(1), g(i(2), m((i(3) : i(3), + i(4) : i(4), + i(5) : i(5))))); + NODE x = f(i(1), g(i(2), m((i(3) : i(3), + i(4) : i(4), + i(5) : i(0))))); + return n != x; +} + +test bool node19() = f() <= f(); +test bool node20() = f() <= g(); +test bool node21() = f() <= f(i(1)); +test bool node22() = f(i(1)) <= f(i(1)); +test bool node23() = f(i(1), i(2)) <= f(i(1), i(3)); +test bool node24() = f(i(1), i(2)) <= g(i(1), i(3)); +test bool node25() = f(i(1), s("abc")) <= f(i(1), s("def")); +test bool node26() = f(i(1), l([i(2), i(3)])) <= f(i(1), l([i(2), i(3), i(4)])); +test bool node27() = f(i(1), l([i(2), i(3)])) <= f(i(1), l([i(2), i(3)])); + +test bool node28() = !(f() < f()); +test bool node29() = f() < g(); +test bool node30() = f() < f(i(1)); +test bool node31() = !(f(i(1)) < f(i(1))); +test bool node32() = f(i(1), i(2)) < f(i(1), i(3)); +test bool node33() = f(i(1), i(2)) < g(i(1), i(3)); +test bool node34() = f(i(1), s("abc")) < f(i(1), s("def")); +test bool node35() = f(i(1), l([i(2), i(3)])) < f(i(1), l([i(2), i(3), i(4)])); +test bool node36() = !(f(i(1), l([i(2), i(3)])) < f(i(1), l([i(2), i(3)]))); + +test bool node37() = f() >= f(); +test bool node38() = g() >= f(); +test bool node39() = f(i(1)) >= f(); +test bool node40() = f(i(1)) >= f(i(1)); +test bool node41() = f(i(1), i(3)) >= f(i(1), i(2)); +test bool node42() = g(i(1), i(2)) >= f(i(1), i(3)); +test bool node43() = f(i(1), s("def")) >= f(i(1), s("abc")); +test bool node44() = f(i(1), l([i(2), i(3), i(4)])) >= f(i(1), l([i(2), i(3)])); +test bool node45() = f(i(1), l([i(2), i(3)])) >= f(i(1), l([i(2), i(3)])); + +test bool node46() = !(f() > f()); +test bool node47() = g() > f(); +test bool node48() = f(i(1)) > f(); +test bool node49() = !(f(i(1)) > f(i(1))); +test bool node50() = f(i(1), i(3)) > f(i(1), i(2)); +test bool node51() = g(i(1), i(2)) > f(i(1), i(3)); +test bool node52() = f(i(1), s("def")) > f(i(1), s("abc")); +test bool node53() = f(i(1), l([i(2), i(3), i(4)])) > f(i(1), l([i(2), i(3)])); +test bool node54() = !(f(i(1), l([i(2), i(3)])) > f(i(1), l([i(2), i(3)]))); + +data D = d(int ival); + +// undefined +test bool undefined2() { + T = (1 : 10); + return (T[1] ? 13) == 10; +} +test bool undefined3() { + T = (1 : 10); + return (T[2] ? 13) == 13; +} + +test bool undefined4() { + T = (1 : 10); + return T[1]? == true; +} +test bool undefined5() { + T = (1 : 10); + return T[2]? == false; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Declaration.rsc| +module lang::rascal::tests::functionality::Declaration + +/******************************************************************************* + * Copyright (c) 2009-2015 CWI + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + + * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI + * * Paul Klint - Paul.Klint@cwi.nl - CWI + * * Bert Lisser - Bert.Lisser@cwi.nl - CWI +*******************************************************************************/test bool localTypeInferenceBottomScope() { + x = 1; + return x == 1; +} + +test bool localTypeInferenceNestedScope() { + return { + x = 1; + x == 1; + } +} + +@ignoreCompiler{INCOMPATIBILITY: Inference works differently} +test bool localTypeInferenceNoEscape() { + { + x = 1; + x == 1; + } + x = "1"; + return x == "1"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/FunctionComposition.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Anastasia Izmaylova - A.Izmaylova@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +module lang::rascal::tests::functionality::FunctionComposition + +/* + * The 'o' function composition operator + */int twice(int n) = 2 * n; +int triple(int n) = 3 * n; + +int dup(int n) = n + n; +str dup(str s) = s + s; + +int trip(int n) = n + n + n; +str trip(str s) = s + s + s; + +test bool twiceTriple1() { + return (twice o triple)(5) == twice(triple(5)); +} + +test bool twiceTriple2() { + c = twice o triple; + return c(5) == twice(triple(5)); +} + +test bool dupTriple1() { + return (dup o triple)(5) == dup(triple(5)); +} + +test bool tripleDup1() { + return (triple o dup)(5) == triple(dup(5)); +} + +test bool dupTrip1() { + return (dup o trip)(5) == dup(trip(5)); +} + +test bool dupTrip2() { + c = dup o trip; + return c(5) == dup(trip(5)); +} + +test bool dupTrip3() { + c = dup o trip; + return c("abc") == dup(trip("abc")); +} + +int fib(0) = 0; +int fib(1) = 1; +default int fib(int n) = fib(n - 1) + fib(n - 2); + +int fact(0) = 1; +int fact(1) = 1; +default int fact(int n) = n * fact(n - 1); +str printResult(int n) = " ; "; +str printResult(str s) = s + s; + +int f(0) = 0; +int f(1) = 1; +default int f(int n) = n + 1; + +int g(0) { + fail; +} +int g(1) = 1; +default int g(int n) = n + 2; + +test bool factorialFibonacci() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + list[int] outputs1 = [fact(fib(i)) | int i <- inputs]; + list[int] outputs2 = [(fact o fib)(i) | int i <- inputs]; + return outputs1 == outputs2; +} + +test bool factorialFibonacciPrint1() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + list[str] outputs1 = [printResult(fact(fib(i))) | int i <- inputs]; + list[str] outputs2 = [(printResult o fact o fib)(i) | int i <- inputs]; + + return outputs1 == outputs2; +} + +test bool factorialFibonacciPrint2() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + list[str] outputs1 = [printResult(fact(fib(i))) | int i <- inputs]; + + // associativity check of the 'o' operator + list[str] outputs3 = [((printResult o fact) o fib)(i) | int i <- inputs]; + return outputs1 == outputs3; +} + +test bool factorialFibonacciPrint3() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + list[str] outputs1 = [printResult(fact(fib(i))) | int i <- inputs]; + + // associativity check of the 'o' operator + list[str] outputs4 = [(printResult o (fact o fib))(i) | int i <- inputs]; + return outputs1 == outputs4; +} + +test bool anonymousFunctionComposition() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + list[int] outputs1 + = [int + (int n){ + switch(n) { + case 0: + return 1; + case 1: + return 1; + case int m: + return m * (m - 1); + default: + return -1; + } + }/* renamed n to m*/ ( + int + (int n){ + switch(n) { + case 0: + return 0; + case 1: + return 1; + case int m: + return (m - 1) + (m - 2); + default: + return -1; + } + }/* renamed n to m*/ ( + i + ) + ) | int i <- inputs]; + list[int] outputs2 + = [(int + (int n){ + switch(n) { + case 0: + return 1; + case 1: + return 1; + case int m: + return m * (m - 1); + default: + return -1; + } + } + /* renamed n to m*/ o + int + (int n){ + switch(n) { + case 0: + return 0; + case 1: + return 1; + case int m: + return (m - 1) + (m - 2); + default: + return -1; + } + })/* renamed n to m*/ ( + i + ) | int i <- inputs]; + return outputs1 == outputs2; +} + +test bool composedOverloadedFunctions1() { + return (g o f)(0) == g(f(0)); +} + +test bool composedOverloadedFunctions2() { + return (g o f)(0) == 2; +} + +/* + * The '+' function composition operator + */str h(0) = "0"; +str h(1) = "1"; +default str h(int n) { + fail; +} + +str i(0) = "1"; +str i(1) = "2"; +default str i(int n) = ""; + +int j0(0) = 0; +int j1(1) = 1; +default int j3(int n) = 2 * n; + +default int j4(int n) = 2 * n - 1; + +int k(int n) { + if (n % 2 == 0) { + fail k; + } + else { + return 2 * n; + } +} + +int l(int n) { + if (n % 2 == 0) { + return n * (n - 1); + } + else { + fail l; + } + ; +} + +test bool nonDeterministicChoiceAndNormalComposition11() { + list[int] inputs = [2, 3]; + list[str] outputs1 = [i(n) | int n <- inputs]; + list[str] outputs2 = [(h + i)(n) | int n <- inputs]; + return outputs1 == outputs2; +} + +test bool nonDeterministicChoiceAndNormalComposition12() { + list[int] inputs = [2, 3]; + list[str] outputs1 = [i(n) | int n <- inputs]; + list[str] outputs3 = [(i + h)(n) | int n <- inputs]; + return outputs1 == outputs3; +} + +test bool nonDeterministicChoiceAndNormalComposition13() + = (h + i)(0) == "0" || (h + i)(0) == "1"; + +test bool nonDeterministicChoiceAndNormalComposition14() + = (h + i)(1) == "1" || (h + i)(1) == "2"; + +test bool nonDeterministicChoiceAndNormalComposition15() + = (i + h)(0) == "0" || (i + h)(0) == "1"; + +test bool nonDeterministicChoiceAndNormalComposition16() + = (i + h)(1) == "1" || (i + h)(1) == "2"; + +test bool nonDeterministicChoiceAndNormalComposition21() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + list[int] outputs = [(n % 2 == 0) ? n * (n - 1) : 2 * n | int n <- inputs]; + list[int] outputs1 = [(k + l)(n) | int n <- inputs]; + + return outputs == outputs1; +} + +test bool nonDeterministicChoiceAndNormalComposition22() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + list[int] outputs = [(n % 2 == 0) ? n * (n - 1) : 2 * n | int n <- inputs]; + list[int] outputs2 = [(l + k)(n) | int n <- inputs]; + + return outputs == outputs2; +} + +test bool nonDeterministicChoiceAndNormalComposition23() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + list[int] outputs = [(n % 2 == 0) ? n * (n - 1) : 2 * n | int n <- inputs]; + list[int] outputs3 = [((k + l) o (l + k))(n) | int n <- inputs]; + list[int] outputs4 = [n * (n - 1) | int n <- outputs]; + + return outputs3 == outputs4; +} + +test bool nonDeterministicChoiceAndNormalComposition24() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + list[int] outputs5 = [(j0 + j1 + (k + l) o j3)(n) | int n <- inputs]; + list[int] outputs7 = [0, 1] + [2 * n * (2 * n - 1) | int n <- inputs - [0, 1]]; + list[int] outputs9 = [2 * n * (2 * n - 1) | int n <- inputs]; + + return outputs5 == outputs7 || outputs5 == outputs9; +} + +test bool nonDeterministicChoiceAndNormalComposition25() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + list[int] outputs6 = [((k + l) o j4 + j0 + j1)(n) | int n <- inputs]; + list[int] outputs8 = [0, 1] + [2 * (2 * n - 1) | int n <- inputs - [0, 1]]; + list[int] outputs10 = [2 * (2 * n - 1) | int n <- inputs]; + + return outputs6 == outputs8 || outputs6 == outputs10; +} + +int twiceNotEven(int n) { + if (n % 2 == 0) { + fail twiceNotEven; + } + else { + return 2 * n; + } +} + +test bool nonDeterministicChoiceAndNormalComposition26() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + list[int] outputs8 = [0, 1] + [2 * (2 * n - 1) | int n <- inputs - [0, 1]]; + list[int] outputs10 = [2 * (2 * n - 1) | int n <- inputs]; + list[int] outputs11 + = [((twiceNotEven + l) o (int (int n){ return 2 * n - 1; }) + j0 + j1)(n) | int n <- inputs]; + + return outputs11 == outputs8 || outputs11 == outputs10; +} + +test bool nonDeterministicChoiceAndNormalComposition27() { + list[int] inputs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + list[int] outputs8 = [0, 1] + [2 * (2 * n - 1) | int n <- inputs - [0, 1]]; + list[int] outputs10 = [2 * (2 * n - 1) | int n <- inputs]; + list[int] outputs11 + = [((l + twiceNotEven) o (int (int n){ return 2 * n - 1; }) + j0 + j1)(n) | int n <- inputs]; + + return outputs11 == outputs8 || outputs11 == outputs10; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Interpolation.rsc| +module lang::rascal::tests::functionality::Interpolation + +test bool interpolateWhile() { + x = 10; + return + "<while (x > 0) {> <{ + x -= 1; + x; + }> <}>" == " 9 8 7 6 5 4 3 2 1 0 "; +} + +test bool interpolateDoWhile() { + x = 10; + return + "<do {> <{ + x -= 1; + x; + }> <}while (x > 0)>" == " 9 8 7 6 5 4 3 2 1 0 "; +} + +test bool interpolateIfThenElse() + = "abc <if (1 > 0) {> GT <}else {> LT <}> cde" == "abc GT cde"; + +test bool interpolateIfThenTrue() + = "abc <if (1 > 0) {> GT <}> cde" == "abc GT cde"; + +test bool interpolateIfThenFalse() + = "abc <if (0 > 0) {> GT <}> cde" == "abc cde"; + +test bool interpolateFor() + = "abc <for (i <- [1, 2, 3]) {> print <}> cde" == "abc print 1 print 2 print 3 cde"; + +test bool interpolateForNested() + = "<for (x <- [1, 2, 3]) {>outer <for (y <- [4, 5, 6]) {>inner , <}><}>" == "outer 1 inner 1,4 inner 1,5 inner 1,6 outer 2 inner 2,4 inner 2,5 inner 2,6 outer 3 inner 3,4 inner 3,5 inner 3,6 "; + +test bool interpolatePreFor() + = "<for (i <- [1, 2, 3]) { + j = i + 1;> <}>" == " 2 3 4 "; + +test bool interpolatePostWhile() { + x = 5; + return "<while (x > 0) {> " == " 5 4 3 2 1 "; +} + +test bool interpolateFor2() { + str s + = "<for (i <- [0..10]) {> + ' <if (i % 2 == 0) {> + ' i = + ' <}> + '<}>"; + return + s == " + ' + ' i = 0 + ' + ' + ' + ' + ' + ' i = 2 + ' + ' + ' + ' + ' + ' i = 4 + ' + ' + ' + ' + ' + ' i = 6 + ' + ' + ' + ' + ' + ' i = 8 + ' + ' + ' + '"; +} + +test bool interpolateQuotes() { + str s = " \" <0 + 0> \" "; + return s == " \" 0 \" "; +} + +test bool interpolateEsc1() { + A = "A"; + return "\t" == "\tA"; +} +test bool interpolateEsc2() { + A = "A"; + return "\t" == "A\t"; +} +test bool interpolateEsc3() { + A = "A"; + return "\t" == "A\tA"; +} + +test bool interpolateEsc4() { + A = "A"; + return "\n" == "\nA"; +} +test bool interpolateEsc5() { + A = "A"; + return "\n" == "A\n"; +} +test bool interpolateEsc6() { + A = "A"; + return "\n" == "A\n"; +} + +test bool interpolateEsc7() { + A = "A"; + return "\"" == "\"A"; +} +test bool interpolateEsc8() { + A = "A"; + return "\"" == "A\""; +} +test bool interpolateEsc9() { + A = "A"; + return "\"" == "A\""; +} + +test bool interpolateEsc10() { + A = "A"; + return "\\" == "\\A"; +} +test bool interpolateEsc11() { + A = "A"; + return "\\" == "A\\"; +} +test bool interpolateEsc12() { + A = "A"; + return "\\" == "A\\"; +} + +test bool interpolateEsc13() { + A = "A"; + return "\u0000" == "\u0000A"; +} +test bool interpolateEsc14() { + A = "A"; + return "\u0000" == "A\u0000"; +} +test bool interpolateEsc15() { + A = "A"; + return "\u0000" == "A\u0000A"; +} + +test bool interpolateEsc16() { + A = "A"; + return "\a20" == " A"; +} +test bool interpolateEsc17() { + A = "A"; + return "\a20" == "A "; +} +test bool interpolateEsc18() { + A = "A"; + return "\a20" == "A A"; +} + +test bool interpolateEsc19() { + A = "A"; + return "\U01F35D" == "🍝A"; +} +test bool interpolateEsc20() { + A = "A"; + return "\U01F35D " == "A🍝 "; +} +test bool interpolateEsc21() { + A = "A"; + return "\U01F35D " == "A🍝 A"; +} + +test bool interpolateEsc22() { + A = "A"; + return "\u2713" == "✓A"; +} +test bool interpolateEsc23() { + A = "A"; + return "\u2713" == "A✓"; +} +test bool interpolateEsc24() { + A = "A"; + return "\u2713" == "A✓A"; +} + +test bool interpolateEsc25() { + A = "A\tB"; + return "xz" == "xA\tBz"; +} +test bool interpolateEsc26() { + A = "A\\tB"; + return "xz" == "xA\\tBz"; +} + +test bool interpolationWithNewline() { + str declInfo2Doc(str doc) = "---- + ' + '++++"; + + str d = "AAA + 'BBB"; + str ls = /\r\n/ := d ? "\r\n" : "\n"; + return declInfo2Doc(d) == "----" + ls + "AAA" + ls + "BBB" + ls + "++++"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/KeywordParameter.rsc| +@license{ + Copyright (c) 2009-2020 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module lang::rascal::tests::functionality::KeywordParameter + +str f(int i, str k = "empty", int j = 0) { + k = k + "; bbb"; + j = j - 1; + return ", , "; +} + +test bool keywordParam10() = f(0) == "0, -1, empty; bbb"; +test bool keywordParam11() = f(0, k = "aaa") == "0, -1, aaa; bbb"; +test bool keywordParam12() = f(0, j = 100) == "0, 99, empty; bbb"; +test bool keywordParam13() = f(0, j = 100, k = "aaa") == "0, 99, aaa; bbb"; + +test bool keywordParam2() { + int incr(int x, int delta = 1) = x + delta; + return incr(3) == 4 && incr(3, delta = 2) == 5; +} + +test bool keywordParam3() { + int sum(int x = 0, int y = 0) = x + y; + return sum() == 0 && sum(x = 5, y = 7) == 5 + 7 && sum(y = 7, x = 5) == 5 + 7; +} + +// delta is unused on purpose for testing purposes +int f4(int _, int delta = 0) = g4(); + +int g4() = h4(); + +int h4(int delta = 1) { + return delta; +} + +test bool keywordParam4() { + return f4(0, delta = 5) == 1; +} + +data Point = point(int i, str color = "red"); + +// color and print are unused on purpose for testing +public +tuple[Point, Point] f5(int i, str color = "green", bool print = false) + = ; + +test bool keywordParam5() { + return + == <, >; +} + +data Figure (str fillColor = "white") = emptyFigure(); + +test bool keywordParam6() = emptyFigure().fillColor == "white"; + +str f7(int i, int j, str k = ", ") = k; + +test bool keywordParam71() = f7(1, 2) == "1, 2"; +test bool keywordParam72() = f7(3, 4) == "3, 4"; +test bool keywordParam73() = f7(1, 3, k = "1 + 3") == "1 + 3"; + +// delta is unused on purpose +int f8(int i, int delta = 100 + i) = g8(); + +int g8() = h8(665); + +int h8(int step, int delta = 1 + step) { + return delta; +} +test bool keywordParam81() = f8(0, delta = 999) == 666; +test bool keywordParam82() = f8(100) == 666; + +data F9 = f9(int i, int delta = 100); + +test bool keywordParam9() { + return f9(0).delta == 100; +} + +data F10 = f10(int i, int delta = 100); + +test bool keywordParam91() { + return f10(0, delta = 1).delta == 1; +} + +test bool keywordParam92() { + bool f11(bool c = false) { + bool g11() { + return c; + } + return g11(); + } + return f11() == false; +} + +test bool keywordParam93() { + bool f12(bool c = false) { + void g12() { + c = true; + } + g12(); + return c; + } + return f12() == true; +} + +test bool keywordParam101() { + int f13(int c = 10) { + int g13(int d = 100) { + return c + d; + } + return g13(); + } + return f13() == 110; +} + +test bool keywordParam102() { + int f14(int c = 10) { + int g14(int d = 100) { + return c + d; + } + return g14(); + } + return f14(c = 11) == 111; +} + +test bool keywordParam103() { + int f15(int c = 10) { + int g15(int d = 100) { + return c + d; + } + return g15(); + } + return f15(c = 11) == 111; +} + +test bool keywordParam104() { + int f16(int c = 10) { + int g16(int d = 100) { + return c + d; + } + return g16(d = 200); + } + return f16(c = 11) == 211; +} + +test bool keywordParam105() { + int f17(int c = 10) { + int g17(int c = 100) { + return c; + } + return g17(c = 200); + } + return f17(c = 11) == 200; +} + +test bool keywordParam106() { + int f18(int c = 10) { + int h18() { + int g18(int d = 100) { + return c + d; + } + return g18(d = 200); + } + return h18(); + } + return f18(c = 11) == 211; +} + +test bool keywordParam107() { + int f19(int c = 10) { + int h19() { + int g19(int c = 1, int d = 100) { + return c + d; + } + return g19(d = 200); + } + return h19(); + } + return f19(c = 11) == 201; +} + +test bool keywordParam108() { + int f20(int c = 10) { + int h20() { + int g20(int c = 1, int d = c * 100) { + return c + d; + } + return g20(c = 2); + } + return h20(); + } + return f20(c = 11) == 202; +} + +data X (int y = 1) = xx( int z = 0); +data X (int yy = 2) = xx(int u); +data X(int yyy = 3); + +test bool sharedKWParams1() = xx().y == 1; +test bool sharedKWParams2() = xx().yy == 2; +test bool sharedKWParams3() = xx().yyy == 3; + +// has +data F11 (int y = 1) + = d11(int n, real r = 1.5) + | d11(str s, bool b = true) + | d11(int n, str s) + ; + +test bool has1() = d11(0) has n; +test bool has2() = d11(0) has r; +test bool has3() = d11(0) has y; +test bool has4() = !(d11(0) has s); +test bool has5() = !(d11(0) has b); + +test bool has6() = d11("abc") has s; +test bool has7() = d11("abc") has b; +test bool has8() = d11("abc") has y; +test bool has9() = !(d11("abc") has n); +test bool has10() = !(d11("abc") has r); + +test bool has11() = d11(0, "abc") has n; +test bool has12() = d11(0, "abc") has s; +test bool has13() = d11(0, "abc") has y; +test bool has14() = !(d11(0, "abc") has r); +test bool has15() = !(d11(0, "abc") has b); + +// when +int f13(int n, str s = "") = n + when s == ""; +int f13(int n, str s = "") = -n + when s != ""; + +test bool when1() = f13(10) == 10; +test bool when2() = f13(10, s = "a") == -10; + +data E[&T] = e(&T t, int j = 0); + +test bool parametrizedDataTypeWithKwParam() = e(1).j == 0 && e(1, j = 2).j == 2; + +// static types +data GGG = ggg( rel[int a, int b] r = {<1, 2 >}); + +test bool fieldsNamesOfKeywordParametersIssue1851() { + ax = ggg(); + return ax.r.a == {1}; +} + +// Keyword parameters used in closures +test bool keywordParameterInClosure1() { + int f(int n, int(int) fun) = n + fun(n); + + int g(int d = 3) { + return f(7, int (int x) { + return x + d; + }); + } + return g(d = 5) == 19; +} + +test bool keywordParameterInClosure2() { + int f(int n, int(int) fun) = n + fun(n); + + int g(int n, int d = 2 * n) { + return f(n, int (int x) { + return x + d; + }); + } + return g(7) == 28; +} + +test bool keywordParameterInClosure3() { + int f(int n, int(int) fun) = n + fun(n); + + int g(int n, int d = 2 * n) { + return f(n, int (int x) { + return x + d; + }); + } + return g(7, d = 5) == 19; +} + +// Using keyword parameters in inner functions +int outer1(int t, int tabSize = 4) { + int rec(int t) = t + tabSize + when t > 10; + default int rec(int t) = t; + return rec(t); +} + +test bool outer1_1() = outer1(1) == 1; +test bool outer1_11() = outer1(11) == 15; +test bool outer1_11_kw() = outer1(11, tabSize = 40) == 51; + +int outer2(int t, int tabSize = 4) { + int rec(int t, int innerKwp = 5) = t + tabSize + innerKwp + when t > 10; + default int rec(int t) = t; + return rec(t); +} + +test bool outer2_1() = outer2(1) == 1; +test bool outer2_11() = outer2(11) == 20; +test bool outer2_11_kw() = outer2(11, tabSize = 40) == 56; + +int outer3(int t, int tabSize = 4) { + int rec(int t) { + int rec_inner(int t) = t + tabSize + when t > 10; + default int rec_inner(int t) = t; + return rec_inner(t); + } + return rec(t); +} + +test bool outer3_1() = outer3(1) == 1; +test bool outer3_11() = outer3(11) == 15; +test bool outer3_11_kw() = outer3(11, tabSize = 40) == 51; + +int outer4(int t, int tabSize = 4) { + int rec(int t) { + int rec_inner(int t, int innerKwp = 5) = t + tabSize + innerKwp + when t > 10; + default int rec_inner(int t) = t; + return rec_inner(t); + } + return rec(t); +} + +test bool outer4_1() = outer4(1) == 1; +test bool outer4_11() = outer4(11) == 20; +test bool outer4_11_kw() = outer4(11, tabSize = 40) == 56; + +int outer5(int t, int tabSize = 4) { + int rec(int t) { + int rec_inner(int t, int innerKwp = 5) = t + tabSize + innerKwp + when t > 10; + default int rec_inner(int t) = t; + return rec_inner(t, innerKwp = 50); + } + return rec(t); +} + +test bool outer5_1() = outer5(1) == 1; +test bool outer5_11() = outer5(11) == 65; +test bool outer5_11_kw() = outer5(11, tabSize = 40) == 101; + +data WorkspaceInfo (rel[int a, int b] defines = {}) = workspaceInfo(); + +@synopsis{a test for issue #2049} +test bool staticTypesOfCommonKeywordDefaults() { + ws = workspaceInfo(); + return ws.defines == {}; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/KeywordParameterExtend1/Extended.rsc| +module lang::rascal::tests::functionality::KeywordParameterExtend1::Extended + +private int xGlobal = 42 ; + +data DefInfo(int x = xGlobal); + +data DefInfo = noDefInfo(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/KeywordParameterExtend1/Extending.rsc| +module lang::rascal::tests::functionality::KeywordParameterExtend1::Extending + +import lang::rascal::tests::functionality::KeywordParameterExtend1::Extended; + +data Vis = publicVis(); + +data DefInfo(Vis vis = publicVis()); + +// issue 2497 +test bool extendedCommonKeyParamNotOverwritten() = noDefInfo().x == 42; + +// issue 2497 +test bool extendingCommonKeyParamNotOverwritten() + = noDefInfo().vis == publicVis(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/KeywordParameterImport1/DiamondBottom.rsc| +module lang::rascal::tests::functionality::KeywordParameterImport1::DiamondBottom + +import lang::rascal::tests::functionality::KeywordParameterImport1::DiamondLeft; +import lang::rascal::tests::functionality::KeywordParameterImport1::DiamondRight; +import lang::rascal::tests::functionality::KeywordParameterImport1::DiamondTop; + +test bool Bottom_ly_left1() = ly() has left; +test bool Bottom_ly_left2() = !ly().left?; +test bool Bottom_ly_left3() = ly().left == 10; +test bool Bottom_ly_left4() = ly(left = 20).left == 20; +@ignoreCompiler{INCOMPATIBILITY: Is defined operator `?` can only be applied to subscript, keyword parameter, field access, field project or get annotation} +test bool Bottom_ly_left5() = ly(left = 20)?; +test bool Bottom_ly_left6() = ly(left = 20).left == 20; + +test bool Bottom_ly_leftsq1() = ly() has leftsq; +test bool Bottom_ly_leftsq2() = !ly().leftsq?; +test bool Bottom_ly_leftsq3() = ly().leftsq == 100; +test bool Bottom_ly_leftsq4() = !(ly(left = 20).leftsq?); +test bool Bottom_ly_leftsq5() = ly(left = 20).leftsq == 400; + +test bool Bottom_ly_leftcb1() = ly() has leftcb; +test bool Bottom_ly_leftcb2() = !ly().leftcb?; +test bool Bottom_ly_leftcb3() = ly().leftcb == 1000; +test bool Bottom_ly_leftcb4() = !(ly(left = 20).leftcb?); +test bool Bottom_ly_leftcb5() = ly(left = 20).leftcb == 8000; + +test bool Bottom_ry_right1() = ry() has right; +test bool Bottom_ry_right2() = !ry().right?; +test bool Bottom_ry_right3() = ry().right == 10; +test bool Bottom_ry_right4() = ry(right = 20).right == 20; +@ignoreCompiler{INCOMPATIBILITY: Is defined operator `?` can only be applied to subscript, keyword parameter, field access, field project or get annotation} +test bool Bottom_ry_right5() = ry(right = 20)?; +test bool Bottom_ry_right6() = ry(right = 20).right == 20; + +test bool Bottom_ry_rightsq1() = ry() has rightsq; +test bool Bottom_ry_rightsq2() = !ry().rightsq?; +test bool Bottom_ry_rightsq3() = ry().rightsq == 100; +test bool Bottom_ry_rightsq4() = !(ry(right = 20).rightsq?); +test bool Bottom_ry_rightsq5() = ry(right = 20).rightsq == 400; + +test bool Bottom_ry_rightcb1() = ry() has rightcb; +test bool Bottom_ry_rightcb2() = !ry().rightcb?; +test bool Bottom_ry_rightcb3() = ry().rightcb == 1000; +test bool Bottom_ry_rightcb4() = !(ry(right = 20).rightcb?); +test bool Bottom_ry_rightcb5() = ry(right = 20).rightcb == 8000; + +test bool Bottom_Left_ly_has_no_right() = Left_ly_has_no_right(); +test bool Bottom_Left_ly_has_no_rightsq() = Left_ly_has_no_rightsq(); +test bool Bottom_Left_ly_has_no_rightcb() = Left_ly_has_no_rightcb(); + +test bool Bottom_Right_ry_has_no_left() = Right_ry_has_no_left(); +test bool Bottom_Right_ry_has_no_leftsq() = Right_ry_has_no_leftsq(); +test bool Bottom_Right_ry_has_no_leftcb() = Right_ry_has_no_leftcb(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/KeywordParameterImport1/DiamondLeft.rsc| +module lang::rascal::tests::functionality::KeywordParameterImport1::DiamondLeft + +import lang::rascal::tests::functionality::KeywordParameterImport1::DiamondTop; + +data X (int left = 10, int leftsq = left * left) = ly( int leftcb = leftsq * left); + +test bool Left_ly_left1() = ly() has left; +test bool Left_ly_left2() = !ly().left?; +test bool Left_ly_left3() = ly().left == 10; +test bool Left_ly_left4() = ly(left = 20).left == 20; +@ignoreCompiler{INCOMPATIBILITY: Is defined operator `?` can only be applied to subscript, keyword parameter, field access, field project or get annotation} +test bool Left_ly_left5() = ly(left = 20)?; +test bool Left_ly_left6() = ly(left = 20).left == 20; + +test bool Left_ly_leftsq1() = ly() has leftsq; +test bool Left_ly_leftsq2() = !ly().leftsq?; +test bool Left_ly_leftsq3() = ly().leftsq == 100; +test bool Left_ly_leftsq4() = !(ly(left = 20).leftsq?); +test bool Left_ly_leftsq5() = ly(left = 20).leftsq == 400; + +test bool Left_ly_left7() = ly() has left; +test bool Left_ly_leftsq6() = ly() has leftsq; + +test bool Left_ly_leftcb1() = ly() has leftcb; +test bool Left_ly_leftcb2() = !(ly().leftcb?); +test bool Left_ly_leftcb3() = ly().leftcb == 1000; +test bool Left_ly_leftcb4() = ly(left = 20).leftcb == 8000; + +test bool Left_ly_has_no_right() = !(ly() has right); +test bool Left_ly_has_no_rightsq() = !(ly() has rightsq); +test bool Left_ly_has_no_rightcb() = !(ly() has rightcb); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/KeywordParameterImport1/DiamondRight.rsc| +module lang::rascal::tests::functionality::KeywordParameterImport1::DiamondRight + +import lang::rascal::tests::functionality::KeywordParameterImport1::DiamondTop; + +data X (int right = 10, int rightsq = right * right) + = ry( int rightcb = rightsq * right); + +test bool Right_ry_right1() = ry() has right; +test bool Right_ry_right2() = !ry().right?; +test bool Right_ry_right3() = ry().right == 10; +test bool Right_ry_right4() = ry(right = 20).right == 20; +@ignoreCompiler{INCOMPATIBILITY: Is defined operator `?` can only be applied to subscript, keyword parameter, field access, field project or get annotation} +test bool Right_ry_right5() = ry(right = 20)?; +test bool Right_ry_right6() = ry(right = 20).right == 20; + +test bool Right_ry_rightsq1() = ry() has rightsq; +test bool Right_ry_rightsq2() = !ry().rightsq?; +test bool Right_ry_rightsq3() = ry().rightsq == 100; +test bool Right_ry_rightsq4() = !(ry(right = 20).rightsq?); +test bool Right_ry_rightsq5() = ry(right = 20).rightsq == 400; + +test bool Right_ry_rightcb1() = ry() has rightcb; +test bool Right_ry_rightcb2() = !ry().rightcb?; +test bool Right_ry_rightcb3() = ry().rightcb == 1000; +test bool Right_ry_rightcb4() = !(ry(right = 20).rightcb?); +test bool Right_ry_rightcb5() = ry(right = 20).rightcb == 8000; + +test bool Right_ry_has_no_left() = !(ry() has left); +test bool Right_ry_has_no_leftsq() = !(ry() has leftsq); +test bool Right_ry_has_no_leftcb() = !(ry() has leftcb); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/KeywordParameterImport1/DiamondTop.rsc| +module lang::rascal::tests::functionality::KeywordParameterImport1::DiamondTop + +data X; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/KeywordParameterImport2/Import1.rsc| +module lang::rascal::tests::functionality::KeywordParameterImport2::Import1 + +data L (int a = 1, int b = 2 * a) = l( int c = 2 * b); + +data L (int d = -1) = m(); + +L createL1() = l(); +L createM1() = l(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/KeywordParameterImport2/Import2.rsc| +module lang::rascal::tests::functionality::KeywordParameterImport2::Import2 + +data L (str e = "e", str f = e + e) = n( str g = f + f); + +data L (str h = "") = p(); + +L createN2() = n(); +L createP2() = p(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/KeywordParameterImport2/Tests.rsc| +@license{ +Copyright (c) 2014-2015 CWI +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/epl-v10.html +} +@contributor=|mailto://Jurgen.Vinju@cwi.nl| +module lang::rascal::tests::functionality::KeywordParameterImport2::Tests + +import lang::rascal::tests::functionality::KeywordParameterImport2::Import1; +import lang::rascal::tests::functionality::KeywordParameterImport2::Import2; + +// this requires keyword parameters attached to the adt +// to be distributed over all constructors in a module: +test bool sameModuleDef1() = l().a == m().a; +test bool sameModuleDef2() = l().d == m().d; + +// this requires keyword parameters attached to the adt +// to be distributed over all constructors visible in the current +// module, also the imported ones: +test bool crossModuleDef1() = l().a == n().a; +test bool crossModuleDef3() = m().d == n().d; +test bool crossModuleDef4() = n().f == l().f; +test bool crossModuleDef5() = n().h == m().h; + +// except for field access, also assignment should work: +test bool crossModuleAssignAndFieldRef() { + a = l(); + b = n(); + + a.e = "hello"; + b.b = 42; + + return a.e == "hello" && b.b == 42; +} + +// this requires the compiler/interpreter to inject code +// to resolve the default values dynamically +test bool allocatedElseWhereUsedWithNewExtension1() + = createL1().f == l().f; + +test bool allocatedElseWhereUsedWithNewExtension2() + = createN2().a == n().a; + +// this requires default values to not be set in the run-time +// such that values remain structurally equal to future extended +// values: +test bool defaultEquality() = createL1() == l(); + +// we see that the user has overridden a default, even though +// it is equal to the original. This is apparent in the serialized +// form as well, necessarily. +// `"l(a=0)" != "l()"` even though `l.a == 0` through the default mechanism +test bool observableOverrides() { + x = l(); + assert x.a == 1; + y = x; + assert x == y && "" == ""; + x.a = 1; + return x != y && "" != ""; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Pattern.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::functionality::Pattern + +import List; +import Set; + +data F + = f(int N) + | f(int N, int M) + | f(int N, value f, bool B) + | g(str S) + ; +data F1 + = f1(int N, int M = 10, bool B = false) + | f1(str S) + ; + +// matchLiteral +test bool matchLiteral1() = true := true; +test bool matchLiteral2() = !(true := false); +test bool matchLiteral3() = true !:= false; + +test bool matchLiteral4() = 1 := 1; +test bool matchLiteral5() = !(2 := 1); +test bool matchLiteral6() = 2 !:= 1; + +test bool matchLiteral7() = 1.5 := 1.5; +test bool matchLiteral8() = !(2.5 := 1.5); +test bool matchLiteral9() = 2.5 !:= 1.5; + +test bool matchLiteral10() = !(1.0 := 1.5); +test bool matchLiteral11() = 1.0 !:= 1.5; + +test bool matchLiteral12() = "abc" := "abc"; +test bool matchLiteral13() = "def" !:= "abc"; +test bool matchLiteral14() = "def" !:= "abc"; + +// matchADT +test bool matchADT1() = f(1) := f(1); +test bool matchADT2() = f(1, g("abc"), true) := f(1, g("abc"), true); +test bool matchADT3() = g("abc") !:= f(1); +test bool matchADT4() = f(1, 2) !:= f(1); +test bool matchADT5() = f(1, 2) !:= f(1); +test bool matchADT6() = f(_) := f(1); +test bool matchADT7() = f(_, _) := f(1, 2); +test bool matchADT8() = f(_, _, _) := f(1, 2.5, true); + +// matchADTWithKeywords +test bool matchADTwithKeywords1() = f1(1) := f1(1); +test bool matchADTwithKeywords2() = f1(1,M = 10) := f1(1); +test bool matchADTwithKeywords3() = f1(1,B = false, M = 10) := f1(1); +test bool matchADTwithKeywords4() = f1(1,M = 20) := f1(1, B = false, M = 20); + +test bool matchADTwithKeywords5() = f1(1,M = X) := f1(1, B = false, M = 20) && X == 20; + +// matchNode +test bool matchNode1() = "f"(1) := "f"(1); +test bool matchNode2() = "f"(1, "g"("abc"), true) := "f"(1, "g"("abc"), true); +test bool matchNode3() = "g"(1) !:= "f"(1); + +@ignoreInterpreter{to be determined} +test bool matchNode4() = !"g"(1) := "f"(1); +test bool matchNode5() = "f"(1, 2) !:= "f"(1); + +@ignoreInterpreter{to be determined} +test bool matchNode6() = !"f"(1, 2) := "f"(1); +test bool matchNode7() = "f"(1, 2) !:= "f"(1, 2, 3); + +@ignoreInterpreter{to be determined} +test bool matchNode8() = !"f"(1, 2) := "f"(1, 2, 3); + +test bool matchNode9() = "f"(_) := "f"(1); +test bool matchNode10() = "f"(_, _) := "f"(1, 2); +test bool matchNode11() = "f"(_, _, _) := "f"(1, 2.5, true); +test bool matchNode12() = "f"(1, _, 3) := "f"(1, 2, 3); +test bool matchNode13() = "f"(_, 2, _) := "f"(1, 2, 3); + +// matchNodeWithKeywords +test bool matchNodeWithKeywords1() = "f1"(1) := "f1"(1); + +test bool matchNodeWithKeywords2() = "f1"(1) !:= "f1"(2); +test bool matchNodeWithKeywords3() = "f1"(1,M = 10) := "f1"(1, M = 10); +test bool matchNodeWithKeywords4() = "f1"(1) := "f1"(1, M = 10); +test bool matchNodeWithKeywords5() = "f1"(1,M = 10) !:= "f1"(1, M = 20); +test bool matchNodeWithKeywords6() = "f1"(1,M = 10) !:= "f1"(1); +test bool matchNodeWithKeywords7() = "f1"(1,M = 10) !:= "f1"(1, B = false); + +test bool matchNodeWithKeywords8() + = "f1"(1,B = false, M = 10) := "f1"(1, M = 10, B = false); +test bool matchNodeWithKeywords9() + = "f1"(1,M = 20, B = false) := "f1"(1, B = false, M = 20); +test bool matchNodeWithKeywords10() = "f1"(1,M = 20) := "f1"(1, B = false, M = 20); +test bool matchNodeWithKeywords11() = "f1"(1) := "f1"(1, B = false, M = 20); +test bool matchNodeWithKeywords12() + = "f1"(1,B = false, M = 10) !:= "f1"(1, M = 20, B = false); +test bool matchNodeWithKeywords13() + = "f1"(1,M = 10, B = false) !:= "f1"(1, B = false, M = 20); +test bool matchNodeWithKeywords14() + = "f1"(1,M = _, B = false) := "f1"(1, B = false, M = 20); +test bool matchNodeWithKeywords15() + = "f1"(_,M = 20, B = false) := "f1"(1, B = false, M = 20); + +test bool matchNodeWithKeywords16() + = "f1"(1,M = X) := "f1"(1, B = false, M = 20) && X == 20; + +// matchVariable +test bool matchVariable1() = (n1 := 1) && (n1 == 1); +test bool matchVariable2() { + int n2 = 1; + return (n2 := 1) && (n2 == 1); +} +test bool matchVariable3() { + int n3 = 1; + return (n3 !:= 2) && (n3 == 1); +} + +test bool matchVariable4() = (f(n5) := f(1)) && (n5 == 1); +test bool matchVariable5() { + int n6 = 1; + return (f(n6) := f(1)) && (n6 == 1); +} + +test bool matchVariable6() = f(_) := f(1); + +// matchTypedVariableBecomes +test bool matchTypedVariableBecomes1() = int N: 3 := 3 && N == 3; + +// matchVariableBecomes +test bool matchVariableBecomes1() = N: 3 := 3 && N == 3; + +// variableBecomesEquality +@IgnoreCompiler{ +TODO: fails, assignment to N is not undone +} +test bool matchVariableBecomesEquality1() { + int N = 5; + return N: 3 !:= 3 && N != 3; +} + +test bool matchVariableBecomesEquality2() { + int N = 3; + return N: 3 := 3 && N == 3; +} + +test bool doubleVariableBecomes1() = !(([N: 3, N: 4] := [3, 4]) && N == 3); + +test bool doubleVariableBecomes2() = [N: 3, N: 3] := [3, 3] && N == 3; + +// antiPattern +test bool antiPattern1() = !4 := 3; +test bool antiPattern2() = !4 !:= 4; +test bool antiPattern3() = !(!4 := 4); +test bool antiPattern4() = !!4 := 4; +test bool antiPattern5() = !!4 !:= 3; +test bool antiPattern6() = !(!!4 := 3); +test bool antiPattern7() = (!(!3 := 3)); +test bool antiPattern8() = ![1, 2, 3] := [1, 2, 4]; +test bool antiPattern9() = ![1, 2, 3] !:= [1, 2, 3]; +test bool antiPattern10() = !(![1, 2, 3] := [1, 2, 3]); + +test bool antiPattern11() = ![1, 2] := [1, 2, 3]; +@ignoreInterpreter{to be determined} +test bool antiPattern12() = ![1, 2, 3] := [1, 2]; + +@ignoreInterpreter{to be determined} +test bool antiPattern13() = !{1, 2, 3} := {1, 2, 4}; + +test bool antiPattern14() = !{1, 2, 3} !:= {1, 2, 3}; +test bool antiPattern15() = !(!{1, 2, 3} := {1, 2, 3}); + +test bool antiPattern16() = !{1, 2} := {1, 2, 4}; + +@ignoreInterpreter{to be determined} +test bool antiPattern17() = !{1, 2, 3} := {1, 2}; + +test bool antiPattern18() = !<1, 2, 3> := <1, 2, 4>; +test bool antiPattern19() = !<1, 2, 3> !:= <1, 2, 3>; +test bool antiPattern20() = !(!<1, 2, 3> := <1, 2, 3>); + +@ignoreInterpreter{to be determined} +test bool antiPattern21() = !<1, 2> := <1, 2, 4>; +@ignoreInterpreter{to be determined} +test bool antiPattern22() = !<1, 2, 3> := <1, 2>; + +data MuExp = muCon(value v); + +bool tcc(muCon(list[value] lst)) = isEmpty(lst); +bool tcc(muCon(set[value] lst)) = isEmpty(lst); + +test bool overloadedConstructorArg1() = tcc(muCon({})); +test bool overloadedConstructorArg2() = tcc(muCon([])); + +// Match in loops +test bool matchInLoop1() { + lst = [1, 2, 3]; + cnt = 0; + for (int x <- lst) { + switch(x) { + case int n: + cnt += n; + } + } + return cnt == ( 0 | it + x | x <- lst ); +} + +test bool matchInLoop2() { + lst = [1, 2, 3]; + cnt = 0; + i = 0; + while(i < size(lst)) { + switch(lst[i]) { + case int n: + cnt += n; + } + i += 1; + } + return cnt == ( 0 | it + x | x <- lst ); +} + +test bool nodeMatchBacktracking() { + y = for ("f"({int a, int b, *int _}) := "f"({1, 2, 3, 4})) + append ; + return size(y) == 12; +} + +test bool tupleMatchBacktracking() { + y = for (<{int a, int b, *int _}> := <{1, 2, 3, 4}>) + append ; + return size(y) == 12; +} + +test bool switchListOnValue1() { + value yy = []; + switch(yy) { + case []: + return true; + default: + return false; + } +} + +test bool switchSetOnValue1() { + value yy = {}; + switch(yy) { + case {}: + return true; + default: + return false; + } +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/PatternDescendant.rsc| +module lang::rascal::tests::functionality::PatternDescendant + +data F + = f(F left, F right) + | g(int N) + ; + +// descendant1 +test bool descendant1() = /int N := 1 && N == 1; +test bool descendant2() = !/int _ := true; + +test bool descendant3() = !(/int _ := []); +test bool descendant4() = /int N := [1] && N == 1; + +test bool descendant5() = /int N := [1, 2, 3, 2] && N > 2; + +test bool descendant6a() = /4 := 4; +test bool descendant6b() = /4 !:= 3; +test bool descendant6c() = !/4 := 3; +test bool descendant6d() = !(/4 := 3); +test bool descendant6e() = !/4 !:= 4; +test bool descendant6f() = !(/4 !:= 4); +test bool descendant6g() = /!4 := 3; +test bool descendant6h() = /!4 !:= 4; +test bool descendant6i() = !/!4 !:= 3; +test bool descendant6j() = !(/!4 !:= 3); + +test bool descendant7a() = !/4 := [1, 2, 3, 2]; +test bool descendant7b() = /!4 := [1, 2, 3, 2]; +@ignoreInterpreter{TBD} +test bool descendant7c() = /!4 !:= [1, 2, 3, 4]; +@ignoreInterpreter{TBD} +test bool descendant7d() = !/!4 := [1, 2, 3, 4]; + +test bool descendant10() = /int N := (1 : 10) && (N == 1 || N == 10); + +test bool descendant11() = !(/int _ := {}); +test bool descendant12() = /int N := {1} && N == 1; +test bool descendant13() = /int N := {<false, 1 >} && N == 1; + +test bool descendant14() = /int N := ("a" : 1) && N == 1; +test bool descendant15() = /int N := <"a", 1> && N == 1; + +test bool descendant16() = [1, /int N, 3] := [1, [1, 2, 3, 2], 3] && N == 1; +test bool descendant17() = [1, /int N, 3] := [1, [1, 2, 3, 2], 3] && N == 2; + +test bool descendant18() = <1, /int N, 3> := <1, [1, 2, 3, 2], 3> && N == 1; +test bool descendant19() = <1, /int N, 3> := <1, [1, 2, 3, 2], 3> && N == 2; + +// descendant2 +data RECT = rect(int w, int h, str color = "white"); + +test bool descendant21() = [n | /int n <- [1, 2, 3]] == [1, 2, 3]; +test bool descendant22() + = [b | /bool b <- [true, false, true]] == [true, false, true]; +test bool descendant23() = [s | /str s <- ["a", "b"]] == ["a", "b"]; + +test bool descendant24() = {n| /int n <- {1, 2, 3}} == {1, 2, 3}; +test bool descendant25() = {n| /int n <- {<1, 2, 3 >}} == {1, 2, 3}; +test bool descendant26() + = {v| /value v <- {<1, "b", true >}} == {1, "b", true, <1, "b", true>}; + +@ignoreInterpreter{Not implemented} +test bool descendant27() + = {n| /int n := [1, "f"(2, kw1 = 3, kw2 = 4), 5]} == {1, 2, 3, 4, 5}; +@ignoreInterpreter{Not implemented} +test bool descendant28() + = {s| /str s := [1, rect(10, 20), 5, rect(30, 40, color = "red")]} == {"red"}; + +// descendant3 +test bool descendant30() = /g(2) := f(g(1), f(g(2), g(3))); +test bool descendant31() = [1, /g(2), 3] := [1, f(g(1), f(g(2), g(3))), 3]; +test bool descendant32() = [1, !/g(5), 3] := [1, f(g(1), f(g(2), g(3))), 3]; + +@ignoreCompiler{FIXME: Typechecker: missing constraints} +test bool descendant33() + = [1, [F] /f(/g(2), F _), 3] := [1, f(g(1), f(g(2), g(3))), 3]; +test bool descendant34() + = [1, /f(/g(2), /g(3)), 3] := [1, f(g(1), f(g(2), g(3))), 3]; + +test bool descendant35() + = [1, F outer: /f(/F inner: g(2), _), 3] := [1, f(g(1), f(g(2), g(3))), 3] + && outer == f(g(1), f(g(2), g(3))) + && inner == g(2); + +test bool descendant36() + = [1, /g(int N1), 3] := [1, f(g(1), f(g(2), g(3))), 3] && N1 == 1; +test bool descendant37() + = [1, /g(int N2), 3] := [1, f(g(1), f(g(2), g(3))), 3] && N2 == 2; +test bool descendant38() + = [1, /g(int N3), 3] := [1, f(g(1), f(g(2), g(3))), 3] && N3 == 3; + +data A = a(int i); +data B = b(str s); + +data C = c(A x, B y); + +test bool descendant40() + = [n | /int n := [c(a(3), b("a")), c(a(4), b("b"))]] == [3, 4]; +test bool descendant41() + = [n | /a(int n) := [c(a(3), b("a")), c(a(4), b("b"))]] == [3, 4]; +test bool descendant42() + = [s | /str s := [c(a(3), b("a")), c(a(4), b("b"))]] == ["a", "b"]; +test bool descendant43() + = [s | /b(str s) := [c(a(3), b("a")), c(a(4), b("b"))]] == ["a", "b"]; + +data D = d(map[A, B] ab); + +test bool descendant50() + = {n + | /int n := + [ + d((a(1) : b("one"), + a(2) : b("two"))), + d((a(10) : b("ten"), + a(20) : b("twenty"))) + ] + } == {1, 2, 10, 20}; +test bool descendant51() + = {n + | /a(int n) := + [ + d((a(1) : b("one"), + a(2) : b("two"))), + d((a(10) : b("ten"), + a(20) : b("twenty"))) + ] + } == {1, 2, 10, 20}; +test bool descendant52() + = {s + | /str s := + [ + d((a(1) : b("one"), + a(2) : b("two"))), + d((a(10) : b("ten"), + a(20) : b("twenty"))) + ] + } == {"one", "two", "ten", "twenty"}; +test bool descendant53() + = {s + | /b(str s) := + [ + d((a(1) : b("one"), + a(2) : b("two"))), + d((a(10) : b("ten"), + a(20) : b("twenty"))) + ] + } == {"one", "two", "ten", "twenty"}; + +data NODE + = nd(NODE lhs, NODE rhs) + | leaf(int n) + ; + +test bool descendant60() { + nd1 = "leaf"(1); + nd2 = "nd"(nd1, "leaf"(2)); + nd3 = "nd"("leaf"(3), "leaf"(4)); + nd4 = "nd"(nd2, nd3); + return + {v| /v: "nd"(node _, "leaf"(int _)) <- "nd"(nd4, "leaf"(0))} == {"nd"("leaf"(1), "leaf"(2)), "nd"("leaf"(3), "leaf"(4))}; +} + +test bool descendant61() { + cnd1 = leaf(1); + cnd2 = nd(cnd1, leaf(2)); + cnd3 = nd(leaf(3), leaf(4)); + cnd4 = nd(cnd2, cnd3); + + return + {v| /v: nd(NODE _, leaf(int _)) <- nd(cnd4, leaf(0))} == {nd(leaf(3), leaf(4)), nd(leaf(1), leaf(2))}; +} + +test bool descendant62() { + nd1 = "leaf"(1); + nd2 = "nd"(nd1, "leaf"(2)); + nd3 = "nd"("leaf"(3), "leaf"(4)); + nd4 = "nd"(nd2, nd3); + + return + [v | /v: "nd"(node _, "leaf"(int _)) <- "nd"(nd4, "leaf"(0))] == ["nd"("leaf"(1), "leaf"(2)), "nd"("leaf"(3), "leaf"(4))]; +} + +test bool descendant63() { + cnd1 = leaf(1); + cnd2 = nd(cnd1, leaf(2)); + cnd3 = nd(leaf(3), leaf(4)); + cnd4 = nd(cnd2, cnd3); + + return + [v | /v: nd(NODE _, leaf(int _)) <- nd(cnd4, leaf(0))] == [nd(leaf(1), leaf(2)), nd(leaf(3), leaf(4))]; +} + +test bool descendant64() { + n = 0; + for (/_: [*value _] := [1, [2], [3, [4, 6, [7]]], [[8, [9]], [[[10]]]]]) { + n += 1; + } + return n == 11; +} + +test bool descendant65() { + n = 0; + for (/1 := [1, 2, 3, [1, 2, 3, [1, 2, 3]]]) { + n += 1; + } + return n == 3; +} + +test bool descendant66() { + return [v | /v: <value _, int _> <- <<<1, 2>, <3, 4>>, 0>] == [<1, 2 >, + <3, 4 >]; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/PatternList1.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::functionality::PatternList1 + +import List; + +public int ModVar42 = 42 ; +public int ModVar44 = 44 ; +public list[int] ModVarList_41_42_43 = [41, 42, 43] ; + +test bool matchModuleVar1() = ModVar42 := 42; +test bool matchModuleVar2() = ModVarList_41_42_43 := ModVarList_41_42_43; + +// matchList1 +test bool matchList1() = !([] := [2]); +test bool matchList2() = !([1] := []); + +test bool matchList3() = [] := []; +test bool matchList4() = [1] := [1]; +test bool matchList5() = [1, 2] := [1, 2]; + +test bool matchList6() = !([1] := [2]); +test bool matchList7() = !([1, 2] := [1, 2, 3]); + +test bool matchList8() = ([int N] := [1]) && (N == 1); +test bool matchList9() = [_] := [1]; + +test bool matchList10() = ([int N, 2, int M] := [1, 2, 3]) && (N == 1) && (M == 3); +test bool matchList11() = [_, 2, _] := [1, 2, 3]; + +test bool matchList12() = ([int N, 2, N] := [1, 2, 1]) && (N == 1); + +test bool matchList13() = !(([int N, 2, N] := [1, 2, 3])); +test bool matchList14() = !([int N, 2, N] := [1, 2, "a"]); + +test bool matchList15() { + int N = 1; + return ([N, 2, int M] := [1, 2, 3]) && (N == 1) && (M == 3); +} +test bool matchList16() { + int N = 1; + return !([N, 2, int _] := [4, 2, 3]); +} + +test bool matchList17() { + list[int] L = [3]; + return [1, 2, *L] := [1, 2, 3]; +} +test bool matchList18() { + list[int] L = [2, 3]; + return [1, *L] := [1, 2, 3]; +} + +test bool matchList19() = [1, [2, 3], 4] := [1, [2, 3], 4]; +test bool matchList20() = !([1, [2, 3], 4] := [1, [2, 3, 4], 4]); + +test bool matchList21() = ([list[int] L] := [[]]) && (L == []); +test bool matchList22() = ([1, list[int] L] := [1, [2]]) && (L == [2]); +test bool matchList23() = ([1, list[int] L, 10] := [1, [], 10]) && (L == []); +test bool matchList24() = ([1, list[int] L, 10] := [1, [2], 10]) && (L == [2]); +test bool matchList25() = ([*list[int] L] := []) && (L == []); +test bool matchList26() { + list[int] X = []; + return ([*int L] := X) && (L == []); +} +test bool matchList27() = ([*int L] := ([1] - [1])) && (L == []); +test bool matchList28() = ([*int L] := [1]) && (L == [1]); +test bool matchList29() = ([*int L] := [1, 2]) && (L == [1, 2]); + +test bool matchList30() = ([1, *int L] := [1]) && (L == []); +test bool matchList31() = ([1, *int L] := [1, 2]) && (L == [2]); +test bool matchList32() = ([1, *int L] := [1, 2, 3]) && (L == [2, 3]); + +test bool matchList33() = ([*int L, 10] := [10]) && (L == []); +test bool matchList34() = ([*int L, 10] := [1, 10]) && (L == [1]); +test bool matchList35() = ([*int L, 10] := [1, 2, 10]) && (L == [1, 2]); + +test bool matchList36() = ([1, *int L, 10] := [1, 10]) && (L == []); +test bool matchList37() = ([1, *int L, 10] := [1, 2, 10]) && (L == [2]); +test bool matchList38() + = ([1, *int L, 10, *int M, 20] := [1, 10, 20]) && (L == []) && (M == []); +test bool matchList39() + = ([1, *int L, 10, *int M, 20] := [1, 2, 10, 20]) && (L == [2]) && (M == []); +test bool matchList40() + = ([1, *int L, 10, *int M, 20] := [1, 2, 10, 3, 20]) && (L == [2]) && (M == [3]); +test bool matchList41() + = ([1, *int L, 10, *int M, 20] := [1, 2, 3, 10, 4, 5, 20]) && (L == [2, 3]) && (M == [4, 5]); + +test bool matchList42() + = ([1, *int L, 10, *L, 20] := [1, 2, 3, 10, 2, 3, 20]) && (L == [2, 3]); +test bool matchList43() = !(([1, *int L, 10, *L, 20] := [1, 2, 3, 10, 2, 4, 20])); + +test bool matchList44() = [*int _] := []; +test bool matchList45() = [*int _] := [1]; +test bool matchList46() = [*int _] := [1, 2]; +test bool matchList47() = ([1, *int _, 10, *int _, 20] := [1, 2, 10, 20]); + +test bool matchList48() = [*x] := []; +test bool matchList49() = [*x, *y] := []; +test bool matchList50() = [*x, *y, *z] := []; + +test bool matchList51() = [*x, 1] := [1]; +test bool matchList52() = [1, *y] := [1]; +test bool matchList53() = [*x, 1, *y] := [1]; + +test bool matchList54() = [*x, a] := [1] && a == 1; +test bool matchList55() = [a, *y] := [1] && a == 1; +test bool matchList56() = [*x, a, *y] := [1] && a == 1; + +test bool matchList60() { + res = []; + for ([*int a, *int b, *int c, *int d] := [1, 2, 3, 4, 5, 6]) { + res = res + [[a, b, c, d]]; + } + return + res == [ + [[], [], [], [1, 2, 3, 4, 5, 6]], + [[], [], [1], [2, 3, 4, 5, 6]], + [[], [], [1, 2], [3, 4, 5, 6]], + [[], [], [1, 2, 3], [4, 5, 6]], + [[], [], [1, 2, 3, 4], [5, 6]], + [[], [], [1, 2, 3, 4, 5], [6]], + [[], [], [1, 2, 3, 4, 5, 6], []], + [[], [1], [], [2, 3, 4, 5, 6]], + [[], [1], [2], [3, 4, 5, 6]], + [[], [1], [2, 3], [4, 5, 6]], + [[], [1], [2, 3, 4], [5, 6]], + [[], [1], [2, 3, 4, 5], [6]], + [[], [1], [2, 3, 4, 5, 6], []], + [[], [1, 2], [], [3, 4, 5, 6]], + [[], [1, 2], [3], [4, 5, 6]], + [[], [1, 2], [3, 4], [5, 6]], + [[], [1, 2], [3, 4, 5], [6]], + [[], [1, 2], [3, 4, 5, 6], []], + [[], [1, 2, 3], [], [4, 5, 6]], + [[], [1, 2, 3], [4], [5, 6]], + [[], [1, 2, 3], [4, 5], [6]], + [[], [1, 2, 3], [4, 5, 6], []], + [[], [1, 2, 3, 4], [], [5, 6]], + [[], [1, 2, 3, 4], [5], [6]], + [[], [1, 2, 3, 4], [5, 6], []], + [[], [1, 2, 3, 4, 5], [], [6]], + [[], [1, 2, 3, 4, 5], [6], []], + [[], [1, 2, 3, 4, 5, 6], [], []], + [[1], [], [], [2, 3, 4, 5, 6]], + [[1], [], [2], [3, 4, 5, 6]], + [[1], [], [2, 3], [4, 5, 6]], + [[1], [], [2, 3, 4], [5, 6]], + [[1], [], [2, 3, 4, 5], [6]], + [[1], [], [2, 3, 4, 5, 6], []], + [[1], [2], [], [3, 4, 5, 6]], + [[1], [2], [3], [4, 5, 6]], + [[1], [2], [3, 4], [5, 6]], + [[1], [2], [3, 4, 5], [6]], + [[1], [2], [3, 4, 5, 6], []], + [[1], [2, 3], [], [4, 5, 6]], + [[1], [2, 3], [4], [5, 6]], + [[1], [2, 3], [4, 5], [6]], + [[1], [2, 3], [4, 5, 6], []], + [[1], [2, 3, 4], [], [5, 6]], + [[1], [2, 3, 4], [5], [6]], + [[1], [2, 3, 4], [5, 6], []], + [[1], [2, 3, 4, 5], [], [6]], + [[1], [2, 3, 4, 5], [6], []], + [[1], [2, 3, 4, 5, 6], [], []], + [[1, 2], [], [], [3, 4, 5, 6]], + [[1, 2], [], [3], [4, 5, 6]], + [[1, 2], [], [3, 4], [5, 6]], + [[1, 2], [], [3, 4, 5], [6]], + [[1, 2], [], [3, 4, 5, 6], []], + [[1, 2], [3], [], [4, 5, 6]], + [[1, 2], [3], [4], [5, 6]], + [[1, 2], [3], [4, 5], [6]], + [[1, 2], [3], [4, 5, 6], []], + [[1, 2], [3, 4], [], [5, 6]], + [[1, 2], [3, 4], [5], [6]], + [[1, 2], [3, 4], [5, 6], []], + [[1, 2], [3, 4, 5], [], [6]], + [[1, 2], [3, 4, 5], [6], []], + [[1, 2], [3, 4, 5, 6], [], []], + [[1, 2, 3], [], [], [4, 5, 6]], + [[1, 2, 3], [], [4], [5, 6]], + [[1, 2, 3], [], [4, 5], [6]], + [[1, 2, 3], [], [4, 5, 6], []], + [[1, 2, 3], [4], [], [5, 6]], + [[1, 2, 3], [4], [5], [6]], + [[1, 2, 3], [4], [5, 6], []], + [[1, 2, 3], [4, 5], [], [6]], + [[1, 2, 3], [4, 5], [6], []], + [[1, 2, 3], [4, 5, 6], [], []], + [[1, 2, 3, 4], [], [], [5, 6]], + [[1, 2, 3, 4], [], [5], [6]], + [[1, 2, 3, 4], [], [5, 6], []], + [[1, 2, 3, 4], [5], [], [6]], + [[1, 2, 3, 4], [5], [6], []], + [[1, 2, 3, 4], [5, 6], [], []], + [[1, 2, 3, 4, 5], [], [], [6]], + [[1, 2, 3, 4, 5], [], [6], []], + [[1, 2, 3, 4, 5], [6], [], []], + [[1, 2, 3, 4, 5, 6], [], [], []] + ]; +} + +test bool matchListDynamic1() = [str s, int n] := ["a", 1]; +test bool matchListDynamic2() = [str _, int _] := ["a", 1]; +test bool matchListDynamic3() = [*str s, int n] := ["a", 1]; +test bool matchListDynamic4() = [str s, *int n] := ["a", 1]; +test bool matchListDynamic5() = [str _, int _] := ["a", 1]; +test bool matchListDynamic6() = [*str _, int _] := ["a", 1]; +test bool matchListDynamic7() = [str _, *int _] := ["a", 1]; + +test bool matchListDynamicNoMatch1() = [str s, int n] !:= ["a", true]; +test bool matchListDynamicNoMatch2() = [str _, int _] !:= ["a", true]; +test bool matchListDynamicNoMatch3() = [*str s, int n] !:= ["a", true]; +test bool matchListDynamicNoMatch4() = [str s, *int n] !:= ["a", true]; +test bool matchListDynamicNoMatch5() = [str _, int _] !:= ["a", true]; +test bool matchListDynamicNoMatch6() = [*str _, int _] !:= ["a", true]; +test bool matchListDynamicNoMatch7() = [str _, *int _] !:= ["a", true]; + +@ignore{investigate} +test bool matchList() + = ([1, list[int] L, [10, list[int] M, 100], list[int] N, 1000] := [1, [10, 100], 1000]); + +test bool matchListFalse1() { + list[value] l = [1, 2, 3]; + return [1, str _, 2] !:= l; +} + +test bool matchListModuleVar1() = [ModVar42] := [42]; +test bool matchListModuleVar2() + = [*ModVarList_41_42_43] := ModVarList_41_42_43; +test bool matchListModuleVar3() + = [ModVar42, *ModVarList_41_42_43] := [ModVar42, *ModVarList_41_42_43]; +@ignoreInterpreter{Seems to be a bug in the interpreter} +test bool matchListModuleVar4() + = [ModVar42, ModVarList_41_42_43] := [ModVar42, ModVarList_41_42_43]; + +// matchNestedList +test bool matchNestedList1() = !([] := [[2]]); + +test bool matchNestedList3() = [] := []; +test bool matchNestedList4() = [[1]] := [[1]]; +test bool matchNestedList5() = [[1, 2]] := [[1, 2]]; + +test bool matchNestedList6() = !([[1]] := [[2]]); +test bool matchNestedList7() = !([[1, 2]] := [[1, 2, 3]]); + +test bool matchNestedList8() = [*list[int] _] := []; + +test bool matchNestedList9() = [*list[int] _] := [[1]]; +test bool matchNestedList10() = [*list[int] _] := [[1, 2]]; + +test bool matchNestedList11() + = ([[1], *list[int] L, [6, 7, 8]] := [[1], [2, 3], [4, 5], [6, 7, 8]]) + && (L == [[2, 3], [4, 5]]); +test bool matchNestedList12() + = !(([[1], *list[int] L, [6, 7, 8]] := [[1], [2, 3], [4, 5], [8]]) + && + (L == [[2, 3], [4, 5]])); + +test bool matchNestedList13() + = ([[1], *list[int] L, [6, 7, 8], *L] := [[1], [2, 3], [4, 5], [6, 7, 8], [2, 3], [4, 5]]) + && (L == [[2, 3], [4, 5]]); + +test bool matchNestedList14() { + if ([*list[int] L] := [[1, 2]] && L == [[1, 2, 3]]) { + return false; + } + else { + return true; + } +} + +// matchExternalListVars +// int n is uninitialized on purpose here +test bool matchExternalListVars1() { + int n; + return n := 3 && n == 3; +} + +/*TODO:fails*/@Ignore +test bool matchExternalListVars2() { + list[int] L; + return ([1, *L, 4, 5] := [1, 2, 3, 4, 5] && L == [2, 3]); +} + +// matchListMultiVars +test bool matchListMultiVars1() = [1, *L, 4, 5] := [1, 2, 3, 4, 5] && L == [2, 3]; +test bool matchListMultiVars2() = [1, *_, 4, 5] := [1, 2, 3, 4, 5]; +test bool matchListMultiVars3() + = [1, *L, 4, *L, 5] := [1, 2, 3, 4, 2, 3, 5] && L == [2, 3]; + +// matchListSpliceVars +test bool matchListSpliceVars1() = [1, *L, 4, 5] := [1, 2, 3, 4, 5] && L == [2, 3]; +test bool matchListSpliceVars2() = [1, *int L, 4, 5] := [1, 2, 3, 4, 5] && L == [2, 3]; +test bool matchListSpliceVars3() = [1, *_, 4, 5] := [1, 2, 3, 4, 5]; +test bool matchListSpliceVars4() = [1, *int _, 4, 5] := [1, 2, 3, 4, 5]; +test bool matchListSpliceVars5() + = [1, *L, 4, *L, 5] := [1, 2, 3, 4, 2, 3, 5] && L == [2, 3]; +test bool matchListSpliceVars6() + = [1, *int L, 4, *L, 5] := [1, 2, 3, 4, 2, 3, 5] && L == [2, 3]; + +@ignoreCompiler{To be investigated} +test bool matchListSpliceVarsInOr() + = [1, 2, 3] == (([*int x] := [1, 2, 3] || [*int x] := [10, 20, 30]) ? x : []); + +// match list of tuples +test bool matchListTuples1() = [<1, 2, 3 >] := [<1, 2, 3 >]; +test bool matchListTuples2() = [<1, int n, 3 >] := [<1, 2, 3 >]; +test bool matchListTuples3() = [<1, int _, 3 >] := [<1, 2, 3 >]; +test bool matchListTuples4() = [, + *c ] := [<1, 2 >, + <3, 4 >]; +test bool matchListTuples5() = [*c, ] := [<1, 2 >, + <3, 3 >]; +test bool matchListTuples6() = [*c, <int a, a>] := [<1, 2 >, + <3, 3 >]; +test bool matchListTuples7() = [<int a, int b >, + , + *c ] := [<1, 2 >, + <2, 2 >, + <3, 4 >]; + +@ignoreCompiler{To be investigated} +test bool matchListTuples8() { + if ([<1, int n, 3 >] := [<1, 2, 3 >] && n == -2) { + return false; + } + else { + return true; + } +} + +// match list of lists +test bool matchListLists1() = [[1, 2, 3]] := [[1, 2, 3]]; +test bool matchListLists2() = [[1, int n, 3]] := [[1, 2, 3]]; +test bool matchListLists3() = [[1, int _, 3]] := [[1, 2, 3]]; +test bool matchListLists4() = [[a, b], *c] := [[1, 2], [3, 4]]; +test bool matchListLists5() = [*c, [a, a]] := [[1, 2], [3, 3]]; +test bool matchListLists6() = [*c, [int a, a]] := [[1, 2], [3, 3]]; +test bool matchListLists7() + = [[int a, int b], [b, b], *c] := [[1, 2], [2, 2], [3, 4]]; + +// match list of ADTs +data D + = d(int x, int y) + | d(int x, int y, int z) + ; + +test bool matchListADTs1() = [d(1, 2, 3)] := [d(1, 2, 3)]; +test bool matchListADTs2() = [d(1, int n, 3)] := [d(1, 2, 3)]; +test bool matchListADTs3() = [d(1, int _, 3)] := [d(1, 2, 3)]; +test bool matchListADTs4() = [d(a, b), *c] := [d(1, 2), d(3, 4)]; +test bool matchListADTs5() = [*c, d(a, a)] := [d(1, 2), d(3, 3)]; +test bool matchListADTs6() = [*c, d(int a, a)] := [d(1, 2), d(3, 3)]; +test bool matchListADTs7() + = [d(int a, int b), d(b, b), *c] := [d(1, 2), d(2, 2), d(3, 4)]; + +// matchListHasOrderedElement +test bool matchListHasOrderedElement1() = hasOrderedElement([]) == false; +test bool matchListHasOrderedElement2() = hasOrderedElement([1]) == false; +test bool matchListHasOrderedElement3() + = hasOrderedElement([1, 2]) == false; +test bool matchListHasOrderedElement4() + = hasOrderedElement([1, 2, 1]) == true; +test bool matchListHasOrderedElement5() + = hasOrderedElement([1, 2, 3, 4, 3, 2, 1]) == true; + +// matchListHasDuplicateElement +test bool matchListHasDuplicateElement1() + = hasDuplicateElement([]) == false; +test bool matchListHasDuplicateElement2() + = hasDuplicateElement([1]) == false; +test bool matchListHasDuplicateElement3() + = hasDuplicateElement([1, 2]) == false; +test bool matchListHasDuplicateElement4() + = hasDuplicateElement([1, 1]) == true; +test bool matchListHasDuplicateElement5() + = hasDuplicateElement([1, 2, 3]) == false; +test bool matchListHasDuplicateElement6() + = hasDuplicateElement([1, 2, 3, 1]) == true; +test bool matchListHasDuplicateElement7() + = hasDuplicateElement([1, 2, 3, 2]) == true; +test bool matchListHasDuplicateElement8() + = hasDuplicateElement([1, 2, 3, 3]) == true; + +// matchListIsDuo1 +test bool matchListIsDuo1() = isDuo1([]) == true; +test bool matchListIsDuo2() = isDuo1([1]) == false; +test bool matchListIsDuo3() = isDuo1([1, 1]) == true; +test bool matchListIsDuo4() = isDuo1([1, 2]) == false; +test bool matchListIsDuo5() = isDuo1([1, 2, 1]) == false; +test bool matchListIsDuo6() = isDuo1([1, 2, 1, 2]) == true; +test bool matchListIsDuo7() = isDuo1([1, 2, 3, 1, 2]) == false; +test bool matchListIsDuo8() = isDuo1([1, 2, 3, 1, 2, 3]) == true; + +// matchListIsDuo2 +test bool matchListIsDuo9() = isDuo2([]) == true; +test bool matchListIsDuo10() = isDuo2([1]) == false; +test bool matchListIsDuo11() = isDuo2([1, 1]) == true; +test bool matchListIsDuo12() = isDuo2([1, 2]) == false; +test bool matchListIsDuo13() = isDuo2([1, 2, 1]) == false; +test bool matchListIsDuo14() = isDuo2([1, 2, 1, 2]) == true; +test bool matchListIsDuo15() = isDuo2([1, 2, 3, 1, 2]) == false; +test bool matchListIsDuo16() = isDuo2([1, 2, 3, 1, 2, 3]) == true; + +// matchListIsDuo3 +test bool matchListIsDuo17() = isDuo3([]) == true; +test bool matchListIsDuo18() = isDuo3([1]) == false; +test bool matchListIsDuo19() = isDuo3([1, 1]) == true; +test bool matchListIsDuo20() = isDuo3([1, 2]) == false; +test bool matchListIsDuo21() = isDuo3([1, 2, 1]) == false; +test bool matchListIsDuo22() = isDuo3([1, 2, 1, 2]) == true; +test bool matchListIsDuo23() = isDuo3([1, 2, 3, 1, 2]) == false; +test bool matchListIsDuo24() = isDuo3([1, 2, 3, 1, 2, 3]) == true; + +// matchListIsTrio1 +test bool matchListIsTrio1() = isTrio1([]) == true; +test bool matchListIsTrio2() = isTrio1([1]) == false; +test bool matchListIsTrio3() = isTrio1([1, 1]) == false; +test bool matchListIsTrio4() = isTrio1([1, 1, 1]) == true; +test bool matchListIsTrio5() = isTrio1([2, 1, 1]) == false; +test bool matchListIsTrio6() = isTrio1([1, 2, 1]) == false; +test bool matchListIsTrio7() = isTrio1([1, 1, 2]) == false; +test bool matchListIsTrio8() = isTrio1([1, 2, 1, 2, 1, 2]) == true; + +// matchListIsTrio2 +test bool matchListIsTrio9() = isTrio2([]) == true; +test bool matchListIsTrio10() = isTrio2([1]) == false; +test bool matchListIsTrio11() = isTrio2([1, 1]) == false; +test bool matchListIsTrio12() = isTrio2([1, 1, 1]) == true; +test bool matchListIsTrio13() = isTrio2([2, 1, 1]) == false; +test bool matchListIsTrio14() = isTrio2([1, 2, 1]) == false; +test bool matchListIsTrio15() = isTrio2([1, 1, 2]) == false; +test bool matchListIsTrio16() = isTrio2([1, 2, 1, 2, 1, 2]) == true; + +// matchListIsTrio3 +test bool matchListIsTrio17() = isTrio3([]) == true; +test bool matchListIsTrio18() = isTrio3([1]) == false; +test bool matchListIsTrio19() = isTrio3([1, 1]) == false; +test bool matchListIsTrio20() = isTrio3([1, 1, 1]) == true; +test bool matchListIsTrio21() = isTrio3([2, 1, 1]) == false; +test bool matchListIsTrio22() = isTrio3([1, 2, 1]) == false; +test bool matchListIsTrio23() = isTrio3([1, 1, 2]) == false; +test bool matchListIsTrio24() = isTrio3([1, 2, 1, 2, 1, 2]) == true; + +// listCount1 +test bool listCount1(list[int] L) { + int cnt(list[int] L) { + int count = 0; + while([int _, *int _] := L) { + count = count + 1; + L = tail(L); + } + return count; + } + return cnt(L) == size(L); +} + +// listCount2 +test bool listCount2(list[int] L) { + int cnt(list[int] L) { + int count = 0; + while([int _, *int _] := L) { + count = count + 1; + L = tail(L); + } + return count; + } + return cnt(L) == size(L); +} + +// listCount3 +test bool listCount3(list[int] L) { + int cnt(list[int] L) { + int count = 0; + while([_, *int _] := L) { + count = count + 1; + L = tail(L); + } + return count; + } + + return cnt(L) == size(L); +} + +public bool hasOrderedElement(list[int] L) { + switch(L) { + case [*int _, int I, *int _, int J, *int _]: { + if (I > J) { + return true; + } + else { + fail; + } + } + } + return false; +} + +public bool hasDuplicateElement(list[int] L) { + switch(L) { + case [*int _, int I, *int _, int J, *int _]: + if (I == J) { + return true; + } + else { + fail; + } + default: + return false; + } +} + +public bool isDuo1(list[int] L) { + switch(L) { + case [*int L1, *int L2]: + if (L1 == L2) { + return true; + } + else { + fail; + } + default: + return false; + } +} + +public bool isDuo2(list[int] L) { + switch(L) { + case [*int L1, *L1]: + return true; + default: + return false; + } +} + +public bool isDuo3(list[int] L) { + return [*int L1, *L1] := L; +} + +public bool isTrio1(list[int] L) { + switch(L) { + case [*int L1, *int L2, *int L3]: + if ((L1 == L2) && (L2 == L3)) { + return true; + } + else { + fail; + } + default: + return false; + } +} + +public bool isTrio2(list[int] L) { + switch(L) { + case [*int L1, *L1, *L1]: + return true; + default: + return false; + } +} + +public bool isTrio3(list[int] L) { + return [*int L1, *L1, *L1] := L; +} + +test bool matchTypedListVarBecomes1() + = list[int] L1: [int N, *int L2, int M] := [1, 2, 3] + && L1 == [1, 2, 3] + && N == 1 + && L2 == [2] + && M == 3; +test bool matchTypedListVarBecomes2() + = [1, list[int] L: [int _], 2] := [1, [2], 2] && L == [2]; +test bool matchTypedListVarBecomes3() + = [1, list[int] L1: [*int L2, int N], 5] := [1, [2, 3, 4], 5] + && L1 == [2, 3, 4] + && L2 == [2, 3] + && N == 4; +test bool matchTypedListVarBecomes4() + = [1, list[int] L1: [*int L2, int N], L1] := [1, [2, 3, 4], [2, 3, 4]] + && L1 == [2, 3, 4] + && L2 == [2, 3] + && N == 4; + +@ignoreCompiler{To be investiagted} +test bool matchTypedListVarInOr() + = 1 == (([int x: 1] := [1] || [int x: 10] := [10]) ? x : -1); + +data D = d(int n); + +test bool listNamedElem1() = [n: d(1)] := [d(1)] && n == d(1); +test bool listNamedElem2() = [_: d(1)] := [d(1)]; +test bool listNamedElem3() = [_n: d(2)] !:= [d(1)]; + +test bool listTypeNamedElem1() = [D n: d(1)] := [d(1)] && n == d(1); +test bool listTypeNamedElem2() = [D _: d(1)] := [d(1)]; +test bool listTypeNamedElem3() = [D _n: d(2)] !:= [d(1)]; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/PatternList2.rsc| +@license{ + Copyright (c) 2009-2020 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module lang::rascal::tests::functionality::PatternList2 + +data DATA + = a() + | b() + | c() + | d() + | e(int N) + | f(list[DATA] L) + | f(set[DATA] S) + | s(set[DATA] S) + | g(int N) + | h(int N) + | f(DATA left, DATA right) + ; + +// matchList3 +test bool matchList48() = [a(), b()] := [a(), b()]; +test bool matchList49() = ([DATA X1, b()] := [a(), b()]) && (X1 == a()); + +test bool matchList50() = ([DATA _, DATA _, c()] !:= [a(), b()]); + +test bool matchList51() = ([e(int X3), b()] := [e(3), b()]) && (X3 == 3); +test bool matchList52() = ([e(int X4)] := [e(3)]) && (X4 == 3); +test bool matchList53() = ([e(int _)] !:= [a()]); + +test bool matchList54() + = ([a(), f([a(), b(), DATA X6])] := [a(), f([a(), b(), c()])]) && (X6 == c()); + +test bool matchList55() + = ([a(), f([a(), b(), DATA X7]), *DATA Y7] := [a(), f([a(), b(), c()]), b()]) + && (X7 == c() && Y7 == [b()]); +test bool matchList56() + = ([DATA A1, f([A1, b(), DATA _])] := [a(), f([a(), b(), c()])]) && (A1 == a()); +test bool matchList57() + = ([A1, f([A1, b(), DATA _])] := [a(), f([a(), b(), c()])]) && (A1 == a()); + +test bool matchList58() + = ([f([DATA A1, b(), DATA _]), A1] := [f([a(), b(), c()]), a()]) && (A1 == a()); +test bool matchList59() + = ([f([A1, b(), DATA _]), A1] := [f([a(), b(), c()]), a()]) && (A1 == a()); + +test bool matchList60() + = ([DATA A2, f([A2, b(), *DATA SX1]), *SX1] := [a(), f([a(), b(), c()]), c()]) + && (A2 == a()) + && (SX1 == [c()]); + +test bool matchList61() + = ([DATA A3, f([A3, b(), *DATA SX2]), *SX2] !:= [d(), f([a(), b(), c()]), a()]); +test bool matchList62() + = ([DATA A4, f([A4, b(), *DATA SX3]), *SX3] !:= [c(), f([a(), b(), c()]), d()]); + +// issue #1228 +test bool matchList63() = [*_, [int _, *int _]] := [[], [1, 1]]; + +// issue #1228 +test bool matchList64() = /[int _, *int _] := [[], [1, 1]]; + +// matchListSet +test bool matchListSet1() = [a(), b()] := [a(), b()]; +test bool matchListSet2() = ([DATA X1, b()] := [a(), b()]) && (X1 == a()); + +test bool matchListSet3() = ([DATA _, DATA _, c()] !:= [a(), b()]); + +test bool matchListSet4() = ([e(int X3), b()] := [e(3), b()]) && (X3 == 3); +test bool matchListSet5() = ([e(int X4)] := [e(3)]) && (X4 == 3); +test bool matchListSet6() = ([e(int _)] !:= [a()]); + +test bool matchListSet7() + = ([a(), f({a(), b(), DATA X6})] := [a(), f({a(), b(), c()})]) && (X6 == c()); +test bool matchListSet8() + = ({a(), f([a(), b(), DATA X7])} := {a(), f([a(), b(), c()])}) && (X7 == c()); + +test bool matchListSet9() + = ([a(), f({a(), b(), DATA X8}), *DATA Y8] := [a(), f({a(), b(), c()}), b()]) + && (X8 == c() && Y8 == [b()]); +test bool matchListSet10() + = ({a(), f([a(), b(), DATA X9]), *DATA Y9} := {a(), f([a(), b(), c()]), b()}) + && (X9 == c() && Y9 == {b()}); + +test bool matchListSet11() + = ([DATA A1, f({A1, b(), DATA _})] := [a(), f({a(), b(), c()})]) && (A1 == a()); +test bool matchListSet12() + = ({DATA A2, f([A2, b(), DATA _])} := {a(), f([a(), b(), c()])}) && (A2 == a()); + +// matchSet2 +test bool matchSet60() = {a(), b()} := {a(), b()}; +test bool matchSet61() = ({DATA X1, b()} := {a(), b()}) && (X1 == a()); + +test bool matchSet62() = {DATA _, DATA _, c()} !:= {a(), b()}; + +test bool matchSet63() = ({e(int X3), b()} := {e(3), b()}) && (X3 == 3); +test bool matchSet64() = ({e(int X4)} := {e(3)}) && (X4 == 3); +test bool matchSet65() = ({e(int _)} !:= {a()}); + +test bool matchSet66() = ({e(int X3), g(X3)} := {e(3), g(3)}) && (X3 == 3); +test bool matchSet67() + = ({e(X3), g(X3), h(X3)} := {e(3), h(3), g(3)}) && (X3 == 3); + +test bool matchSet68() + = ({a(), s({a(), b(), DATA X6})} := {a(), s({a(), b(), c()})}) && (X6 == c()); +test bool matchSet69() + = ({s({a(), b(), DATA X7}), a()} := {a(), s({a(), b(), c()})}) && (X7 == c()); + +test bool matchSet70() + = ({a(), s({a(), b(), DATA X8}), *DATA Y8} := {a(), b(), s({a(), b(), c()})}) + && (X8 == c() && Y8 == {b()}); +test bool matchSet71() + = ({DATA A1, s({A1, b(), DATA _})} := {a(), s({a(), b(), c()})}) && (A1 == a()); +test bool matchSet72() + = ({DATA A2, s({A2, b(), DATA _})} := {s({a(), b(), c()}), a()}) && (A2 == a()); + +test bool matchSet73() + = ({DATA A3, s({A3, b(), *DATA SX1}), *SX1} := {a(), s({a(), b(), c()}), c()}) + && ((A3 == a()) && (SX1 == {c()}) || (A3 == c()) && (SX1 == {a()})); + +test bool matchSet74() + = ({DATA A4, s({A4, b(), *DATA SX2}), *SX2} := {s({a(), b(), c()}), a(), c()}) + && ((A4 == a()) && (SX2 == {c()}) || (A4 == c()) && (SX2 == {a()})); +test bool matchSet75() + = ({DATA A5, s({A5, b(), *DATA SX3}), *SX3} := {c(), s({a(), b(), c()}), a()}) + && ((A5 == a()) && (SX3 == {c()}) || (A5 == c()) && (SX3 == {a()})); + +test bool matchSet76() + = ({DATA A6, s({A6, b(), *DATA SX4}), *SX4} !:= {d(), s({a(), b(), c()}), a()}); +test bool matchSet77() + = ({DATA A7, s({A7, b(), *DATA SX5}), *SX5} !:= {c(), s({a(), b(), c()}), d()}); + +test bool matchSet78() + = ({DATA A8, s({A8, b()})} := {s({a(), b()}), a()}) && (A8 == a()); +test bool matchSet79() = ({s({DATA A9, b()}), A9} := {s({a(), b()}), a()}); +test bool matchSet80() + = ({s({DATA A9, b()}), A9} := {s({a(), b()}), a()}) && (A9 == a()); +test bool matchSet81() + = ({s({DATA A10, b(), *DATA SX6}), A10, *SX6} := {c(), s({a(), b(), c()}), a()}) + && ((A10 == a()) && (SX6 == {c()}) || (A10 == c()) && (SX6 == {a()})); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/PatternSet1.rsc| +@license{ + Copyright (c) 2009-2020 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module lang::rascal::tests::functionality::PatternSet1 + +public int ModVar42 = 42 ; +public int ModVar44 = 44 ; +public set[int] ModVarSet_41_42_43 = {41, 42, 43} ; + +test bool matchModuleVar1() = ModVar42 := 42; +test bool matchModuleVar3() = ModVarSet_41_42_43 := ModVarSet_41_42_43; + +// matchSet1 +test bool matchSet1() = {} := {}; +test bool matchSet2() = {1} := {1}; +test bool matchSet3() = {1, 2} := {1, 2}; + +test bool matchSet4() = {int _} := {1}; +test bool matchSet5() = {int _, int _} := {1, 2}; + +test bool matchSet6() = {_} := {1}; +test bool matchSet7() = {_, _} := {1, 2}; +test bool matchSet8() = !({_} := {1, 2}); +test bool matchSet9() = !({_, _} := {1}); + +test bool matchSet10() = !({_, _} := {1, 2, 3}); + +test bool matchSet11() = !({_, _, _} := {1, 2}); + +test bool matchSet12() = !({} := {1}); +test bool matchSet13() = !({1} := {2}); +test bool matchSet14() = !({1, 2} := {1, 3}); + +test bool matchSet15() = {*int X} := {} && X == {}; + +test bool matchSet16() = {*int X} := {1} && X == {1}; +test bool matchSet17() = {*int X} := {1, 2} && X == {1, 2}; + +test bool matchSet18() = {*Y} := {1, 2} && Y == {1, 2}; + +// TODO: Test related to + multivariables are commented out since they are not yet supported by the Rascal syntax +// test bool assertTrue() = {Y+} := {1,2} && Y == {1,2}; +test bool matchSet19() = {*int _} := {1, 2}; +test bool matchSet20() = {*_} := {1, 2}; + +// test bool matchSet() = {_+} := {1,2}; +test bool matchSet21() = ({int N, 2, N} := {1, 2}) && (N == 1); + +test bool matchSet22() = !(({int N, 2, N} := {1, 2, 3})); +bool assertFalse3() = ({int N, 2, N} := {1, 2, "a"}); + +test bool matchSet23() { + int N = 3; + return {N, 2, 1} := {1, 2, 3}; +} +test bool matchSet24() { + set[int] S = {3}; + return {*S, 2, 1} := {1, 2, 3}; +} +test bool matchSet25() { + set[int] S = {2, 3}; + return {*S, 1} := {1, 2, 3}; +} + +test bool matchSet26() = {1, *int X, 2} := {1, 2} && X == {}; +test bool matchSet27() = {1, *X, 2} := {1, 2} && X == {}; +test bool matchSet28() = {1, *_, 2} := {1, 2}; + +// test bool matchSet() = !({ {1, X+, 2} := {1,2}); +// test bool matchSet() = !({ {1, _+, 2} := {1,2};}) _+ does not exist yet +test bool matchSet29() = {1, *X, 2} := {1, 2} && X == {}; +test bool matchSet30() = !({1, *_, 2} := {1, 3}); +test bool matchSet31() = !({1, *_, 2} := {1, 3}); + +test bool matchSet32() = {1, *int X, 2} := {1, 2, 3} && X == {3}; +test bool matchSet33() = {1, *X, 2} := {1, 2, 3} && X == {3}; + +// test bool matchSet() = {1, X+, 2} := {1,2,3} && X == {3}; +test bool matchSet34() = {1, *_, 2} := {1, 2, 3}; + +// test bool matchSet() = {1, _+, 2} := {1,2,3}; +test bool matchSet35() = {1, *int X, 2} := {1, 2, 3, 4} && X == {3, 4}; + +test bool matchSet36() = {*int X, *int Y} := {} && X == {} && Y == {}; +test bool matchSet37() = {1, *int X, *int Y} := {1} && X == {} && Y == {}; +test bool matchSet38() = {*int X, 1, *int Y} := {1} && X == {} && Y == {}; +test bool matchSet39() = {*int X, *int Y, 1} := {1} && X == {} && Y == {}; + +test bool matchSet40() = !({*int _, *int _, 1} := {2}); +test bool matchSet41() = !({*_, *_, 1} := {2}); + +test bool matchSet42() + = {*int X, *int Y} := {1} && ((X == {} && Y == {1}) || (X == {1} && Y == {})); +/* added parentheses */test bool matchSet43() + = {*X, *Y} := {1} && ((X == {} && Y == {1}) || (X == {1} && Y == {})); + +/* added parentheses */test bool matchSet44() = {*int X, *int Y, *int Z} := {} && X == {} && Y == {} && Z == {}; +test bool matchSet45() = {*X, *Y, *Z} := {} && X == {} && Y == {} && Z == {}; +test bool matchSet46() + = {*int X, *int Y, *int Z} := {1} + && ((X == {1} && Y == {} && Z == {}) + || + (X == {} && Y == {1} && Z == {}) + || + (X == {} && Y == {} && Z == {1})); +/* added parentheses */test bool matchSet47() + = {*X, *Y, *Z} := {1} + && ((X == {1} && Y == {} && Z == {}) + || + (X == {} && Y == {1} && Z == {}) + || + (X == {} && Y == {} && Z == {1})); + +/* added parentheses */test bool matchSet48() = {int X, *int Y} := {1} && X == 1 && Y == {}; +test bool matchSet49() = {*int X, int Y} := {1} && X == {} && Y == 1; +test bool matchSet50() = {*X, int Y} := {1} && X == {} && Y == 1; + +// test bool matchSet() = !({ {X+, int Y} := {1};}) +test bool matchSet51() = {*int _, int _} := {1}; +test bool matchSet52() = {*_, int _} := {1}; +test bool matchSet53() = {*_, _} := {1}; + +// test bool matchSet() = !({_+, _} := {1}); +test bool matchSet54() + = {*int X, int Y} := {1, 2} && ((X == {1} && Y == 2) || (X == {2} && Y == 1)); +/* added parentheses */test bool matchSet55() + = {*X, int Y} := {1, 2} && ((X == {1} && Y == 2) || (X == {2} && Y == 1)); + +/* added parentheses */test bool matchSet56() + = {*int X, int Y} := {1, 2} && ((X == {1} && Y == 2) || (X == {2} && Y == 1)); +/* added parentheses */test bool matchSet57() + = {*int X, *real Y} := {1, 5.5, 2, 6.5} && (X == {1, 2} && Y == {5.5, 6.5}); +test bool matchSet58() + = {*X, *Y} := {1, 5.5, 2, 6.5} + && (X == {1, 5.5, 2, 6.5} && Y == {} || X == {} && Y == {1, 5.5, 2, 6.5}); + +test bool matchSet59() { + set[int] x = {}; + return {} := x; +} + +test bool matchSet60() { + res = {}; + for ({*int a, *int b, *int c} := {1, 2, 3, 4, 5}) { + res = res + {{a, b, c}}; + } + return + res == {{{}, {5, 1, 3, 2, 4}}, + {{}, {4}, {5, 1, 3, 2}}, + {{}, {2}, {5, 1, 3, 4}}, + {{}, {5, 1, 3}, {2, 4}}, + {{5, 1, 3}, {2}, {4}}, + {{}, {5, 1, 2, 4}, {3}}, + {{}, {5, 1, 2}, {3, 4}}, + {{3}, {4}, {5, 1, 2}}, + {{}, {3, 2}, {5, 1, 4}}, + {{3}, {2}, {5, 1, 4}}, + {{}, {3, 2, 4}, {5, 1}}, + {{5, 1}, {4}, {3, 2}}, + {{5, 1}, {2}, {3, 4}}, + {{2, 4}, {5, 1}, {3}}, + {{}, {1}, {5, 3, 2, 4}}, + {{}, {1, 4}, {5, 3, 2}}, + {{1}, {4}, {5, 3, 2}}, + {{}, {1, 2}, {5, 3, 4}}, + {{1}, {2}, {5, 3, 4}}, + {{}, {1, 2, 4}, {5, 3}}, + {{5, 3}, {4}, {1, 2}}, + {{5, 3}, {2}, {1, 4}}, + {{1}, {5, 3}, {2, 4}}, + {{}, {5, 2, 4}, {1, 3}}, + {{5, 2, 4}, {1}, {3}}, + {{}, {5, 2}, {1, 3, 4}}, + {{1, 3}, {4}, {5, 2}}, + {{3}, {5, 2}, {1, 4}}, + {{1}, {5, 2}, {3, 4}}, + {{}, {5, 4}, {1, 3, 2}}, + {{1, 3}, {2}, {5, 4}}, + {{3}, {5, 4}, {1, 2}}, + {{1}, {5, 4}, {3, 2}}, + {{}, {5}, {1, 3, 2, 4}}, + {{5}, {4}, {1, 3, 2}}, + {{5}, {2}, {1, 3, 4}}, + {{5}, {1, 3}, {2, 4}}, + {{5}, {1, 2, 4}, {3}}, + {{5}, {1, 2}, {3, 4}}, + {{5}, {1, 4}, {3, 2}}, + {{5}, {1}, {3, 2, 4}}}; +} + +test bool matchSet61() { + res = {}; + for ({6, *int a, int _, *int b, int _, 2, *int c} := {1, 2, 3, 4, 5, 6, 7}) { + res = res + {{a, b, c}}; + } + return + res == {{{}, {5, 7, 1}}, + {{}, {5, 7}, {1}}, + {{}, {7}, {5, 1}}, + {{}, {5}, {7, 1}}, + {{5}, {7}, {1}}, + {{}, {5, 7, 3}}, + {{}, {5, 7}, {3}}, + {{}, {7}, {5, 3}}, + {{}, {5}, {7, 3}}, + {{5}, {7}, {3}}, + {{}, {5, 1, 3}}, + {{}, {5, 1}, {3}}, + {{}, {1}, {5, 3}}, + {{}, {5}, {1, 3}}, + {{5}, {1}, {3}}, + {{}, {7, 1, 3}}, + {{}, {3}, {7, 1}}, + {{}, {1}, {7, 3}}, + {{}, {7}, {1, 3}}, + {{7}, {1}, {3}}, + {{}, {5, 7, 4}}, + {{}, {5, 7}, {4}}, + {{}, {7}, {5, 4}}, + {{}, {5}, {7, 4}}, + {{5}, {7}, {4}}, + {{}, {5, 1, 4}}, + {{}, {5, 1}, {4}}, + {{}, {1}, {5, 4}}, + {{}, {5}, {1, 4}}, + {{5}, {1}, {4}}, + {{}, {7, 1, 4}}, + {{}, {7, 1}, {4}}, + {{}, {1}, {7, 4}}, + {{}, {7}, {1, 4}}, + {{7}, {1}, {4}}, + {{}, {5, 3, 4}}, + {{}, {5, 3}, {4}}, + {{}, {3}, {5, 4}}, + {{}, {5}, {3, 4}}, + {{5}, {3}, {4}}, + {{}, {7, 3, 4}}, + {{}, {7, 3}, {4}}, + {{}, {3}, {7, 4}}, + {{}, {7}, {3, 4}}, + {{7}, {3}, {4}}, + {{}, {1, 3, 4}}, + {{}, {1, 3}, {4}}, + {{}, {3}, {1, 4}}, + {{}, {1}, {3, 4}}, + {{1}, {3}, {4}}}; +} + +test bool matchSetDynamic1() = {str s, int n} := {"a", 1}; +test bool matchSetDynamic2() = {str _, int _} := {"a", 1}; +test bool matchSetDynamic3() = {*str s, int n} := {"a", 1}; +test bool matchSetDynamic4() = {str s, *int n} := {"a", 1}; +test bool matchSetDynamic5() = {str _, int _} := {"a", 1}; +test bool matchSetDynamic6() = {*str _, int _} := {"a", 1}; +test bool matchSetDynamic7() = {str _, *int _} := {"a", 1}; + +test bool matchSetDynamicNoMatch1() = {str s, int n} !:= {"a", true}; +test bool matchSetDynamicNoMatch2() = {str _, int _} !:= {"a", true}; +test bool matchSetDynamicNoMatch3() = {*str s, int n} !:= {"a", true}; +test bool matchSetDynamicNoMatch4() = {str s, *int n} !:= {"a", true}; +test bool matchSetDynamicNoMatch5() = {str _, int _} !:= {"a", true}; +test bool matchSetDynamicNoMatch6() = {*str _, int _} !:= {"a", true}; +test bool matchSetDynamicNoMatch7() = {str _, *int _} !:= {"a", true}; + +test bool matchSetModuleVar1() = {ModVar42} := {42}; +test bool matchSetModuleVar2() + = {*ModVarSet_41_42_43} := ModVarSet_41_42_43; +test bool matchSetModuleVar3() + = {ModVar44, *ModVarSet_41_42_43} := {ModVar44, *ModVarSet_41_42_43}; +@ignoreInterpreter{Seems to be a bug in the interpreter} +test bool matchSetModuleVar4() + = {ModVar44, ModVarSet_41_42_43} := {ModVar44, ModVarSet_41_42_43}; + +// matchNestedSet +test bool matchNestedSet1() = !({} := {{2}}); + +test bool matchNestedSet3() = {} := {}; +test bool matchNestedSet4() = {{1}} := {{1}}; +test bool matchNestedSet5() = {{1, 2}} := {{1, 2}}; + +test bool matchNestedSet6() = !({{1}} := {{2}}); +test bool matchNestedSet7() = !({{1, 2}} := {{1, 2, 3}}); + +test bool matchNestedSet8() = {*set[int] _} := {}; + +test bool matchNestedSet9() = {*set[int] _} := {{1}}; +test bool matchNestedSet10() = {*set[int] _} := {{1, 2}}; + +test bool matchNestedSet11() + = ({{1}, *set[int] L, {6, 7, 8}} := {{1}, {2, 3}, {4, 5}, {6, 7, 8}}) + && (L == {{2, 3}, {4, 5}}); +test bool matchNestedSet12() + = !(({{1}, *set[int] L, {6, 7, 8}} := {{1}, {2, 3}, {4, 5}, {8}}) && (L == {{2, 3}, {4, 5}})); + +@IgnoreInterpreter{ +TBD +} +test bool matchNestedSet13() + = ({{1}, *set[int] L, {6, 7, 8}, *L} := {{1}, {2, 3}, {4, 5}, {6, 7, 8}, {2, 3}, {4, 5}}) + && (L == {{2, 3}, {4, 5}}); + +test bool matchNestedSet14() { + if ({*set[int] S} := {{1, 2}} && S == {{1, 2, 3}}) { + return false; + } + else { + return true; + } +} + +// matchSetMultiVars +test bool matchSetMultiVars1() = {1, *S, 4, 5} := {1, 2, 3, 4, 5} && S == {2, 3}; +test bool matchSetMultiVars2() = {1, *_, 4, 5} := {1, 2, 3, 4, 5}; + +// matchSetSpliceVars +test bool matchSetSpliceVars1() = {1, *S, 4, 5} := {1, 2, 3, 4, 5} && S == {2, 3}; +test bool matchSetSpliceVars2() = {1, *int S, 4, 5} := {1, 2, 3, 4, 5} && S == {2, 3}; +test bool matchSetSpliceVars3() = {1, *_, 4, 5} := {1, 2, 3, 4, 5}; +test bool matchSetSpliceVars4() = {1, *int _, 4, 5} := {1, 2, 3, 4, 5}; + +@ignoreCompiler{To be investigated} +test bool matchSetSpliceVarInOr() + = {1, 2, 3} == (({*int x} := {1, 2, 3} || {*int x} := {10, 20, 30}) ? x : {}); + +@ignoreCompiler{To be investigated} +test bool matchTypedSetVarInOr() + = 1 == (({int x: 1} := {1} || {int x: 10} := {10}) ? x : -1); + +// match set of tuples +test bool matchSetTuples1() = {<1, 2, 3 >} := {<1, 2, 3 >}; +test bool matchSetTuples2() = {<1, int n, 3 >} := {<1, 2, 3 >}; +test bool matchSetTuples3() = {<1, int _, 3 >} := {<1, 2, 3 >}; +test bool matchSetTuples4() = {, + *c } := {<1, 2 >, + <3, 4 >}; +test bool matchSetTuples5() = {, + *c } := {<1, 2 >, + <3, 3 >}; +test bool matchSetTuples6() = {<int a, a >, + *c } := {<1, 2 >, + <3, 3 >}; +test bool matchSetTuples7() = {<int a, int b >, + , + *c } := {<1, 2 >, + <2, 2 >, + <3, 4 >}; + +test bool matchSetTuples8() { + if ({<1, int n, 3 >} := {<1, 2, 3 >} && n == -2) { + return false; + } + else { + return true; + } +} + +// match set of lists +test bool matchSetLists1() = {[1, 2, 3]} := {[1, 2, 3]}; +test bool matchSetLists2() = {[1, int n, 3]} := {[1, 2, 3]}; +test bool matchSetLists3() = {[1, int _, 3]} := {[1, 2, 3]}; +test bool matchSetLists4() = {[a, b], *c} := {[1, 2], [3, 4]}; +test bool matchSetLists5() = {[a, a], *c} := {[1, 2], [3, 3]}; +test bool matchSetLists6() = {[int a, a], *c} := {[1, 2], [3, 3]}; +test bool matchSetLists7() = {[int a, int b], [b, b], *c} := {[1, 2], [2, 2], [3, 4]}; + +@ignoreCompiler{To be investigated} +test bool matchSetLists8() { + if ({[1, int n, 3]} := {[1, 2, 3]} && n == -2) { + return false; + } + else { + return true; + } +} + +// match set of ADTs +data D + = d(int x, int y) + | d(int x, int y, int z) + ; + +test bool matchSetADTs1() = {d(1, 2, 3)} := {d(1, 2, 3)}; +test bool matchSetADTs2() = {d(1, int n, 3)} := {d(1, 2, 3)}; +test bool matchSetADTs3() = {d(1, int _, 3)} := {d(1, 2, 3)}; +test bool matchSetADTs4() = {d(a, b), *c} := {d(1, 2), d(3, 4)}; +test bool matchSetADTs5() = {d(a, a), *c} := {d(1, 2), d(3, 3)}; +test bool matchSetADTs6() = {d(int a, a), *c} := {d(1, 2), d(3, 3)}; +test bool matchSetADTs7() + = {d(int a, int b), d(b, b), *c} := {d(1, 2), d(2, 2), d(3, 4)}; + +// matchListSetVariableScopes +data PAIR + = a1() + | b1() + | c1() + | d1() + | pair(PAIR q1, PAIR q2) + | s1(set[PAIR] S) + | l1(list[PAIR] L) + ; + +test bool matchListSetVariableScopes1() + = {PAIR D, pair(D, b1())} := {pair(a1(), b1()), a1()} && D == a1(); +test bool matchListSetVariableScopes2() + = {PAIR D, pair(D, b1())} !:= {pair(a1(), b1()), c1()}; + +test bool matchListSetVariableScopes3() + = {pair(PAIR D, b1()), D} := {pair(a1(), b1()), a1()} && D == a1(); +test bool matchListSetVariableScopes4() + = {pair(PAIR D, b1()), D} !:= {pair(a1(), b1()), c1()}; + +test bool matchListSetVariableScopes5() + = {pair(s1(set[PAIR] S1), c1()), *S1} := + {pair(s1({a1(), b1()}), c1()), a1(), b1()} + && S1 == {a1(), b1()}; +test bool matchListSetVariableScopes6() + = {pair(s1(set[PAIR] S1), c1()), *S1} !:= + {pair(s1({a1(), b1()}), c1()), a1(), d1()}; + +test bool matchListSetVariableScopes7() { + list[PAIR] L1 = [a1(), b1()]; + return [*L1, c1()] := [a1(), b1(), c1()]; +} +test bool matchListSetVariableScopes8() { + list[PAIR] L1 = [a1(), b1()]; + return [*L1, c1()] !:= [a1(), d1(), c1()]; +} + +test bool matchListSetVariableScopes9() + = [pair(l1(list[PAIR] L1), c1()), *L1] := + [pair(l1([a1(), b1()]), c1()), a1(), b1()]; +test bool matchListSetVariableScopes10() + = [pair(l1(list[PAIR] L1), c1()), *L1] !:= + [pair(l1([a1(), b1()]), c1()), a1(), d1()]; + +test bool matchListSetVariableScopes11() + = [pair(PAIR L1, b1()), L1] := [pair(a1(), b1()), a1()]; +test bool matchListSetVariableScopes12() + = [pair(PAIR L1, b1()), L1] !:= [pair(a1(), b1()), d1()]; + +// matchSetExternalVar +// S is uninitialized on purpose +test bool matchSetExternalVar1() { + set[int] S; + return ({1, *S, 2} := {1, 2, 3} && S == {3}); +} + +data D = d(int n); + +test bool setNamedElem1() = {n: d(1)} := {d(1)} && n == d(1); +test bool setNamedElem2() = {_: d(1)} := {d(1)}; +test bool setNamedElem3() = {_n: d(2)} !:= {d(1)}; + +test bool setTypeNamedElem1() = {D n: d(1)} := {d(1)} && n == d(1); +test bool setTypeNamedElem2() = {D _: d(1)} := {d(1)}; +test bool setTypetNamedElem3() = {D _n: d(2)} !:= {d(1)}; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/PatternSet2.rsc| +module lang::rascal::tests::functionality::PatternSet2 + +data TYPESET + = SET(str name) + | SUBTYPES(TYPESET tset) + | INTERSECT(set[TYPESET] tsets) + ; + +// Anastassija's type constraint examples +bool testSimplifyA(TYPESET(TYPESET ts) simplify) + = simplify(INTERSECT({SUBTYPES(INTERSECT({})), SET("s1")})) == INTERSECT({SUBTYPES(INTERSECT({})), SET("s1")}); + +bool testSimplifyB(TYPESET(TYPESET ts) simplify) + = simplify(INTERSECT({SUBTYPES(INTERSECT({SET("s1")})), SET("s2")})) == INTERSECT({SUBTYPES(INTERSECT({SET("s1")})), SET("s2")}); + +bool testSimplifyC(TYPESET(TYPESET ts) simplify) + = simplify(INTERSECT({SUBTYPES(INTERSECT({SET("s1")})), SET("s1")})) == INTERSECT({SUBTYPES(INTERSECT({})), SET("s1")}); + +bool testSimplifyD(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT({SUBTYPES(INTERSECT({SET("s1")})), SET("s1"), SET("s2")}) + ) == INTERSECT({SUBTYPES(INTERSECT({})), SET("s1"), SET("s2")}); + +bool testSimplifyE(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT({SUBTYPES(INTERSECT({SET("s1")})), SET("s2"), SET("s1")}) + ) == INTERSECT({SUBTYPES(INTERSECT({})), SET("s2"), SET("s1")}); + +bool testSimplifyF(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT({SUBTYPES(INTERSECT({SET("s1"), SET("s2")})), SET("s3")}) + ) == INTERSECT({SUBTYPES(INTERSECT({SET("s1"), SET("s2")})), SET("s3")}); + +bool testSimplifyG(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SET("s2")})), SET("s3"), SET("s4")} + ) + ) == INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SET("s2")})), SET("s3"), SET("s4")} + ); + +bool testSimplifyH(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT({SUBTYPES(INTERSECT({SET("s1"), SET("s3")})), SET("s1")}) + ) == INTERSECT({SUBTYPES(INTERSECT({SET("s3")})), SET("s1")}); + +bool testSimplifyI(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT({SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), SET("s3")}) + ) == INTERSECT({SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), SET("s3")}); + +bool testSimplifyJ(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT({SUBTYPES(INTERSECT({SET("s70"), SET("s4")})), SET("s70")}) + ) == INTERSECT({SUBTYPES(INTERSECT({SET("s4")})), SET("s70")}); + +bool testSimplifyK(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SUBTYPES(SET("s3"))})), SET("s1")} + ) + ) == INTERSECT({SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), SET("s1")}); + +bool testSimplifyL(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SUBTYPES(SET("s3"))})), + SUBTYPES(SET("s2")), + SET("s1")} + ) + ) == INTERSECT( + {SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), + SUBTYPES(SET("s2")), + SET("s1")} + ); + +bool testSimplifyM(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SUBTYPES(SET("s2"))})), + SUBTYPES(SET("s2")), + SET("s1")} + ) + ) == INTERSECT({SUBTYPES(INTERSECT({})), SUBTYPES(SET("s2")), SET("s1")}); + +bool testSimplifyN(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT( + {SUBTYPES(INTERSECT({SUBTYPES(SET("s3")), SUBTYPES(SET("s2"))})), + SUBTYPES(SET("s2"))} + ) + ) == INTERSECT( + {SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), SUBTYPES(SET("s2"))} + ); + +bool testSimplifyO(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT( + {SUBTYPES( + INTERSECT({SET("s1"), SUBTYPES(SET("s2")), SUBTYPES(SET("s3"))}) + ), + SUBTYPES(SET("s2")), + SET("s1")} + ) + ) == INTERSECT( + {SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), + SUBTYPES(SET("s2")), + SET("s1")} + ); + +bool testSimplifyP(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SET("s2"), SET("s3"), SET("s5")})), + SET("s6"), + SET("s2"), + SET("s7"), + SET("s1")} + ) + ) == INTERSECT( + {SUBTYPES(INTERSECT({SET("s3"), SET("s5")})), + SET("s6"), + SET("s2"), + SET("s7"), + SET("s1")} + ); + +bool testSimplifyQ(TYPESET(TYPESET ts) simplify) + = simplify( + INTERSECT( + {SUBTYPES( + INTERSECT({SET("s1"), SUBTYPES(SET("s2")), SET("s3"), SET("s5")}) + ), + SET("s6"), + SUBTYPES(SET("s2")), + SET("s7"), + SET("s1"), + SET("s3")} + ) + ) == INTERSECT( + {SUBTYPES(INTERSECT({SET("s5")})), + SET("s6"), + SUBTYPES(SET("s2")), + SET("s7"), + SET("s1"), + SET("s3")} + ); + +// Version 1; with explicit simplification function, no non-linear constraints, fail to explore alternative matches +TYPESET simp1(TYPESET ts) { + for (INTERSECT( + {SUBTYPES(INTERSECT({TYPESET tset, *TYPESET rest})), TYPESET tset1, + *TYPESET rest1} + ) := ts) { + if (tset == tset1) + return simp1(INTERSECT({SUBTYPES(INTERSECT(rest)), tset1, *rest1})); + else + fail; + } + return ts; +} + +test bool testSimp1A() = testSimplifyA(simp1); +test bool testSimp1B() = testSimplifyB(simp1); +test bool testSimp1C() = testSimplifyC(simp1); +test bool testSimp1D() = testSimplifyD(simp1); +test bool testSimp1E() = testSimplifyE(simp1); +test bool testSimp1F() = testSimplifyF(simp1); +test bool testSimp1G() = testSimplifyG(simp1); +test bool testSimp1H() = testSimplifyH(simp1); +test bool testSimp1I() = testSimplifyI(simp1); +test bool testSimp1J() = testSimplifyJ(simp1); +test bool testSimp1K() = testSimplifyK(simp1); +test bool testSimp1L() = testSimplifyL(simp1); +test bool testSimp1M() = testSimplifyM(simp1); +test bool testSimp1N() = testSimplifyN(simp1); +test bool testSimp1O() = testSimplifyO(simp1); +test bool testSimp1P() = testSimplifyP(simp1); +test bool testSimp1Q() = testSimplifyQ(simp1); + +// Version 2; with explicit simplification function, and non-linear constraints (tset) +public TYPESET simp2(TYPESET ts) { + if (INTERSECT( + {SUBTYPES(INTERSECT({TYPESET tset, *TYPESET rest})), tset, *TYPESET rest1} + ) := ts) { + return simp2(INTERSECT({SUBTYPES(INTERSECT(rest)), tset, *rest1})); + } + return ts; +} + +test bool testSimp2A() = testSimplifyA(simp2); +test bool testSimp2B() = testSimplifyB(simp2); +test bool testSimp2C() = testSimplifyC(simp2); +test bool testSimp2D() = testSimplifyD(simp2); +test bool testSimp2E() = testSimplifyE(simp2); +test bool testSimp2F() = testSimplifyF(simp2); +test bool testSimp2G() = testSimplifyG(simp2); +test bool testSimp2H() = testSimplifyH(simp2); +test bool testSimp2I() = testSimplifyI(simp2); +test bool testSimp2J() = testSimplifyJ(simp2); +test bool testSimp2K() = testSimplifyK(simp2); +test bool testSimp2L() = testSimplifyL(simp2); +test bool testSimp2M() = testSimplifyM(simp2); +test bool testSimp2N() = testSimplifyN(simp2); +test bool testSimp2O() = testSimplifyO(simp2); +test bool testSimp2P() = testSimplifyP(simp2); +test bool testSimp2Q() = testSimplifyQ(simp2); + +// Version 3; with explicit simplification function, non-linear constraints (tset) and nested simp call +public TYPESET simp3(TYPESET ts) { + if (INTERSECT( + {SUBTYPES(INTERSECT({TYPESET tset, *TYPESET rest})), tset, *TYPESET rest1} + ) := ts) { + return + simp3(INTERSECT({SUBTYPES(simp3(INTERSECT(rest))), tset, *rest1})); + } + return ts; +} + +test bool testSimp3A() = testSimplifyA(simp3); +test bool testSimp3B() = testSimplifyB(simp3); +test bool testSimp3C() = testSimplifyC(simp3); +test bool testSimp3D() = testSimplifyD(simp3); +test bool testSimp3E() = testSimplifyE(simp3); +test bool testSimp3F() = testSimplifyF(simp3); +test bool testSimp3G() = testSimplifyG(simp3); +test bool testSimp3H() = testSimplifyH(simp3); +test bool testSimp3I() = testSimplifyI(simp3); +test bool testSimp3J() = testSimplifyJ(simp3); +test bool testSimp3K() = testSimplifyK(simp3); +test bool testSimp3L() = testSimplifyL(simp3); +test bool testSimp3M() = testSimplifyM(simp3); +test bool testSimp3N() = testSimplifyN(simp3); +test bool testSimp3O() = testSimplifyO(simp3); +test bool testSimp3P() = testSimplifyP(simp3); +test bool testSimp3Q() = testSimplifyQ(simp3); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/PatternSet3.rsc| +module lang::rascal::tests::functionality::PatternSet3 + +import lang::rascal::tests::functionality::PatternSet2; + +// Anastassija's type constraint examples +// Version 4; with overloaded constructor INTERSECT , and non-linear constraints (tset) +public +TYPESET INTERSECT( + {SUBTYPES(INTERSECT({TYPESET tset, *TYPESET rest})), tset, *TYPESET rest1} +) { + return INTERSECT({SUBTYPES(INTERSECT(rest)), tset, *rest1}); +} + +test bool testSimplifyA() + = INTERSECT({SUBTYPES(INTERSECT({})), SET("s1")}) == INTERSECT({SUBTYPES(INTERSECT({})), SET("s1")}); + +test bool testSimplifyB() + = INTERSECT({SUBTYPES(INTERSECT({SET("s1")})), SET("s2")}) == INTERSECT({SUBTYPES(INTERSECT({SET("s1")})), SET("s2")}); + +test bool testSimplifyC() + = INTERSECT({SUBTYPES(INTERSECT({SET("s1")})), SET("s1")}) == INTERSECT({SUBTYPES(INTERSECT({})), SET("s1")}); + +test bool testSimplifyD() + = INTERSECT({SUBTYPES(INTERSECT({SET("s1")})), SET("s1"), SET("s2")}) == INTERSECT({SUBTYPES(INTERSECT({})), SET("s1"), SET("s2")}); + +test bool testSimplifyE() + = INTERSECT({SUBTYPES(INTERSECT({SET("s1")})), SET("s2"), SET("s1")}) == INTERSECT({SUBTYPES(INTERSECT({})), SET("s2"), SET("s1")}); + +test bool testSimplifyF() + = INTERSECT({SUBTYPES(INTERSECT({SET("s1"), SET("s2")})), SET("s3")}) == INTERSECT({SUBTYPES(INTERSECT({SET("s1"), SET("s2")})), SET("s3")}); + +test bool testSimplifyG() + = INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SET("s2")})), SET("s3"), SET("s4")} + ) == INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SET("s2")})), SET("s3"), SET("s4")} + ); + +test bool testSimplifyH() + = INTERSECT({SUBTYPES(INTERSECT({SET("s1"), SET("s3")})), SET("s1")}) == INTERSECT({SUBTYPES(INTERSECT({SET("s3")})), SET("s1")}); + +test bool testSimplifyI() + = INTERSECT({SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), SET("s3")}) == INTERSECT({SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), SET("s3")}); + +test bool testSimplifyJ() + = INTERSECT({SUBTYPES(INTERSECT({SET("s70"), SET("s4")})), SET("s70")}) == INTERSECT({SUBTYPES(INTERSECT({SET("s4")})), SET("s70")}); + +test bool testSimplifyK() + = INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SUBTYPES(SET("s3"))})), SET("s1")} + ) == INTERSECT({SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), SET("s1")}); + +test bool testSimplifyL() + = INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SUBTYPES(SET("s3"))})), + SUBTYPES(SET("s2")), + SET("s1")} + ) == INTERSECT( + {SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), + SUBTYPES(SET("s2")), + SET("s1")} + ); + +test bool testSimplifyM() + = INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SUBTYPES(SET("s2"))})), + SUBTYPES(SET("s2")), + SET("s1")} + ) == INTERSECT({SUBTYPES(INTERSECT({})), SUBTYPES(SET("s2")), SET("s1")}); + +test bool testSimplifyN() + = INTERSECT( + {SUBTYPES(INTERSECT({SUBTYPES(SET("s3")), SUBTYPES(SET("s2"))})), + SUBTYPES(SET("s2"))} + ) == INTERSECT( + {SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), SUBTYPES(SET("s2"))} + ); + +test bool testSimplifyO() + = INTERSECT( + {SUBTYPES( + INTERSECT({SET("s1"), SUBTYPES(SET("s2")), SUBTYPES(SET("s3"))}) + ), + SUBTYPES(SET("s2")), + SET("s1")} + ) == INTERSECT( + {SUBTYPES(INTERSECT({SUBTYPES(SET("s3"))})), + SUBTYPES(SET("s2")), + SET("s1")} + ); + +test bool testSimplifyP() + = INTERSECT( + {SUBTYPES(INTERSECT({SET("s1"), SET("s2"), SET("s3"), SET("s5")})), + SET("s6"), + SET("s2"), + SET("s7"), + SET("s1")} + ) == INTERSECT( + {SUBTYPES(INTERSECT({SET("s3"), SET("s5")})), + SET("s6"), + SET("s2"), + SET("s7"), + SET("s1")} + ); + +test bool testSimplifyQ() + = INTERSECT( + {SUBTYPES( + INTERSECT({SET("s1"), SUBTYPES(SET("s2")), SET("s3"), SET("s5")}) + ), + SET("s6"), + SUBTYPES(SET("s2")), + SET("s7"), + SET("s1"), + SET("s3")} + ) == INTERSECT( + {SUBTYPES(INTERSECT({SET("s5")})), + SET("s6"), + SUBTYPES(SET("s2")), + SET("s7"), + SET("s1"), + SET("s3")} + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/PatternTuple.rsc| +module lang::rascal::tests::functionality::PatternTuple + +// matchTuple +test bool matchTuple1a() = <1> := <1>; +test bool matchTuple1b() = <2> !:= <1>; +test bool matchTuple1c() = !(<2> := <1>); +test bool matchTuple1d() = !<2> := <1>; + +test bool matchTuple2a() = <1, "abc"> := <1, "abc">; +test bool matchTuple2b() = <1, "abc"> !:= <1, "def">; +test bool matchTuple2c() = !(<1, "abc"> := <1, "def">); +test bool matchTuple2d() = !<1, "abc"> := <1, "def">; + +test bool matchTuple2e() = <_, "abc"> := <1, "abc">; +test bool matchTuple2f() = <1, _> := <1, "abc">; +test bool matchTuple2g() = <_, _> := <1, "abc">; + +// T is not initialized on purpose here +test bool matchTupleExternalVar1() { + tuple[int, int] T; + return T := <1, 2> && T[0] == 1 && T[1] == 2; +} + +data D + = d1() + | d2() + ; + +test bool matchTupleADT1a() = := ; +test bool matchTupleADT1b() = !:= ; +test bool matchTupleADT1c() = !( := ); +@ignoreInterpreter{to be determined} +test bool matchTupleADT1d() = ! := ; + +test bool matchTupleList1a() = <[]> := <[]>; +test bool matchTupleList1b() = <[1]> := <[1]>; +test bool matchTupleList1c() = <[1]> !:= <[2]>; +test bool matchTupleList1d() = !(<[1]> := <[2]>); +test bool matchTupleList1e() = !<[1]> := <[2]>; + +test bool matchTupleList2a() = <[1, int _, 3]> := <[1, 2, 3]>; +test bool matchTupleList2b() = <[*int _, *int _, 3]> := <[1, 2, 3]>; +test bool matchTupleList2c() = <[*int x, *int _, 3]> := <[1, 2, 3]> && x == [1, 2]; +test bool matchTupleList2d() = <[*int _, *int y, 3]> := <[1, 2, 3]> && y == [1, 2]; + +test bool matchTupleSet1a() = <{}> := <{}>; +test bool matchTupleSet1b() = <{1}> := <{1}>; +test bool matchTupleSet1c() = <{1}> !:= <{2}>; +test bool matchTupleSet1d() = !(<{1}> := <{2}>); +@ignoreInterpreter{to be determined} +test bool matchTupleSet1e() = !<{1}> := <{2}>; + +test bool matchTupleSet2a() = <{1, int _, 3}> := <{1, 2, 3}>; +test bool matchTupleSet2b() = <{*int _, *int _, 3}> := <{1, 2, 3}>; +test bool matchTupleSet2c() = <{*int x, *int _, 3}> := <{1, 2, 3}> && x == {1, 2}; +test bool matchTupleSet2d() = <{*int _, *int y, 3}> := <{1, 2, 3}> && y == {1, 2}; + +test bool matchTupleSet3a() = <{int x, *int _}> := <{1, 2, 3}> && x == 1; +test bool matchTupleSet3b() = <{int x, *int _}> := <{1, 2, 3}> && x == 2; +test bool matchTupleSet3c() = <{int x, *int _}> := <{1, 2, 3}> && x == 3; + +test bool matchTupleSet4a() + = <{int x, *int _}, {int p, *int _}> := <{1, 2}, {10, 20}> && x == 1 && p == 10; +test bool matchTupleSet4b() + = <{int x, *int _}, {int p, *int _}> := <{1, 2}, {10, 20}> && x == 1 && p == 20; +test bool matchTupleSet4c() + = <{int x, *int _}, {int p, *int _}> := <{1, 2}, {10, 20}> && x == 2 && p == 10; +test bool matchTupleSet4d() + = <{int x, *int _}, {int p, *int _}> := <{1, 2}, {10, 20}> && x == 2 && p == 20; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Projection.rsc| +module lang::rascal::tests::functionality::Projection + +/******************************************************************************* + * Copyright (c) 2009-2015 CWI + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + + * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI + * * Bert Lisser - Bert.Lisser@cwi.nl - CWI +*******************************************************************************/// nonEmpty +test bool nonEmpty1() = {<1, 2 >}<0> == {1}; + +test bool nonEmpty2() = {<1, 2 >}<1> == {2}; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Range.rsc| +module lang::rascal::tests::functionality::Range + +/******************************************************************************* + * Copyright (c) 2009-2015 CWI + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + + * * Tijs van der Storm - Tijs.van.der.Storm@cwi.nl + * * Paul Klint - Paul.Klint@cwi.nl - CWI + *******************************************************************************/// rangeInt +test bool rangeInt1() = [1..1] == []; +test bool rangeInt2() = [1..-1] == [1, 0]; +test bool rangeInt3() = [1..0] == [1]; +test bool rangeInt4() = [1..2] == [1]; +test bool rangeInt5() = [1..5] == [1, 2, 3, 4]; +test bool rangeInt6() = [1,3.. 10] == [1, 3, 5, 7, 9]; +test bool rangeInt7() = [1,-2.. -5] == [1, -2]; + +// rangeNum +test bool rangeNum1() { + { + num n1 = 1; + return [n1..n1] == []; + } +} +test bool rangeNum2() { + { + num n1 = 1; + num n2 = 2; + return [n1..n2] == [1]; + } +} +test bool rangeNum3() { + { + num n1 = 1; + num n5 = 5; + return [n1..n5] == [1, 2, 3, 4]; + } +} +test bool rangeNum4() { + { + num n1 = 1; + num n3 = 3; + num n10 = 10; + return [n1,n3.. n10] == [1, 3, 5, 7, 9]; + } +} + +// rangeReals +test bool rangeReals1() = [1.0.. .1] == [1.0]; +test bool rangeReals2() = [1.0..1.0] == []; +test bool rangeReals3() = [1.0..5.0] == [1.0, 2.0, 3.0, 4.0]; +test bool rangeReals4() = [1.0..5.5] == [1.0, 2.0, 3.0, 4.0, 5.0]; +test bool rangeReals5() = [1.0,1.5.. 2.0] == [1.0, 1.5]; +test bool rangeReals6() = [1.0,-2.0.. -10.0] == [1.0, -2.0, -5.0, -8.0]; + +// rangeMixed +test bool rangeMixed1() = [1..1.0] == []; + +@ignoreCompiler{Different behaviour} +test bool rangeMixed2a() = [1.. .1] == [1.]; +@ignoreInterpreter{Different behaviour} +test bool rangeMixed2b() = [1.. .1] == [1.0]; + +@ignoreCompiler{Different behaviour} +test bool rangeMixed3a() = [1..5.0] == [1., 2.0, 3.0, 4.0]; +@ignoreInterpreter{Different behaviour} +test bool rangeMixed3b() = [1..5.0] == [1.0, 2.0, 3.0, 4.0]; + +@ignoreCompiler{Different behaviour} +test bool rangeMixed4a() = [1..5.5] == [1., 2.0, 3.0, 4.0, 5.0]; +@ignoreInterpreter{Different behaviour} +test bool rangeMixed4b() = [1..5.5] == [1.0, 2.0, 3.0, 4.0, 5.0]; + +@ignoreCompiler{Different behaviour} +test bool rangeMixed5a() = [1,1.5.. 2.0] == [1., 1.5]; +@ignoreInterpreter{Different behaviour} +test bool rangeMixed5b() = [1,1.5.. 2.0] == [1.0, 1.5]; + +test bool rangeMixed6() = [1,1.5.. 3] == [1, 1.5, 2.0, 2.5]; +test bool rangeMixed7() = [1.0,-2.. -10.0] == [1.0, -2.0, -5.0, -8.0]; + +alias nat = int; + +test bool aliased1() { + nat x = 0; + nat y = 3; + return [i | int i <- [x..y]] == [0..3]; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Reducer.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +module lang::rascal::tests::functionality::Reducer + +test bool testCount() = ( 0 | it + 1 | _ <- [1, 2, 3] ) == 3; + +test bool testMax() = ( 0 | x > it ? x : it | x <- [1, 2, 3] ) == 3; + +test bool testSum() = ( 0 | it + x | x <- [1, 2, 3] ) == 6; + +test bool testFlatMap() = ( {} | it + x | x <- {{1, 2}, {2, 3, 4}} ) == {1, 2, 3, 4}; + +int wordCount(list[str] lines) + = ( 0 | it + ( 0 | it + 1 | /\w+/ := line ) | str line <- lines ); + +test bool wordCount1() + = wordCount([ + "Andra moi ennepe,", "Mousa, polutropon,", "hos mala polla " + ]) == 8; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/RegExp.rsc| +@license{ + Copyright (c) 2009-2020 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module lang::rascal::tests::functionality::RegExp + +import Exception; + +// match +test bool match1() = /abc/ := "abc"; +test bool match2() = /def/ !:= "abc"; +test bool match3() = /def/ !:= "abc"; +test bool match4() = /[a-z]+/ := "abc"; +test bool match5() = /.*is.*/ := "Rascal is marvelous"; +test bool match6() = /@.*@/ := "@ abc @"; + +test bool match7() = (// := "abc") && (x == "abc"); +test bool match8() + = (/ifthenfi/ := "if a \> b then c fi") + && (tst == " a \> b ") + && (th == " c "); + +test bool match9() + = (/[Rr][Aa][Ss][Cc][Aa][Ll]/ := "RASCAL is marvelous") + && (l == "") + && (r == " is marvelous"); + +test bool match10() { + str x = "abc"; + return // := "abc"; +} +test bool match11() { + str x = "abc"; + int n = 3; + return // := "abc3"; +} + +test bool match12() = (/-/ := "abc-abc") && (x == "abc"); +test bool match13() = (/--/ := "abc-abc-abc") && (x == "abc"); +test bool match14() = /-/ !:= "abc-def"; + +test bool match15() = /\// := "/"; + +test bool match16() = // := "/" && x == "/"; +test bool match17() = // := "/" && x == "/"; + +/* NOTE: we no longer allow local shadowing of variables +test bool matchWithLocalVariable() { str x; return (// := "abc") && (x == "abc");} +test bool matchWithLocalVariable() { str x = "123"; return (// := "abc") && (x == "abc");} +test bool matchWithLocalVariable() { str x = "123"; return (// := "abc"); (x == "123");} + + +test bool matchWithLocalVariable() { int x; return (// := "abc") && (x == "abc");} +test bool matchWithLocalVariable() { int x = 123; return (// := "abc") && (x == "abc");} +test bool matchWithLocalVariable() { int x = 123; return (// := "abc"); (x == 123);} + +test bool nomatchWithLocalVariable() { str x = "123"; return (// !:= "abc" || x == "123");} +test bool nomatchWithLocalVariable() { str x = "123"; (// !:= "abc"); return (x == "123");} +*/test bool repeatedInterpolation() { + r = out :for (i <- [1, 2, 3]) + for (// := "332211") + append out : i; + return r == [1, 1, 2, 2, 3, 3]; +} + +// interpolating +test bool interpolation1() { + x = "."; + return (// !:= "a"); +} +test bool interpolation2() { + x = "."; + return // := "."; +} +test bool interpolation3() = /./ := "abc" && x == "bc"; +test bool interpolation4() = /^(a|b)*$/ := "ababab"; +test bool interpolation5() = /(a|b)/ := "acd" && x == "cd"; +test bool interpolation6() = /^(a|b)*$/ !:= "abacbab"; +test bool interpolation7() = /(.)/ := "abc" && x == "bc"; +test bool interpolation8() { + x = "("; + y = ")"; + return /./ !:= "a"; +} +test bool interpolation9() { + x = "("; + y = ")"; + return /./ := "(a)"; +} + +// escape +test bool escape1() = /\\/ := "\\"; +test bool escape2() = /a\\/ := "a\\"; +test bool escape3() = /\\b/ := "\\b"; +test bool escape4() = /a\\b/ := "a\\b"; + +test bool escape5() = /[\\]/ := "\\"; +test bool escape6() = /[\\ \<]/ := "\\"; +test bool escape7() = /[\\ \<]/ := "\<"; + +test bool escape8() = /a[\\ \<]/ := "a\\"; +test bool escape9() = /a[\\ \<]/ := "a\<"; +test bool escape10() = /[\\ \>]b/ := "\\b"; +test bool escape11() = /[\\ \>]b/ := "\>b"; +test bool escape12() = /a[\\ \>]b/ := "a\\b"; +test bool escape13() = /a[\\ \>]b/ := "a\>b"; + +// literalBracket +test bool literalBracket() = /\(/ := "("; + +// lotsofbrackets +test bool lotsofBracket1() = /()/ := "abc" && x == "abc"; +test bool lotsofBracket2() = /(())/ := "abc" && x == "abc"; +test bool lotsofBracket3() = /()/ := "abc" && x == "abc"; +test bool lotsofBracket4() = /()/ := "abc" && x == "abc"; + +//nogrouplookaheadandbehind +test bool nogrouplookaheadandbehind() = /(?s)a.c/ := "a\nc"; + +// InterpolateInPatternVarDecl +test bool interpolateIndPatternDeclSimple1() { + int n = 3; + return (/>/ := "3" && x == "3"); +} +test bool interpolateIndPatternDeclSimple2() { + real n = 3.5; + return (/>/ := "3.5" && x == "3.5"); +} +test bool interpolateIndPatternDeclSimple3() { + rat n = 2r3; + return (/>/ := "2r3" && x == "2r3"); +} + +test bool interpolateIndPatternDecl1() { + int n = 3; + return (/>/ := "33" && x == "3"); +} +test bool interpolateIndPatternDecl2() { + int n = 3; + return (/>/ := "a3" && x == "a3"); +} +test bool interpolateIndPatternDecl3() { + int n = 3; + return (/b>/ := "3b" && x == "3b"); +} +test bool interpolateIndPatternDecl4() { + int n = 3; + return (/b>/ := "a3b" && x == "a3b"); +} +test bool interpolateIndPatternDecl5() { + int n = 3; + return (/b>/ := "a3b" && x == "a3b"); +} +test bool interpolateIndPatternDecl6() { + int n = 3; + return (/bc>/ := "a3b3c" && x == "a3b3c"); +} +test bool interpolateIndPatternDecl7() { + int n = 3; + return (/bc>/ := "a3b3ca3b3c" && x == "a3b3c"); +} +test bool interpolateIndPatternDecl8() { + int n = 3; + return (/}>/ := "aaa" && x == "aaa"); +} +test bool interpolateIndPatternDecl9() { + str a = "a"; + int n = 3; + return (/{}>/ := "aaa" && x == "aaa"); +} +test bool interpolateIndPatternDecl10() { + str a = "abc"; + int n = 3; + return (/){}>/ := "abcabcabc" && x == "abcabcabc"); +} + +test bool interpolateIndPatternDecl11() { + return (// := "\\" && x == "\\"); +} +test bool interpolateIndPatternDecl12() { + return (/>/ := "\>" && x == "\>"); +} + +test bool interpolateIndPatternDecl13() { + return (// := "\<" && x == "\<"); +} +test bool interpolateIndPatternDecl14() { + int n = 3; + return (/>/ := "\< 3" && x == "\< 3"); +} +test bool interpolateIndPatternDecl15() { + int n = 3; + return (/\>>/ := "\< 3\>" && x == "\< 3\>"); +} + +// multipleMatches +test bool multipleMatches1() + = [ | // := "abcd"] == [<"a", "b" >, + <"c", "d" >]; +test bool multipleMatches2() { + int n = 3; + return [y | /abc}>/ := "abc111abc222abc333"] == ["111", "222", "333"]; +} +test bool multipleMatches3() + = [s | // := "abcdef"] == ["a", "b", "c", "d", "e", "f"]; + +/*TODO: add interpreter tests here*//* NOTE: we no longer allow local shadowing of variables +@Test +void matchWithExternalModuleVariable(){ +prepareModule("XX", "module XX str x = "abc";"); +runTestInSameEvaluator("import XX;"); +assertTrue(runTestInSameEvaluator("(// := "abc") && (x == "abc"); +} + +@Test +void nomatchWithExternalModuleVariable(){ +prepareModule("XX", "module XX str x = "abc";"); +runTestInSameEvaluator("import XX;"); +assertTrue(runTestInSameEvaluator("(// !:= "pqr") || (x == "abc"); +assertTrue(runTestInSameEvaluator("{(// !:= "pqr") ; (x == "abc");} +} + +@Test +void matchWithExternalModuleVariableOfWrongType(){ +prepareModule("XX", "module XX int x = 123;"); +assertTrue(runTestInSameEvaluator("(// := "abc") && (x == "abc"); +} +*/@ignoreCompiler{Remove-after-transtion-to-compiler: Different exception} +@expected{SyntaxError} +test bool RegExpSyntaxError1() = /[a-/ := "abc"; + +@ignoreInterpreter{Different exception} +@expected{RegExpSyntaxError} +test bool RegExpSyntaxError1() = /[a-/ := "abc"; + +// modifiers +test bool modifiers1() = /abc/i := "ABC"; +test bool modifiers2() = /abc/i := "ABC"; +test bool modifiers3() = /ab.*c/s := "ab\\nc"; +test bool modifiers4() = /ab.*c/si := "AB\\nc"; +test bool modifiers5() = /^ab.*c$/smd := "ab\\r\\nc"; + +// wordCount +test bool wordCount1() { + int cnt(str S) { + int count = 0; + while(/^\W*\w+/ := S) { + count = count + 1; + S = rest; + } + return count; + } + return cnt("abc def ghi") == 3; +} + +test bool wordCount2() { + int cnt(str S) { + int count = 0; + while(/^\W*\w+/ := S) { + count = count + 1; + S = rest; + } + return count; + } + return cnt("abc def ghi") == 3; +} + +// RegExp nested in other pattern +test bool regExpInList1() = [/abc/] := ["abc"]; +test bool regExpInList2() = [/abc/, /def/] := ["abc", "def"]; + +@ignore{Interpreter: "append statement without enclosing loop", compiler: "Parse error in concrete syntax"} +test bool regExpInSet1() = {/abc/} := {"abc"}; +@ignore{Interpreter: "append statement without enclosing loop", compiler: "Parse error in concrete syntax"} +test bool regExpInSet2() = {/abc/, /def/} := {"abc", "def"}; + +test bool regExpInTuple1() = := <"abc">; +test bool regExpInTuple2() = := <"abc", "def">; + +test bool regExpInNode1() = "f"(/abc/) := "f"("abc"); +test bool regExpInNode2() = "f"(/abc/, /def/) := "f"("abc", "def"); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Reification.rsc| +@contributor{Jurgen Vinju} +@contributor{Paul Klint} +module lang::rascal::tests::functionality::Reification + +import ParseTree; +import Set; + +data P + = prop(str name) + | and(P l, P r) + | or(P l, P r) + | not(P a) + | t() + | f() + | axiom( P mine = t()) + ; +data D[&T] = d1(&T fld); + +test bool reifyBool() = #bool.symbol == \bool(); +test bool reifyStr() = #str.symbol == \str(); +test bool reifyInt() = #int.symbol == \int(); +test bool reifyReal() = #real.symbol == \real(); +test bool reifyRat() = #rat.symbol == \rat(); +test bool reifyNum() = #num.symbol == \num(); +test bool reifyLoc() = #loc.symbol == \loc(); +test bool reifyDatetime() = #datetime.symbol == \datetime(); +test bool reifyNode() = #node.symbol == \node(); +test bool reifyVoid() = #void.symbol == \void(); +test bool reifyValue() = #value.symbol == \value(); +test bool reifyList() = #list[int].symbol == \list(\int()); +test bool reifySet() = #set[int].symbol == \set(\int()); + +@ignoreInterpreter{Not implemented} +@ignoreCompiler{Not implemented} +test bool reifyBag() = #bag[int].symbol == \bag(\int()); + +test bool reifyMap() = #map[int, str].symbol == \map(\int(), \str()); +test bool reifyMapWithLabels() + = #map[int k, str v].symbol == \map(label("k", \int()), label("v", \str())); + +test bool reifyFunction() = #int(int).symbol == \func ( + \int(), + [\int()], + [] + ); +test bool reifyFunctionWithLabel() + = #int(int a).symbol == \func ( + \int(), + [label("a", \int())], + [] + ); + +test bool reifyParameter() = #&T.symbol == \parameter("T", \value()); +test bool reifyParameterWithBound() + = #&T <: list[&U].symbol == \parameter("T", \list(\parameter("U", \value()))); + +test bool reifyTuple() = #tuple[int, str].symbol == \tuple([\int(), \str()]); +test bool reifyTupleWithLabels() + = #tuple[int a, str b].symbol == \tuple([\label("a", \int()), \label("b", \str())]); + +test bool reifyRel() + = #rel[int, int].symbol == \set(\tuple([\int(), \int()])); +test bool reifyRelWithLabels() + = #rel[int a, int b].symbol == \set(\tuple([label("a", \int()), label("b", \int())])); + +test bool reifyLrel() + = #lrel[int, int].symbol == \list(\tuple([\int(), \int()])); +test bool reifyLrelWithLabels() + = #lrel[int a, int b].symbol == \list(\tuple([label("a", \int()), label("b", \int())])); + +test bool reifyReified1() = #type[int].symbol == \reified(\int()); +test bool reifyReified2() = #type[P].symbol == \reified(\adt ( + "P", + [] + )); +test bool reifyReified3() + = #type[D[int]].symbol == \reified(\adt ( + "D", + [\int()] + )); + +test bool everyTypeCanBeReifiedWithoutExceptions(&T u) = _ := typeOf(u); + +test bool allConstructorsAreDefined() + = size(#P.definitions[adt ( + "P", + [] + )].alternatives) == 7; + +test bool allConstructorsForAnAlternativeDefineTheSameSort() + = !(/choice(def, /cons(label(_, def), _, _, _)) !:= #P.definitions); + +test bool typeParameterReificationIsStatic1(&F _) + = #&F.symbol == \parameter("F", \value()); +test bool typeParameterReificationIsStatic2(list[&F] _) + = #list[&F].symbol == \list(\parameter("F", \value())); + +@ignore{issue #1007} +// Fails for: +// Type parameters: +// &T<:list[&F] => list[void] +// &F => void +// Actual parameters: +// list[void] =>[] +test bool typeParameterReificationIsStatic3(&T <: list[&F] f) + = #&T.symbol == \parameter("T", \list(\parameter("F", \value()))); + +test bool dynamicTypesAreAlwaysGeneric(value v) + = !(type[value] _ !:= type(typeOf(v), ())); + +// New tests which can be enabled after succesful bootstrap +data P(int size = 0); + +@ignore{To be investigated} +test bool allConstructorsHaveTheCommonKwParam() + = all(/choice(def, /cons(_, _, kws, _)) := #P.definitions, label("size", \int()) in kws); + +@ignoreCompiler{To be investigated} +test bool axiomHasItsKwParam() + = /cons(label("axiom", _), _, kws, _) := #P.definitions + && label("mine", \adt ( + "P", + [] + )) + in kws; + +@ignore{To be investigated} +test bool axiomsKwParamIsExclusive() + = all(/cons(label(!"axiom", _), _, kws, _) := #P.definitions, label("mine", \adt ( + "P", + [] + )) notin kws); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Scoping.rsc| +module lang::rascal::tests::functionality::Scoping +/* TODO: inert interpreter test *//*The following tests have been removed since we no longer support shadowing + + test bool localShadowing(){ + int n = 2; return int n := 3; + } + + test bool localRedeclarationInt1(){ + int n ; return int n := 3 && n == 3; + } + + test bool localRedeclarationInt2(){ + int n; return [int n] := [3] && n == 3; + } + + test bool localShadowing2(){ + int n; return [*int n] := [1,2,3] && n == [1,2,3]; + } + + test bool localShadowingListMatch(){ + list[int] n = [10,20]; return [*int n] := [1,2,3] && n == [1,2,3]; + } + + test bool localRedeclarationList(){ + list[int] n; return [*int n] := [1,2,3] && n == [1,2,3]; + } + + test bool localRedeclarationError9(){ + int n; return // := "123"; + } + + test bool localComprehensionShadowing(){ + int n = 5; L = [n | int n <- [1 .. 10]]; return n==5; + } + + test bool localReunexpectedDeclaration10(){ + int n; L = [n | int n <- [1 .. 10]]; return L == [1 .. 10]; + } + *///test bool RedeclaredLocal(){ +// int n = 1; {int m = 2;}; int m = 3; return n == 1 && m == 3; +//} +// +//test bool formalsToGlobalsLeak() { +// int x = 0; +// void f(int x) { x += 1;} +// f(1); +// return x == 0; +//} +// +//// Nested function declarations and non-local variables +// +// +//int inc0(){ +// int n = 13; +// int inc0(){ +// return n; +// } +// return inc0(); +//} +// +//test bool tstInc0() = inc0() == 13; +// +//int inc1(int n){ +// int inc1(){ +// return n; +// } +// return inc1(); +//} +// +//test bool tstInc1() = inc1(13) == 13; +// +//int inc2(int n){ +// int inc2(){ +// n += 1; +// return n; +// } +// inc2(); +// return n; +//} +// +//test bool tstInc2() = inc2(13) == 14; +// +//int inc3(int n){ +// int inc3(){ +// int inc3(){ +// return n; +// } +// return inc3(); +// } +// inc3(); +// return n; +//} +// +//test bool tstInc3() = inc3(13) == 13; +// +//int inc4(int n){ +// int inc4(){ +// int inc4(){ +// n += 1; +// return n; +// } +// return inc4(); +// } +// inc4(); +// return n; +//} +// +//test bool tstInc4() = inc4(13) == 14; +// +//int (int) f() { +// int n = 100; +// return int (int i) { return i + n; }; +//} +// +//test bool tstF() = f()(11) == 111; +// +// +//// Closures +// +//int (int) g(int n) { +// return int (int i) { return i + n; }; +//} +// +//test bool tstG1() = g(100)(11) == 111; +// +//test bool tstG2() { int n = 1000; return g(100)(11) == 111 && g(200)(11) == 211; } +// +//test bool tstG2() { +// g100 = g(100); +// int k(int n) = g100(n); +// return g(100)(11) == k(11); +//} +// +//int (int) h(int n1) { +// int n2 = 50; +// int k(int i) { return n1 + n2 + i; } +// return k; +//} +// +//test bool tstH() = h(1)(2) == 53; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/SimpleVisit.rsc| +module lang::rascal::tests::functionality::SimpleVisit + +data B + = and(B lhs, B rhs) + | t() + ; + +test bool visitTest() { + visit(and(t(), t())) { + case t(): + return true; + } + ; + + return false; +} + +test bool matchTest() = /t() := and(t(), t()); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Statement.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the EclipseLicense v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::functionality::Statement + +import Exception; + +// testAssert + +test bool testAssert1() {return assert 3 > 2;} +test bool testAssert2() {return assert 3 > 2: "Yes assert succeeds";} + +@expected{AssertionFailed} +test bool assertError1() {assert 1 == 2;return false;} + +@expected{AssertionFailed} +test bool assertError2() {assert 1 == 2: "1 really differs from 2"; return false;} + +// assignment + +test bool assignment1() {int x = 3; return x == 3;} +test bool assignment2() {int x = 3; x = 4; return x == 4;} +test bool assignment3() {return { = <3, 4>; (x == 3) && (y == 4);};} +test bool assignment4() {return { = <3, 4, 5>; (x == 3) && (y == 4) && (z == 5);};} +test bool assignment5() {return { = <3, 4>; x = 5; return (x == 5) && (y == 4);};} + +test bool assignment6() {int x = 3; x += 2; return x == 5;} +test bool assignment7() {int x = 3; x -= 2; return x == 1;} +test bool assignment8() {int x = 3; x *= 2; return x == 6;} +test bool assignment9() {int x = 3; x /= 2; return x == 1;} + +test bool assignment10() {list[int] x = [0,1,2]; return x == [0,1,2];} +test bool assignment11() {list[int] x = [0,1,2]; return x[0] == 0;} +test bool assignment12() {list[int] x = [0,1,2]; return x[1] == 1;} +test bool assignment13() {list[int] x = [0,1,2]; return x[2] == 2;} +test bool assignment14() {return {list[int] x = [0,1,2]; x[1] = 10; (x[0] == 0) && (x[1] == 10) && (x[2] == 2);};} + +test bool assignment15() {return {map[int,int] x = (0:0,1:10,2:20); x == (0:0,1:10,2:20);};} +test bool assignment16() {return {map[int,int] x = (0:0,1:10,2:20); x[1] = 15; (x[0] == 0) && (x[1] == 15) && (x[2] == 20);};} + +test bool assignment17() {set[int] x = {0,1,2}; return x == {0,1,2};} +test bool assignment18() {set[int] x = {0,1,2}; x = x + {3,4}; return x == {0,1,2, 3,4};} + +test bool assignment19() {rel[str,list[int]] s = {<"a", [1,2]>, <"b", []>, <"c", [4,5,6]>}; return s != {};} +test bool assignment20() {rel[str,list[int]] s = {<"a", [1,2]>, <"b", []>, <"c", [4,5,6]>};return s != {};} + +// block + +test bool block1() {int x = 3; x = 4; return x ==4;} +test bool block2() {int x = 3; x = 4; return x == 4;} + +// testBreak + +test bool testBreak() {int n = 0; while(n < 10){ n = n + 1; break;}; return n == 1;} + + +// testContinue + + /* no tests available */ + +// doWhile + +test bool doWhile1() {return {int n = 0; m = 2; do {m = m * m; n = n + 1;} while (n < 1); (n == 1) && (m == 4);};} +test bool doWhile2() {return {int n = 0; m = 2; do {m = m * m; n = n + 1;} while (n < 3); m == 256;};} + +// testWhile + +test bool testWhile1() {return {int n = 0; int m = 2; while(n != 0){ m = m * m;}; (n == 0)&& (m == 2);};} +test bool testWhile2() {return {int n = 0; int m = 2; while(n < 3){ m = m * m; n = n + 1;}; (n ==3) && (m == 256);};} + +test bool testWhileWithBacktracking1() { + list[list[int]] res = []; + l:while([*int x, *int _] := [1,2,3]) { + res = res + [ x ]; + fail l; + } + return res == [[],[1],[1,2],[1,2,3]]; +} + +test bool testWhileWithBacktracking2() { + list[list[int]] res = []; + + while(true) { + res = res + [ [999] ]; + fail; + } + return res == [[999]]; +} + +test bool testWhileWithBacktracking3(){ + list[list[int]] res = []; + + n = 0; + while([*int x, *int y] := [3,4,3,4], n < 3) { + if(x == y) { + res = res + [ x ]; + n = n + 1; + } else { + res = res + [ [0] ]; + fail; + } + } + return res == [[0],[0],[3,4],[0],[0],[3,4],[0],[0],[3,4]]; +} + +test bool testWhileWithBacktracking4(){ + list[list[int]] res = []; + + n = 0; + while(n < 3) { + res = res + [ [10] ]; + n = n + 1; + } + return res == [[10],[10],[10]]; +} + +test bool testWhileWithBacktracking5(){ + list[list[int]] res = []; + + n = 0; + while(1 == 1, n < 3) { + res = res + [ [11] ]; + n = n + 1; + } + return res == [[11],[11],[11]]; +} + +test bool testWhileWithBacktracking6(){ + list[list[int]] res = []; + + n = 0; + while(1 == 2 || n < 3) { + res = res + [ [12] ]; + n = n + 1; + } + return res == [[12],[12],[12]]; +} + +@ignoreInterpreter{Infinite loop} +test bool labelledWhileContinue(){ + res = ""; + next_fun: + while(_n <- [3,3]){ + try { + throw "NO"; + } catch "NO": { + res += "NO"; + continue next_fun; + } + } + return res == "NONO"; +} + +@ignoreCompiler{FIXME: pre and post should be reset to undefined on loop entry} +test bool testWhileWithPatternVariables(){ + syms = [10,9,1,3,5]; + while([*pre, x, y, *post] := syms, x > y){ + syms = [*pre, y, x, *post]; + } + return syms == [1,3,5,9,10]; +} + +data D = d(int i) | d(); + +D d(int i) { if (i % 2 == 0) fail d; else return d();} + +// fail + +test bool fail1() = d(2) := d(2); +test bool fail2() = d(3) == d(); + +test bool fail3() { + int n = 0; + loop:for(int _ <- [1,2,3,4], n <= 3) { + if(n == 3) { + fail loop; + } + n = n + 1; + } + return n == 3; +} + +test bool fail4() { + int main(){ + if1:if(x <- [1,2,3,4], x <= 3) { + if2:if(y <- [4,3,2,1], y >= 3) { + if(x != 3) { + fail if1; + } else if(y != 3) { + fail if2; + } + return x + y; + } + } + return -1; + } + return main() == 6; +} + +test bool fail5() { + str trace = ""; + if(true) { + if(false) { + ; + } else { + trace += "fail inner!"; + fail; + } + } else { + trace += "else outer!"; + } + return trace == "fail inner!else outer!"; +} + +// testFor + +test bool testFor1() {int n = 0; for(int i <- [1,2,3,4]){ n = n + i;} return n == 10;} +test bool testFor2() {int n = 0; for(int i <- [1,2,3,4], n <= 3){ n = n + i;} return n == 6;} +test bool testFor3() {int n = 0; for(int _ <- [1,2,3,4]){ n = n + 1; if (n == 3) break; } return n == 3;} +test bool testFor4() {int n = 0; for(int _ <- [1,2,3,4], n <= 3){ if (n == 3) continue; n = n + 1; } return n == 3;} +test bool testFor5() {int n = 0; loop:for(int _ <- [1,2,3,4], n <= 3){ if (n == 3) fail loop; n = n + 1; } return n == 3;} + +test bool labelledForContinue(){ + res = ""; + next_fun: + for(_n <- [3,3]){ + try { + throw "NO"; + } catch "NO": { + res += "NO"; + continue next_fun; + } + } + return res == "NONO"; +} + +// testAppend + +/*TODO:?*/ +//test bool testAppend() for(int i <- [1,2,3,4]){ 3 * i; } == 12;)); +test bool testAppend1() { L = for(int i <- [1,2,3,4]){ append 3 * i; }; return L == [3,6,9,12];} +test bool testAppend2() { L = for(int i <- [1,2,3,4]){ append 3 * i; append 4 *i;}; return L == [3,4,6,8,9,12,12,16];} + +test bool testAppend3() { + res1 = for(2 > 1) append 0; + res1 = res1 + [ 1 | 2 > 1 ]; + res2 = for(2 < 1) append 2; + res2 = res2 + [ 3 | 2 < 1 ]; + return res1 + res2 == [0, 1]; +} + +// We no longer allow dynamically scoped appends +//test bool testAppend4() { +// res = for(x <- [1,2,3,4]) { int f() { append x; return 4; }; append f(); }; +// return res == [1,4,2,4,3,4,4,4]; +//} + +// ifThen + +test bool ifThen1() {int n = 10; if(n < 10){n = n - 4;} return n == 10;} +test bool ifThen2() {int n = 10; if(n < 15){n = n - 4;} return n == 6;} +test bool ifThen3() {int n = 10; l:if(int i <- [1,2,3]){ if (i % 2 != 0) { n = n + 4; fail l; } n = n - 4;} return n == 10;} + +// ifThenElse + +test bool ifThenElse1() {int n = 10; if(n < 10){n = n - 4;} else { n = n + 4;} return n == 14;} +test bool ifThenElse2() {int n = 12; if(n < 10){n = n - 4;} else { n = n + 4;} return n == 16;} + +// solve + +rel[int,int] R1 = {<1,2>, <2,3>, <3,4>}; + +test bool solve1() { + rel[int,int] T = R1; + solve (T) T = T + (T o R1); + return T == {<1,2>, <1,3>,<1,4>,<2,3>,<2,4>,<3,4>}; +} + +test bool solve2() { + int j = 0; + solve (j) if (j < 1000) j += 1; + return j == 1000; +} + +@expected{IndexOutOfBounds} +test bool solveIndexOutOfBounds1() { + rel[int,int] T = R1; + solve (T; -1) T = T + (T o R1); + return T == {<1,2>, <1,3>,<1,4>,<2,3>,<2,4>,<3,4>}; + } + +data PAIR = pair(int x, int y); + +set[PAIR] removeIdPairs(set[PAIR] inp){ + res = inp; + solve(res) { + if ( { pair(a, b), pair(b, b), *c } := res ) + res = { *c, pair(a, b) }; + } + return res; +} + +test bool removeIdPairs1() = removeIdPairs({}) == {}; +test bool removeIdPairs2() = removeIdPairs({pair(1,2),pair(2,3)}) == {pair(1,2),pair(2,3)}; +test bool removeIdPairs3() = removeIdPairs({pair(1,2),pair(2,3),pair(2,2)}) == {pair(1,2),pair(2,3)}; +test bool removeIdPairs4() = removeIdPairs({pair(1,2),pair(2,2),pair(2,3),pair(3,3)}) == {pair(1,2),pair(2,3)}; +test bool removeIdPairs5() = removeIdPairs({pair(2,2),pair(1,2),pair(2,2),pair(2,3),pair(3,3)}) == {pair(1,2),pair(2,3)}; +test bool removeIdPairs6() = removeIdPairs({pair(2,2),pair(3,3),pair(1,2),pair(2,2),pair(2,3),pair(3,3)}) == {pair(1,2),pair(2,3)}; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Subscription.rsc| +module lang::rascal::tests::functionality::Subscription + +/******************************************************************************* + * Copyright (c) 2009-2015 CWI + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + + * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI + * * Paul Klint - Paul.Klint@cwi.nl - CWI + * * Bert Lisser - Bert.Lisser@cwi.nl - CWI +*******************************************************************************/import Exception; + +data NODE = f(int a, str b, real c); + +// list +test bool listTest1() = [0, 1, 2, 3][0] == 0; +test bool listTest2() = [0, 1, 2, 3][1] == 1; +test bool listTest3() = [0, 1, 2, 3][2] == 2; +test bool listTest4() = [0, 1, 2, 3][3] == 3; + +test bool listTest5() { + list[int] L = [0, 1, 2, 3]; + L[0] = 10; + return L == [10, 1, 2, 3]; +} +test bool listTest6() { + list[int] L = [0, 1, 2, 3]; + L[1] = 11; + return L == [0, 11, 2, 3]; +} +test bool listTest7() { + list[int] L = [0, 1, 2, 3]; + L[2] = 22; + return L == [0, 1, 22, 3]; +} +test bool listTest8() { + list[int] L = [0, 1, 2, 3]; + L[3] = 33; + return L == [0, 1, 2, 33]; +} + +@expected{IndexOutOfBounds} +test bool listError1() = [0, 1, 2, 3][4] == 3; + +@expected{IndexOutOfBounds} +test bool listError2() { + list[int] L = [0, 1, 2, 3]; + L[4] = 44; + L == [0, 1, 2, 3, 44]; + return false; +} + +// map +test bool mapTest1() = (1 : 10, + 2 : 20, + 3 : 30)[1] == 10; +test bool mapTest2() = (1 : 10, + 2 : 20, + 3 : 30)[2] == 20; +test bool mapTest3() = (1 : 10, + 2 : 20, + 3 : 30)[3] == 30; + +@expected{NoSuchKey} +test bool mapTest4() = (1 : 10, + 2 : 20, + 3 : 30)[4] == 30; + +test bool mapTest5() { + map[int, int] M = (1 : 10, + 2 : 20, + 3 : 30); + M[1] = 100; + return M == (1 : 100, + 2 : 20, + 3 : 30); +} +test bool mapTest6() { + map[int, int] M = (1 : 10, + 2 : 20, + 3 : 30); + M[2] = 200; + return M == (1 : 10, + 2 : 200, + 3 : 30); +} +test bool mapTest7() { + map[int, int] M = (1 : 10, + 2 : 20, + 3 : 30); + M[3] = 300; + return M == (1 : 10, + 2 : 20, + 3 : 300); +} +test bool mapTest8() { + map[int, int] M = (1 : 10, + 2 : 20, + 3 : 30); + M[4] = 400; + return M == (1 : 10, + 2 : 20, + 3 : 30, + 4 : 400); +} + +// tuple +test bool tupleTest1() = <0, "a", 3.5>[0] == 0; +test bool tupleTest2() = <0, "a", 3.5>[1] == "a"; +test bool tupleTest3() = <0, "a", 3.5>[2] == 3.5; + +// relation +test bool relationTest1() = {<1, "a" >, + <2, "b" >}[0] == {}; +test bool relationTest2() = {<1, "a" >, + <2, "b" >}[1] == {"a"}; +test bool relationTest3() = {<1, "a" >, + <2, "b" >}[2] == {"b"}; + +test bool relationTest4() = {<1, "a" >, + <2, "b" >, + <1, "abc" >}[1] == {"a", "abc"}; + +test bool relationTest5() = {<1, "a", 10 >, + <2, "b", 20 >, + <1, "abc", 100 >}[0] == {}; +test bool relationTest6() + = {<1, "a", 10 >, + <2, "b", 20 >, + <1, "abc", 100 >}[1] == {<"a", 10 >, + <"abc", 100 >}; +test bool relationTest7() + = {<1, "a", 10 >, + <2, "b", 20 >, + <1, "abc", 100 >}[2] == {<"b", 20 >}; +test bool relationTest8() + = {<1, "a", 10 >, + <2, "b", 20 >, + <1, "abc", 100 >}[{1, 2}] == {<"a", 10 >, + <"b", 20 >, + <"abc", 100 >}; + +test bool relationTest9() + = {<1, "a", 10 >, + <2, "b", 20 >, + <1, "abc", 100 >}[1, _] == {10, 100}; + +// relationMultiIndex +test bool relationMultiIndex1() + = {<1, "a", 1.0 >, + <2, "b", 2.0 >, + <3, "c", 3.0 >}[0] == {}; +test bool relationMultiIndex2() + = {<1, "a", 1.0 >, + <2, "b", 2.0 >, + <3, "c", 3.0 >}[1] == {<"a", 1.0 >}; +test bool relationMultiIndex3() + = {<1, "a", 1.0 >, + <2, "b", 2.0 >, + <3, "c", 3.0 >}[2, "b"] == {2.0}; +test bool relationMultiIndex4() + = {<1, 10, 10.5 >, + <2, 20, 20.5 >, + <3, 20, 30.5 >, + <2, 10, 100.5 >}[{1}, {10, 20}] == {10.5}; + +// node +test bool nodeTest1() = f(0, "a", 3.5)[0] == 0; +test bool nodeTest2() = f(0, "a", 3.5)[1] == "a"; +test bool nodeTest3() = f(0, "a", 3.5)[2] == 3.5; + +/* structure assignment no longer allowed; *///test bool nodeTest4() {NODE T = f(0, "a", 3.5); T[0] = 10; return T == f(10, "a", 3.5);} +@expected{IndexOutOfBounds} +test bool nodeBoundsError() = f(0, "a", 3.5)[3] == 3.5; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Switch.rsc| +module lang::rascal::tests::functionality::Switch + +// testSwitch +test bool testSwitch1a() { + int n = 0; + switch(2) { + case 2: + n = 2; + case 4: + n = 4; + case 6: + n = 6; + default: + n = 10; + } + return n == 2; +} +test bool testSwitch1b() { + int n = 0; + switch(4) { + case 2: + n = 2; + case 4: + n = 4; + case 6: + n = 6; + default: + n = 10; + } + return n == 4; +} +test bool testSwitch1c() { + int n = 0; + switch(6) { + case 2: + n = 2; + case 4: + n = 4; + case 6: + n = 6; + default: + n = 10; + } + return n == 6; +} +test bool testSwitch1d() { + int n = 0; + switch(8) { + case 2: + n = 2; + case 4: + n = 4; + case 6: + n = 6; + default: + n = 10; + } + return n == 10; +} +test bool testSwitch1e() { + int n = 0; + switch(8) { + default: + ; + } + return n == 0; +} +test bool testSwitch1f() { + int n = 0; + switch(8) { + default: + n = 10; + } + return n == 10; +} + +int sw2(int e) { + int n = 0; + switch(e) { + case 1: + n = 1; + case _: 2: + n = 2; + case int _: 3: + n = 3; + default: + n = 4; + } + return n; +} + +test bool testSwitch2a() = sw2(1) == 1; +test bool testSwitch2b() = sw2(2) == 2; +test bool testSwitch2c() = sw2(3) == 3; +test bool testSwitch2d() = sw2(4) == 4; + +int sw3(str e) { + int n = 0; + switch(e) { + case "abc": + n = 1; + case /A/: + n = 2; + case str _: "def": + n = 3; + default: + n = 4; + } + return n; +} + +test bool testSwitch3a() = sw3("abc") == 1; +test bool testSwitch3b() = sw3("AAA") == 2; +test bool testSwitch3c() = sw3("def") == 3; +test bool testSwitch3d() = sw3("zzz") == 4; + +data D + = d(int i) + | d() + ; + +int sw4(value e) { + int n = 0; + switch(e) { + case "abc": + n = 1; + case str _: /def/: + n = 2; + case 3: + n = 3; + case d(): + n = 4; + case d(_): + n = 5; + case str _(3): + n = 6; + case [1, 2, 3]: + n = 7; + case [1, 2, 3, 4]: + n = 8; + default: + n = 9; + } + return n; +} + +test bool testSwitch4a() = sw4("abc") == 1; +test bool testSwitch4b() = sw4("def") == 2; +test bool testSwitch4c() = sw4(3) == 3; +test bool testSwitch4d() = sw4(d()) == 4; +test bool testSwitch4e() = sw4(d(2)) == 5; +test bool testSwitch4f() = sw4("abc"(3)) == 6; +test bool testSwitch4g() = sw4([1, 2, 3]) == 7; +test bool testSwitch4h() = sw4([1, 2, 3, 4]) == 8; +test bool testSwitch4i() = sw4(<-1, -1>) == 9; + +data E + = e() + | e(int n) + | e(str s, int m) + ; + +int sw5(value v) { + int n = 0; + switch(v) { + case "abc": + n = 1; + case e(//, 2): { + n = 2; + if (str _ := s/* just use the s to avoid warning */ ) + true; + } + case e(//, 3): { + n = 3; + if (str _ := s) + true; + } + case 4: + n = 4; + case e(): + n = 5; + case e(int _): + n = 6; + case str _(7): + n = 7; + case [1, 2, 3]: + n = 8; + case [1, 2, 3, 4]: + n = 9; + case e("abc", 10): + n = 10; + case e("abc", int _): + n = 11; + case node _: + n = 12; + default: + n = 13; + } + return n; +} + +test bool testSwitch5a() = sw5("abc") == 1; +test bool testSwitch5b() = sw5(e("abc", 2)) == 2; +test bool testSwitchdc() = sw5(e("abc", 3)) == 3; +test bool testSwitch5e() = sw5(4) == 4; +test bool testSwitch5f() = sw5(e()) == 5; +test bool testSwitch5g() = sw5(e(6)) == 6; +test bool testSwitch5h() = sw5(e(7)) == 6; +test bool testSwitch5i() = sw5("f"(7)) == 7; +test bool testSwitch5j() = sw5([1, 2, 3]) == 8; +test bool testSwitch5k() = sw5([1, 2, 3, 4]) == 9; +test bool testSwitch5l() = sw5(e("abc", 10)) == 10; +test bool testSwitch5m() = sw5(e("abc", 11)) == 11; +test bool testSwitch5n() = sw5("f"(12)) == 12; +test bool testSwitch5o() = sw5(13) == 13; + +int sw6(value v) { + int n = 0; + switch(v) { + case true: + n = 1; + case 2: + n = 2; + case 3.0: + n = 3; + case 4r3: + n = 4; + case |home:///|: + n = 5; + case $2015-02-11T20:09:01.317+00:00$: + n = 6; + case "abc": + n = 7; + case [1, 2, 3]: + n = 8; + case [<1, 2, 3 >]: + n = 9; + case {1, 2, 3}: + n = 10; + case {<1, 2, 3 >}: + n = 11; + + //case ("a" : 1): n = 12; + default: + n = 13; + } + return n; +} + +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6a() = sw6(true) == 1; + +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6b() = sw6(2) == 2; +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6c() = sw6(3.0) == 3; +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6d() = sw6(4r3) == 4; +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6e() = sw6(|home:///|) == 5; +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6f() = sw6($2015-02-11T20:09:01.317+00:00$) == 6; +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6g() = sw6("abc") == 7; +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6h() = sw6([1, 2, 3]) == 8; +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6i() = sw6([<1, 2, 3>]) == 9; +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6j() = sw6({1, 2, 3}) == 10; +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6k() = sw6({<1, 2, 3 >}) == 11; +@ignore{map pattern not supported} +test bool testSwitch6l() = sw6(("a" : 1)) == 12; +@ignoreInterpreter{Location, datetime and map pattern not supported} +test bool testSwitch6m() = sw6(13) == 13; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/TryCatch.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::functionality::TryCatch + +import Exception; +import List; +import Set; +import Map; +import IO; +import util::Math; + +data NODEA = fA(int N); + +data NODEB + = fB(int N) + | dB(NODEB a, NODEB b) + ; + +data NODEC + = fC(int N) + | fin(value V) + | dC(NODEC a) + | dC(NODEC a, NODEC b) + ; + +data Exception = divide_by_zero(); + +int classify(value v) { + try { + throw v; + } + catch int _: { + return 1; + } + catch node _: { + return 2; + } + catch str _: { + return 3; + } + catch: { + return 4; + } +} + +value duplicate(value v) { + try { + throw v; + } + catch int x: { + return x + x; + } + catch NODEB x: { + return dB(x, x); + } + catch str s: { + return s + s; + } + catch: { + return v; + } +} + +value dfin(value v) { + value res = 0; + try { + throw v; + } + catch int x: { + res = x + x; + } + catch NODEC x: { + res = dC(x, x); + } + catch str s: { + res = s + s; + } + catch: { + res = v; + } + finally { + return fin(res); + } +} + +int divide(int x, int y) throws divide_by_zero { + if (y == 0) { + throw divide_by_zero(); + } + else { + return x / y; + } +} + +int safeDivide(int x, int y) { + try + return divide(x, y); + catch: + return 0; +} + +int catchStackThrow(int i) { + throw i; +} + +value catchStack() { + int i = 1; + int y = 0; + while(i < 50) { + try { + y = 1 + catchStackThrow(i); + } + catch int x: { + i = x + 1; + } + } + return ; +} + +// testCatchStack +test bool testCatchStack() = catchStack() == <50>; + +// testClassify +test bool testClassify1() = classify(3) == 1; +test bool testClassify2() = classify(fA(3)) == 2; +test bool testClassify3() = classify("abc") == 3; +test bool testClassify4() = classify([1, 2, 3]) == 4; + +// testDuplicate +test bool testDuplicate1() = duplicate(3) == 6; +test bool testDuplicate2() = duplicate(fB(3)) == dB(fB(3), fB(3)); +test bool testDuplicate3() = duplicate("abc") == "abcabc"; +test bool testDuplicate4() = duplicate(3.5) == 3.5; + +// testDFin +test bool testDFin1() = dfin(3) == fin(6); +test bool testDFin2() = dfin(fC(3)) == fin(dC(fC(3), fC(3))); +test bool testDFin3() = dfin("abc") == fin("abcabc"); +test bool testDFin4() = dfin(3.5) == fin(3.5); + +// testDivide +test bool testDivide1() = divide(3, 2) == 1; +test bool testDivide2() = safeDivide(3, 2) == 1; +test bool testDivide3() = safeDivide(3, 0) == 0; + +// emptyListException +test bool emptyListException1() { + try { + head([]); + } + catch EmptyList(): + return true; + return false; +} + +// emptyMapException +test bool emptyMapException1() { + try { + getOneFrom(()); + } + catch EmptyMap(): + return true; + return false; +} + +// emptySetException +test bool emptySetException1() { + try { + getOneFrom({}); + } + catch EmptySet(): + return true; + return false; +} + +// indexOutOfBoundsException +test bool indexOutOfBoundsException1() { + try { + [0, 1, 2][3]; + } + catch IndexOutOfBounds(int _): + return true; + return false; +} + +// pathNotFoundException +test bool pathNotFoundException1() { + try { + S = readFile(|file:///DoesNotExist|); + } + catch PathNotFound(loc _): + return true; + return false; +} + +test bool emptyTryStatement() { + try + ; + catch: + ; + return return true; +} + +test bool emptyTryBlock() { + try { + ; + } + catch: + ; + return return true; +} + +// empty catch statement +test bool emptyCatchStatement1() { + try { + return true; + } + catch: + ; + return false; +} + +int f_using_empty_catch() { + x = 10; + try { + x/0; + ; + } + catch: + ; + return x; +} + +test bool emptyCatchStatement2() { + return f_using_empty_catch() == 10; +} + +// case with wildcard pattern +int f() { + throw "abc"; +} + +test bool afterCatch() { + try + f(); + catch _: + x = 3; + return true; +} + +// empty catch and finally lock +test bool emptyCatchAndFinallyBlock() { + try { + return true; + } + catch: + ; + finally { + ; + } + return false; +} + +int x = 0 ; + +int f_using_finally1() { + x = 10; + try { + return 123456; + } + catch: + ; + finally { + x = 20; + } + return -1; +} + +test bool finally1() { + return f_using_finally1() == 123456 && x == 20; +} + +int f_using_finally2() { + x = 10; + try { + return 123456; + } + catch: + ; + finally { + x = 20; + return 789; + } +} + +test bool finally2() { + return f_using_finally2() == 789 && x == 20; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Visit1.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl} +module lang::rascal::tests::functionality::Visit1 + +import Node; + +test bool visit1a() { + return + visit([1, 2, 3]) { + case list[int] l => [( 0 | it + i | int i <- l )] + case int i => i + 100 + } == [306]; +} + +test bool visit1b() { + int f1b() { + visit([1, 2, 3]) { + case list[int] l => [( 0 | it + i | int i <- l )] + case int _: + return 42; + } + ; + return 101; + } + return f1b() == 42; +} + +test bool visit2a() { + return + top-down visit([1, 2, 3]) { + case list[int] l => [( 0 | it + i | int i <- l )] + case int i => i + 100 + } == [106]; +} + +test bool visit2b() { + int f2b() { + top-down visit([1, 2, 3]) { + case list[int] l => [( 0 | it + i | int i <- l )] + case int _: + return 42; + } + return 101; + } + return f2b() == 42; +} + +test bool visit3a() { + return + visit([1, 2, 3]) { + case list[int] l: + insert [( 0 | it + i | int i <- l )]; + case int i: { + i = i + 100; + insert i; + i = i + 200; + } + } == [306]; +} + +test bool visit3b() { + int f3b() { + visit([1, 2, 3]) { + case list[int] l: + insert [( 0 | it + i | int i <- l )]; + case int i: { + i = i + 100; + return 42; + } + } + return 101; + } + return f3b() == 42; +} + +test bool visit4a() { + return + top-down visit([1, 2, 3]) { + case list[int] l: + insert [( 0 | it + i | int i <- l )]; + case int i: { + i = i + 100; + insert i; + i = i + 200; + } + } == [106]; +} + +test bool visit4b() { + int f4b() { + top-down visit([1, 2, 3]) { + case list[int] l: + insert [( 0 | it + i | int i <- l )]; + case int i: { + i = i + 100; + return 42; + } + } + return 101; + } + return f4b() == 42; +} + +test bool visit5() { + return + visit((1 : "1", + 2 : "2", + 3 : "3")) { + // ( 306:"1 + 100; 2 + 100; 3 + 100; " ) + case map[int, str] m => (( 0 | it + k | int k <- m ) : ( "" | it + m[k] | int k <- m )) + case int i => i + 100 + case str s => s + " + 100; " + } == (306 : "3 + 100; 2 + 100; 1 + 100; "); +} + +test bool visit6() { + return + top-down visit((1 : "1", + 2 : "2", + 3 : "3")) { + // ( 106:"321 + 100; " ) + case map[int, str] m => (( 0 | it + k | int k <- m ) : ( "" | it + m[k] | int k <- m )) + case int i => i + 100 + case str s => s + " + 100; " + } == (106 : "132 + 100; "); +} + +test bool visit3() { + return + visit({[1, 1], [2, 2], [3, 3]}) { + // { [202], [204], [206], [4,5] } + case set[list[int]] s => s + {[4, 5]} + case list[int] l => [( 0 | it + i | int i <- l )] + case int i => i + 100 + } == {[206], [4, 5], [204], [202]}; +} + +test bool visit7() { + return + top-down visit({[1, 1], [2, 2], [3, 3]}) { + // { [102], [104], [106], [109] } + case set[list[int]] s => s + {[4, 5]} + case list[int] l => [( 0 | it + i | int i <- l )] + case int i => i + 100 + } == {[102], [104], [106], [109]}; +} + +test bool visit8() { + return + visit({<1, 1 >, + <2, 2 >, + <3, 3 >}) { + case set[tuple[int, int]] s => s + {<4, 5 >} + case tuple[int, int] t => { + elem = ( 0 | it + i | int i <- t ); + ; + } + case int i => i + 100 + } == {<204, 204 >, + <202, 202 >, + <206, 206 >, + <4, 5 >}; +} + +test bool visit9() { + return + top-down visit({<1, 1 >, + <2, 2 >, + <3, 3 >}) { + case set[tuple[int, int]] s => s + {<4, 5 >} + case tuple[int, int] t => { + elem = ( 0 | it + i | int i <- t ); + ; + } + case int i => i + 100 + } == {<106, 106 >, + <104, 104 >, + <109, 109 >, + <102, 102 >}; +} + +test bool visit10() { + return + visit({"a"(1, 1), "b"(2, 2), "c"(3, 3)}) { + case set[node] s => s + {"d"(4, 5)} + case node n: str s(int _, int _) + => { + elem = ( 0 | it + i | int i <- n ); + (s + "_been_here")(elem, elem); + } + case int i => i + 100 + } == {"a_been_here"(202, 202), + "b_been_here"(204, 204), + "d"(4, 5), + "c_been_here"(206, 206)}; +} + +test bool visit11() { + return + top-down visit({"a"(1, 1), "b"(2, 2), "c"(3, 3)}) { + case set[node] s => s + {"d"(4, 5)} + case node n: str s(int _, int _) + => { + elem = ( 0 | it + i | int i <- n ); + (s + "_been_here")(elem, elem); + } + case int i => i + 100 + } == {"a_been_here"(102, 102), + "c_been_here"(106, 106), + "b_been_here"(104, 104), + "d_been_here"(109, 109)}; +} + +data ABCD + = a(int x, int y) + | b(int x, int y) + | c(int x, int y) + | d(int x, int y) + ; + +test bool visit12() { + return + visit({[a(1, 1)], [b(2, 2)], [c(3, 3)]}) { + case set[list[ABCD]] s => s + {[d(4, 5)]} + case a(int x, int y) => a(x + 1000, y + 1000) + case ABCD nd => { + elem = ( 0 | it + i | int i <- nd ); + a(elem, elem); + } + case int i => i + 100 + } == {[d(4, 5)], [a(1101, 1101)], [a(204, 204)], [a(206, 206)]}; +} + +test bool visit13() { + return + top-down visit({[a(1, 1)], [b(2, 2)], [c(3, 3)]}) { + case set[list[ABCD]] s => s + {[d(4, 5)]} + case a(int x, int y) => b(x + 1000, y + 1000) + case ABCD nd => { + elem = ( 0 | it + i | int i <- nd ); + a(elem, elem); + } + case int i => i + 100 + } == {[a(106, 106)], [a(109, 109)], [b(1101, 1101)], [a(104, 104)]}; +} + +test bool visit14() { + return + bottom-up-break visit({[a(1, 1)], [b(2, 2)], [c(3, 3)]}) { + case set[list[ABCD]] s => s + {[d(5, 5)]} + // should not match + case list[ABCD] l => l + [d(4, 4)] + // should match only for [ c(3,3) ] + case a(int x, int y) => a(x + 1000, y + 1000) + // should match + case b(int x, int y) => b(x + 1000, y + 1000) + // should not match + case 2 => 102 + } // { [ b(102,102) ], [ c(3,3), d(4,4) ], [ a(1001,1001) ] } + == {[b(102, 102)], [a(1001, 1001)], [c(3, 3), d(4, 4)]}; +} + +test bool visit15() { + return + top-down-break visit({[a(1, 1)], [b(2, 2)], [c(3, 3)]}) { + // case set[list[ABCD]] s => s + { [ d(5,5) ] } + case [a(int x, int y)] => [a(x + 10, y + 10), d(4, 4)] + case a(int x, int y) => a(x + 1000, y + 1000) + case b(int x, int y) => b(x + 1000, y + 1000) + case int i => i + 100 + } == {[a(11, 11), d(4, 4)], [b(1002, 1002)], [c(103, 103)]}; +} + +test bool visit16a() { + l = [1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0]; + + // 11 + return + outermost visit({l}) { + case [*int sub, 1, 0] => [10, *sub] + case 10 => 20 + } == {[20, 20, 20, 20, 1, 0, 1]}; +} + +test bool visit16b() { + l = [1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0]; + // 11 + int f16b() { + outermost visit({l}) { + case [*int sub, 1, 0] => [10, *sub] + case 10: + return 42; + } + return 101; + } + return f16b() == 42; +} + +test bool visit17a() { + l = [1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0]; + + // 11 + return + innermost visit({l}) { + case [*int sub, 1, 0] => [10, *sub] + case 10 => 20 + } == {[10, 10, 10, 10, 1, 0, 1]}; +} + +test bool visit17b() { + l = [1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0]; + // 11 + int f17b() { + innermost visit({l}) { + case [*int _, 1, 0]: + return 42; + case 10 => 20 + } + return 101; + } + return f17b() == 42; +} + +test bool visit18() { + l = [1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0]; + + return + outermost visit({l}) { + case [*int sub, 1, 0] => [998, *sub] + case [*int sub, 1, 1] => [666, *sub] + case 998 => 999 + } == {[666, 999, 999, 666, 666, 999, 999, 999, 999, 1, 0, 1, 0, 1]}; +} + +data LIST = lst(list[int] elems); + +@ignoreInterpreter{Interpreter crashes on this test} +@ignoreCompiler{Gives [lst(["666"])] which the most likely answer} +test bool visit19() { + return + [ + visit(lst([1])) { + // list[str] <: list[value]; typeOf(subject) <: list[value] + // '+' list[str] <: typeOf(subject) + case list[value] _: + insert ["666"]; + case list[int] l: { + insert l+[666]; + l = l + [777]; + } + case int _: + insert 999; + } + ] == [lst([999, 666])]; +} + +@ignoreInterpreter{Interpreter crashes on this test} +@ignoreCompiler{Gives incorrect answer [lst([999])]} +test bool visit20() { + return + [ + visit(lst([1])) { + case list[value] _ => ["666"] + case list[int] l => l + [666] + case int _ => 999 + } + ] == [lst([999, 666])]; +} + +data X = weird1(list[void] x); + +@ignoreInterpreter{Interpreter crashes on this test} +test bool visit21() = visit(weird1([])) { + case list[int] _ => [1] + } == weird1([]); + +data Y = weird2(list[int] y); + +@IgnoreCompiler{ +/* TODO: fails in compiler: because we require that the dynamic type of the replacement is a subtype of the dynamic type of the subject, + * however list[int] !<: list[void] + * Should become: require that the dynamic type of the replacement is a subtype of the static type of the subject. + */ + +/* This a very tricky case! At the moment the compiler has the following information available: + * - the static types of the subject, the pattern and the replacement + * At runtime are available: + * - the dynamic types of the subject, the visited subtree and the replacement. + * What is NOT available is the static type of the visited subtree of the subject. + * This would require the following: + * - maintain a path from the root of the subject to the current subtree + * - use this path to determine the static type of the current subtree. + */ +} +test bool visit22() + = visit(weird2([])) { + case list[int] _ => [1] + } == weird2([1]); + +data Z = z(int n); + +test bool visit23() = visit(z(2)) { + case node _ => z(3) + } == z(3); + +@ignoreInterpreter{Interpreter crashes on this test} +test bool visit24() = visit([]) { + case _ => [1] + } == []; + +test bool visit27() { + return + visit({[a(1, 1)], [b(2, 2)], [c(3, 3)]}) { + case a(x, y) => a(x + 1000, y + 2000) + } == {[a(1001, 2001)], [c(3, 3)], [b(2, 2)]}; +} + +test bool visit28() { + return visit([1, 2]) { + case list[int] l => l when [1, 2] := l + } == [1, 2]; +} + +data NODE1 + = f(value V) + | f(value V1, value V2) + | f(value V1, value V2, value V3) + | g(value V1, value V2) + | h(value V1, value V2) + | h(value V1, value V2, value V3) + ; + +data T + = knot(int i, T l, T r) + | tip(int i) + ; + +data NODE10 + = f1(int I) + | g1(list[NODE10] L) + | h1(NODE10 N1, NODE10 N2) + ; + +int cnt(NODE1 t) { + int C = 0; + visit(t) { + case int _: + C = C + 1; + } + return C; +} + +NODE1 walk(NODE1 t) { + return visit(t) { + case int N => x when x := N * 2, x >= 1 + } + ; +} + +NODE1 drepl(NODE1 T) { + return bottom-up-break visit(T) { + case g(value T1, value T2) => h(T1, T2) + } + ; +} + +NODE1 frepa(NODE1 T) { + return visit(T) { + case g(value T1, value T2): + insert h(T1, T2); + } + ; +} + +NODE1 frepb(NODE1 T) { + return visit(T) { + case g(value T1, value T2) => h(T1, T2) + } + ; +} + +NODE1 frepG2H3a(NODE1 T) { + return visit(T) { + case g(value T1, value T2): + insert h(T1, T2, 0); + } + ; +} + +NODE1 frepG2H3b(NODE1 T) { + return visit(T) { + case g(value T1, value T2) => h(T1, T2, 0) + } + ; +} + +NODE1 inc(NODE1 T) { + return visit(T) { + case int N: + insert N+1; + } +} + +tuple[int, NODE1] inc_and_count(NODE1 T, int D) { + int C = 0; + T = visit(T) { + case int N: { + C = C + 1; + insert N+D; + } + } + ; + return ; +} + +NODE1 srepl(NODE1 T) { + return top-down-break visit(T) { + case g(value T1, value T2) => h(T1, T2) + } + ; +} + +list[int] order(NODE10 T) { + res = []; + visit(T) { + case int N: + res += N; + } + ; + return res; +} + +// Cnt() +test bool Cnt1() = cnt(f(3)) == 1; +test bool Cnt2() = cnt(f(1, 2, 3)) == 3; +test bool Cnt3() = cnt(f(1, g(2, 3))) == 3; +test bool Cnt4() = cnt(f(1, g ( + 2, + [3, 4, 5] + ))) == 5; +test bool Cnt5() = cnt(f(1, g(2, {3, 4, 5}))) == 5; +test bool Cnt6() = cnt(f(1, g(2, <3, 4, 5>))) == 5; +test bool Cnt7() = cnt(f(1, g(2, {<1, 10 >, + <2, 20 >}))) == 6; +test bool Cnt8() = cnt(f(1, g(2, (1 : 10, + 2 : 20)))) == 6; + +// When +test bool When1() = walk(f(3)) == f(6); +test bool When2() = walk(f(1, 2, 3)) == f(2, 4, 6); +test bool When3() = walk(f(1, g(2, 3))) == f(2, g(4, 6)); +test bool When4() = walk(f(1, g ( + 2, + [3, 4, 5] + ))) == f(2, g ( + 4, + [6, 8, 10] + )); + +// NewTreeVisibleBottomUp +test bool NewTreeVisibleBottomUp() + = visit(knot(0, tip(0), tip(0))) { + case tip(int i) => tip(i + 1) + case knot(int i, T l, T r) => knot(i + l.i + r.i, l, r) + } == knot(2, tip(1), tip(1)); + +// Drepl +test bool Drepl1() = drepl(f(3)) == f(3); +test bool Drepl2() = drepl(g(1, 2)) == h(1, 2); +test bool Drepl3() = drepl(g(1, f(g(2, 3)))) == g(1, f(h(2, 3))); + +// The following test used to work, but now that we are using more and more static types it fails. +// Explanation: [g(2,3),4,5] has as type list[value] and the elements have static type value as well. +// In particular g(2,3) has type value. +// As a result the node pattern g(value T1, value T2) in the case does not match. +// test bool Drepl()= + drepl + "drepl(g(1,f([g(2,3),4,5]))) == g(1,f([h(2,3),4,5]));}")); +// FrepA +test bool FrepA1() = frepa(f(3)) == f(3); +test bool FrepA2() = frepa(f(1, 2, 3)) == f(1, 2, 3); +test bool FrepA3() = frepa(f(1, g(2, 3))) == f(1, h(2, 3)); +test bool FrepA4() = frepa(f(1, g ( + 2, + [3, 4, 5] + ))) == f(1, h ( + 2, + [3, 4, 5] + )); +test bool FrepA5() = frepa(f(1, g(2, {3, 4, 5}))) == f(1, h(2, {3, 4, 5})); +test bool FrepA6() = frepa(f(1, g(2, <3, 4, 5>))) == f(1, h(2, <3, 4, 5>)); +test bool FrepA7() + = frepa(f(1, g(2, {<1, 10 >, + <2, 20 >}))) == f(1, h(2, {<1, 10 >, + <2, 20 >})); +test bool FrepA8() = frepa(f(1, g(2, (1 : 10, + 2 : 20)))) == f(1, h(2, (1 : 10, + 2 : 20))); + +// FrepB +test bool FrepB1() = frepb(f(3)) == f(3); +test bool FrepB2() = frepb(f(1, 2, 3)) == f(1, 2, 3); +test bool FrepB3() = frepb(f(1, g(2, 3))) == f(1, h(2, 3)); +test bool FrepB4() = frepb(f(1, g ( + 2, + [3, 4, 5] + ))) == f(1, h ( + 2, + [3, 4, 5] + )); +test bool FrepB5() = frepb(f(1, g(2, {3, 4, 5}))) == f(1, h(2, {3, 4, 5})); +test bool FrepB6() = frepb(f(1, g(2, <3, 4, 5>))) == f(1, h(2, <3, 4, 5>)); +test bool FrepB7() + = frepb(f(1, g(2, {<1, 10 >, + <2, 20 >}))) == f(1, h(2, {<1, 10 >, + <2, 20 >})); +test bool FrepB8() = frepb(f(1, g(2, (1 : 10, + 2 : 20)))) == f(1, h(2, (1 : 10, + 2 : 20))); + +// FrepG2H3a +test bool FrepG2H3a1() = frepG2H3a(f(3)) == f(3); +test bool FrepG2H3a2() = frepG2H3a(f(1, 2, 3)) == f(1, 2, 3); +test bool FrepG2H3a3() = frepG2H3a(f(1, g(2, 3))) == f(1, h(2, 3, 0)); +test bool FrepG2H3a4() + = frepG2H3a(f(1, g ( + 2, + [3, 4, 5] + ))) == f(1, h(2, [3, 4, 5], 0)); +test bool FrepG2H3a5() + = frepG2H3a(f(1, g(2, {3, 4, 5}))) == f(1, h(2, {3, 4, 5}, 0)); +test bool FrepG2H3a6() + = frepG2H3a(f(1, g(2, <3, 4, 5>))) == f(1, h(2, <3, 4, 5>, 0)); +test bool FrepG2H3a7() + = frepG2H3a(f(1, g(2, {<1, 10 >, + <2, 20 >}))) == f(1, h(2, {<1, 10 >, + <2, 20 >}, 0)); +test bool FrepG2H3a8() + = frepG2H3a(f(1, g(2, (1 : 10, + 2 : 20)))) == f(1, h(2, (1 : 10, + 2 : 20), 0)); + +// FrepG2H3b +test bool FrepG2H3b1() = frepG2H3b(f(3)) == f(3); +test bool FrepG2H3b2() = frepG2H3b(f(1, 2, 3)) == f(1, 2, 3); +test bool FrepG2H3b3() = frepG2H3b(f(1, g(2, 3))) == f(1, h(2, 3, 0)); +test bool FrepG2H3b4() + = frepG2H3b(f(1, g ( + 2, + [3, 4, 5] + ))) == f(1, h(2, [3, 4, 5], 0)); +test bool FrepG2H3b5() + = frepG2H3b(f(1, g(2, {3, 4, 5}))) == f(1, h(2, {3, 4, 5}, 0)); +test bool FrepG2H3b6() + = frepG2H3b(f(1, g(2, <3, 4, 5>))) == f(1, h(2, <3, 4, 5>, 0)); +test bool FrepG2H3b7() + = frepG2H3b(f(1, g(2, {<1, 10 >, + <2, 20 >}))) == f(1, h(2, {<1, 10 >, + <2, 20 >}, 0)); +test bool FrepG2H3b8() + = frepG2H3b(f(1, g(2, (1 : 10, + 2 : 20)))) == f(1, h(2, (1 : 10, + 2 : 20), 0)); + +// Inc +test bool Inc1() = inc(f(3)) == f(4); +test bool Inc2() = inc(f(1, 2, 3)) == f(2, 3, 4); +test bool Inc3() = inc(f(1, g(2, 3))) == f(2, g(3, 4)); +test bool Inc4() = inc(f(1, g ( + 2, + [3, 4, 5] + ))) == f(2, g ( + 3, + [4, 5, 6] + )); +test bool Inc5() = inc(f(1, g(2, {3, 4, 5}))) == f(2, g(3, {4, 5, 6})); +test bool Inc6() = inc(f(1, g(2, <3, 4, 5>))) == f(2, g(3, <4, 5, 6>)); +test bool Inc7() + = inc(f(1, g(2, {<1, 10 >, + <2, 20 >}))) == f(2, g(3, {<2, 11 >, + <3, 21 >})); +test bool Inc8() = inc(f(1, g(2, (1 : 10, + 2 : 20)))) == f(2, g(3, (2 : 11, + 3 : 21))); + +// IncAndCount +test bool IncAndCount1() = inc_and_count(f(3), 10) == <1, f(13)>; +test bool IncAndCount2() = inc_and_count(f(1, 2, 3), 10) == <3, f(11, 12, 13)>; +test bool IncAndCount3() + = inc_and_count(f(1, g(2, 3)), 10) == <3, f(11, g(12, 13))>; +test bool IncAndCount4() + = inc_and_count(f(1, g ( + 2, + [3, 4, 5] + )), 10) == <5, f(11, g ( + 12, + [13, 14, 15] + ))>; +test bool IncAndCount5() + = inc_and_count(f(1, g(2, {3, 4, 5})), 10) == <5, f(11, g(12, {13, 14, 15}))>; +test bool IncAndCount6() + = inc_and_count(f(1, g(2, <3, 4, 5>)), 10) == <5, f(11, g(12, <13, 14, 15>))>; +test bool IncAndCount7() + = inc_and_count(f(1, g(2, {<1, 10 >, + <2, 20 >})), 10) == <6, f(11, g(12, {<11, 20 >, + <12, 30 >}))>; +test bool IncAndCount8() + = inc_and_count(f(1, g(2, (1 : 10, + 2 : 20))), 10) == <6, f(11, g(12, (11 : 20, + 12 : 30)))>; + +// Srepl +test bool srepl1() = srepl(f(3)) == f(3); +test bool srepl2() = srepl(g(1, 2)) == h(1, 2); +test bool srepl3() = srepl(g(1, f(g(2, 3)))) == h(1, f(g(2, 3))); +test bool srepl4() = srepl(g(1, f([g(2, 3), 4, 5]))) == h(1, f([g(2, 3), 4, 5])); + +// Order +test bool order1() = order(f1(3)) == [3]; +test bool order2() = order(g1([f1(1), f1(2)])) == [1, 2]; +test bool order3() = order(h1(f1(1), h1(f1(2), f1(3)))) == [1, 2, 3]; +test bool order4() + = order(h1(f1(1), g1([h1(f1(2), f1(3)), f1(4), f1(5)]))) == [1, 2, 3, 4, 5]; + +// VisitWithAnno +data NODE + = nd(NODE left, NODE right) + | leaf(int n) + ; + +anno + int + NODE + @ + pos +; + +NODE N1 = nd(leaf(0)[@pos = 0], leaf(1)[@pos = 1])[@pos = 2] ; + +test bool visitWithAnno1() { + return + visit(leaf(1)[@pos = 1]) { + case leaf(1) => leaf(10) + default: + ; + } == leaf(10); +} + +test bool visitWithAnno2() { + return visit(N1) { + default: + ; + } == N1; +} + +test bool visitWithAnno3() { + return + visit(N1) { + case leaf(1) => leaf(10) + default: + ; + } == nd(leaf(0)[@pos = 0], leaf(10))[@pos = 2]; +} + +test bool visitWithAnno4() { + return + visit(N1) { + case leaf(0) => leaf(0) + case leaf(1) => leaf(10) + default: + ; + } == nd(leaf(0), leaf(10))[@pos = 2]; +} + +test bool visitWithAnno5() { + return + visit(N1) { + case leaf(0) => leaf(0) + case leaf(1) => leaf(10) + case nd(left, right) => nd(right, left) + default: + ; + } == nd(leaf(10), leaf(0)); +} + +public +&T delAnnotationsRec1(&T v) + = visit(v) { + case node n => delAnnotations(n) + }; + +public +&T delAnnotationsRec2(&T v) + = visit(v) { + case node n: { + insert delAnnotations(n); + } + }; + +public NODE A1 = leaf(3) ; +public NODE A2 = leaf(3)[@pos = 1] ; + +test bool visitWithAnno6() = !delAnnotationsRec1(A2)@pos?; + +test bool visitWithAnno7() = !delAnnotationsRec2(A2)@pos?; + +// StringVisit1a +test bool StringVisit1a1() = visit("") { + case /b/: + insert "B"; + } == ""; +test bool StringVisit1a2() = visit("a") { + case /b/: + insert "B"; + } == "a"; +test bool StringVisit1a3() = visit("b") { + case /b/: + insert "B"; + } == "B"; +test bool StringVisit1a4() = visit("abc") { + case /b/: + insert "B"; + } == "aBc"; +test bool StringVisit1a5() + = visit("abcabc") { + case /b/: + insert "B"; + } == "aBcaBc"; + +// StringVisit1b +test bool StringVisit1b1() = visit("") { + case /b/ => "B" + } == ""; +test bool StringVisit1b2() = visit("a") { + case /b/ => "B" + } == "a"; +test bool StringVisit1b3() = visit("b") { + case /b/ => "B" + } == "B"; +test bool StringVisit1b4() = visit("abc") { + case /b/ => "B" + } == "aBc"; +test bool StringVisit1b5() = visit("abcabc") { + case /b/ => "B" + } == "aBcaBc"; + +// StringVisit2 +test bool StringVisit2a1() = visit("") { + case /b/: + insert "BB"; + } == ""; +test bool StringVisit2a2() = visit("a") { + case /b/: + insert "BB"; + } == "a"; +test bool StringVisit2a3() = visit("b") { + case /b/: + insert "BB"; + } == "BB"; +test bool StringVisit2a4() = visit("abc") { + case /b/: + insert "B"; + } == "aBc"; +test bool StringVisit2a5() + = visit("abcabc") { + case /b/: + insert "BB"; + } == "aBBcaBBc"; + +// StringVisit3 +test bool StringVisit3a1() + = visit("") { + case /^a/: + insert "AA"; + case /^b/: + insert "BB"; + } == ""; +test bool StringVisit3a2() + = visit("a") { + case /^a/: + insert "AA"; + case /^b/: + insert "BB"; + } == "AA"; +test bool StringVisit3a3() + = visit("b") { + case /^a/: + insert "AA"; + case /^b/: + insert "BB"; + } == "BB"; +test bool StringVisit3a4() + = visit("abcabc") { + case /^a/: + insert "AA"; + case /^b/: + insert "BB"; + } == "AABBcAABBc"; +test bool StringVisit3a5() + = visit("abcabca") { + case /^a/: + insert "AA"; + case /^b/: + insert "BB"; + } == "AABBcAABBcAA"; + +// StringVisit4 +test bool StringVisit4a1() + = visit("") { + case "a": + insert "AA"; + case /b/: + insert "BB"; + } == ""; +test bool StringVisit4a2() + = visit("a") { + case "a": + insert "AA"; + case /b/: + insert "BB"; + } == "AA"; +test bool StringVisit4a3() + = visit("b") { + case "a": + insert "AA"; + case /b/: + insert "BB"; + } == "BB"; +test bool StringVisit4a4() + = visit("abcabc") { + case "a": + insert "AA"; + case /b/: + insert "BB"; + } == "aBBcaBBc"; +test bool StringVisit4a5() + = visit("abcabca") { + case "a": + insert "AA"; + case /b/: + insert "BB"; + } == "aBBcaBBcAA"; + +// StringVisit5 +tuple[int, int] cntAB(str s) { + int cntA = 0; + int cntB = 0; + visit(s) { + case /^a/: + cntA += 1; + case /^b/: + cntB += 10; + } + + return ; +} + +test bool StringVisit51() = cntAB("") == <0, 0>; +test bool StringVisit52() = cntAB("cdefg") == <0, 0>; +test bool StringVisit53() = cntAB("a") == <1, 0>; +test bool StringVisit54() = cntAB("b") == <0, 10>; +test bool StringVisit55() = cntAB("ab") == <1, 10>; +test bool StringVisit56() = cntAB("ba") == <1, 10>; +test bool StringVisit57() = cntAB("abcabca") == <3, 20>; + +// StringVisit6 +tuple[int, int] TDCntAB(str s) { + int cntA = 0; + int cntB = 0; + top-down visit(s) { + case /^a/: + cntA += 1; + case /^b/: + cntB += 10; + } + + return ; +} + +test bool StringVisit61() = TDCntAB("") == <0, 0>; +test bool StringVisit62() = TDCntAB("cdefg") == <0, 0>; +test bool StringVisit63() = TDCntAB("a") == <1, 0>; +test bool StringVisit64() = TDCntAB("b") == <0, 10>; +test bool StringVisit65() = TDCntAB("ab") == <1, 10>; +test bool StringVisit66() = TDCntAB("ba") == <1, 10>; +test bool StringVisit67() = TDCntAB("abcabca") == <3, 20>; + +// StringVisit7 +str deescape(str s) + = visit(s) { + case /\\ \\ b f n r t]>/m => c + }; + +test bool StringVisit71() = deescape("abc") == "abc"; +test bool StringVisit72() = deescape("\\") == "\\"; +test bool StringVisit73() = deescape("\\\\") == "\\"; +test bool StringVisit74() = deescape("\\\<") == "\<"; +test bool StringVisit75() = deescape("\\\>") == "\>"; +test bool StringVisit76() = deescape("\\n") == "n"; + +// test some unicode features of string visiting +test bool StringUnicodeVisitEmoji1() { + return visit("Hello 🌈World") { + case /🌈/ => "" + } == "Hello World"; +} + +test bool StringUnicodeVisitEmoji1TD() { + return top-down visit("Hello 🌈World") { + case /🌈/ => "" + } == "Hello World"; +} + +test bool StringUnicodeVisitEmoji1TDB() { + return + top-down-break visit("Hello 🌈World") { + case /🌈/ => "" + } == "Hello World"; +} + +test bool StringUnicodeVisitEmoji1BU() { + return bottom-up visit("Hello 🌈World") { + case /🌈/ => "" + } == "Hello World"; +} + +test bool StringUnicodeVisitEmoji1BUB() { + return + bottom-up-break visit("Hello 🌈World") { + case /🌈/ => "" + } == "Hello World"; +} + +test bool StringUnicodeVisitEmoji1INNER() { + return innermost visit("Hello 🌈World") { + case /🌈/ => "" + } == "Hello World"; +} + +test bool StringUnicodeVisitEmoji1OUTER() { + return outermost visit("Hello 🌈World") { + case /🌈/ => "" + } == "Hello World"; +} + +test bool StringUnicodeVisitEmoji2() { + return visit("Hello World") { + case / / => "🌈" + } == "Hello🌈World"; +} +test bool StringUnicodeVisitEmoji3() { + return visit("Hello👍🏽World") { + case /👍🏽/ => "🌈" + } == "Hello🌈World"; +} + +test bool StringUnicodeVisitEmoji4() { + return visit("Hello🫂🌈World") { + case /🫂/ => "🌈" + } == "Hello🌈🌈World"; +} +test bool StringUnicodeVisitEmoji5() { + return visit("Hello🫂🫂🫂World") { + case /[🫂]+/ => "🌈" + } == "Hello🌈World"; +} + +test bool StringUnicodeVisitEmoji6() { + return visit("Hello🫂🫂🫂World") { + case /🫂[🫂]*/ => "🌈" + } == "Hello🌈World"; +} + +test bool StringUnicodeVisitEmoji7() { + return visit("Hello🌈World") { + case /World/ => "🌍" + } == "Hello🌈🌍"; +} + +// Keywords and visit +data RECT = rect(int w, int h, str color = "white"); + +test bool KeywordVisit1() + = visit("f"(1, kw1 = "abc", kw2 = 13)) { + case 1 => 10 + case "abc" => "def" + case 13 => 14 + } == "f"(10, kw1 = "def", kw2 = 14); +test bool KeywordVisit2() + = visit(rect(10, 20, color = "white")) { + case "white" => "red" + } == rect(10, 20, color = "red"); + +test bool nestedEmptyStringVisit1() { + x = [""]; + result = []; + bottom-up visit(x) { + case value y: + result += [y]; + } + + return result == ["", [""]]; +} + +test bool nestedEmptyStringVisit2() { + x = ["", "1", "2"]; + result = []; + bottom-up visit(x) { + case value y: + result += [y]; + } + + return result == ["", "1", "2", ["", "1", "2"]]; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/Visit2.rsc| +module lang::rascal::tests::functionality::Visit2 + +import Grammar; +import ParseTree; + +anno + int + Symbol + @ + id +; + +Grammar G0 + = grammar( + {sort("S")[@id = 2]}, + ( + sort("S")[@id = 3] : choice( + sort("S")[@id = 4], {prod(sort("S")[@id = 5], [lit("0")[@id = 6]], {})} + ), + lit("0")[@id = 7] : choice( + lit("0")[@id = 8], + {prod(lit("0")[@id = 9], [\char-class([range(48, 48)])[@id = 10]], {})} + ) + ) + ) ; + +test bool cntLit() { + cnt = 0; + visit(G0) { + case lit(_): + cnt += 1; + } + ; + return cnt == 4; +} + +test bool cntLitCC() { + cnt = 0; + visit(G0) { + case lit(_): + cnt += 1; + case \char-class(_): + cnt += 1; + } + return cnt == 5; +} + +test bool cntInt() { + cnt = 0; + visit(G0) { + case int _: + cnt += 1; + } + return cnt == 11; +} + +// visit does go into kw params +test bool cntStr() { + cnt = 0; + visit(G0) { + case str _: + cnt += 1; + } + return cnt == 8; +} + +test bool cntIntStr() { + cnt = 0; + visit(G0) { + case int _: + cnt += 1; + case str _: + cnt += 1; + } + return cnt == 19; +} + +test bool cntProd() { + cnt = 0; + visit(G0) { + case prod(_, _, _): + cnt += 1; + } + return cnt == 2; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/functionality/VisitOptimized.rsc| +@license{ + Copyright (c) 2009-2022 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +module lang::rascal::tests::functionality::VisitOptimized + +// visit implementations may group patterns by outermost function +// symbol. In issue #1719 we saw that this optimization introduced +// a bug that would leak pattern variables between cases of the same +// outermost function symbol when the pattern failed halfway through. +// these tests would trigger that bug. +data Bool + = \true() + | \false() + | \and(Bool lhs, Bool rhs) + | \or(Bool lhs, Bool rhs) + | \not(Bool arg) + | \cond(Bool cond, Bool lhs, Bool rhs) + | \null() + ; + +public list[Bool] examples + = [ + and(and(and(\true(), and(\false(), \true())), \true()), \false()), + and( + \true(), cond(and(\false(), \true()), or(\false(), \true()), \false()) + ), + or(\false(), \true()), + and(\true(), cond(and(\true(), \true()), or(\false(), \true()), \false())), + and( + \true(), + cond(and(\false(), \true()), null(), cond(\true(), null(), null())) + ) + ] ; + +Bool reduce(Bool b) + = innermost visit(b) { + case \and(\true(), X) => X + case \and(X: \false(), _) => X + case \and(Bool X, \true()) => X + case \and(_, X: \false()) => X + case \or(X: \true(), _) => X + case \or(_, X: \true()) => X + case \or(\false(), X) => X + case \or(Bool X, \false()) => X + case \cond(\true(), X, _) => X + case \cond(\false(), _, X) => X + case 1 => 1 + }; + +bool normal(\false()) = true; +bool normal(\true()) = true; +bool normal(null()) = true; +default bool normal(Bool _) = false; + +test bool allExamplesNormalAfterVisit() + = all(x <- examples, normal(reduce(x))); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/C1.rsc| +module lang::rascal::tests::imports::C1 + +import lang::rascal::tests::imports::C2; + +data D = d(int n); + +alias C1Alias = int; + +bool isDint(d(int _)) = true; +default bool isDint(D _) = false; + +C1Alias C1func(C2Alias i) = i; + +public int C1function() { + return 1; +} + +int C1testFunction(int() f = C2function) = f(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/C2.rsc| +module lang::rascal::tests::imports::C2 + +import lang::rascal::tests::imports::C1; + +data D = d(str s); + +alias C2Alias = int; + +bool isDstr(d(str _)) = true; +default bool isDstr(D _) = false; + +C2Alias C2func(C1Alias i) = i; + +public int C2function() { + return 2; +} + +int C2testFunction(int() f = C1function) = f(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/CyclicImports1.rsc| +module lang::rascal::tests::imports::CyclicImports1 + +import lang::rascal::tests::imports::C1; +import lang::rascal::tests::imports::C2; + +test bool Cycle1() = isDint(d(13)) == true; +test bool Cycle2() = isDint(d("abc")) == false; + +test bool Cycle3() = isDstr(d("abc")) == true; +test bool Cycle4() = isDstr(d(13)) == false; + +test bool Cycle5() = C2func(1) == 1; +test bool Cycle6() = C1func(2) == 2; + +test bool Cycle7() = C1testFunction() == 2; +test bool Cycle8() = C2testFunction() == 1; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/IMP1.rsc| +module lang::rascal::tests::imports::IMP1 + +public str dup_imp1(str s) = s + s + "_imp1"; + +value main_imp1(list[value] _) = dup_imp1("IMP1;"); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/IMP2.rsc| +module lang::rascal::tests::imports::IMP2 + +import lang::rascal::tests::imports::IMP1; + +public str dup_imp2(str s) = s + s + "_imp2"; + +value main_imp2(list[value] _) + = [main_imp1([]), dup_imp1("IMP2"), dup_imp2("IMP2;")]; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/Imports1.rsc| +module lang::rascal::tests::imports::Imports1 + +import lang::rascal::tests::imports::M1; + +test bool Test11() = lang::rascal::tests::imports::M1::f(3) == 6; + +test bool Test12() = f(3) == 6; + +test bool Test13() { + int f(int n) { + return 3 * n; + } + return f(3) == 9; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/Imports2.rsc| +module lang::rascal::tests::imports::Imports2 + +import lang::rascal::tests::imports::M2; + +test bool Test21() = lang::rascal::tests::imports::M2::n == 3; + +test bool Test22() = n == 3; + +test bool Test23() { + int n = 4; + return n == 4; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/Imports3.rsc| +module lang::rascal::tests::imports::Imports3 + +import lang::rascal::tests::imports::Mbase; + +test bool Test31() = lang::rascal::tests::imports::Mbase::n == 2; + +test bool Test32() = n == 2; + +test bool Test33() = lang::rascal::tests::imports::Mbase::f(3) == 6; + +test bool Test34() = f(3) == 6; + +test bool Test35() { + int n = 3; + return n == 3; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/Imports4.rsc| +module lang::rascal::tests::imports::Imports4 + +import lang::rascal::tests::imports::M4; + +test bool Test41() = lang::rascal::tests::imports::M4::m == 2; + +test bool Test42() = lang::rascal::tests::imports::M4::f() == 2; + +test bool Test43() = lang::rascal::tests::imports::M4::g() == 2; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/Imports5.rsc| +module lang::rascal::tests::imports::Imports5 + +import lang::rascal::tests::imports::M5; + +import lang::rascal::tests::imports::Mbase; + +test bool Test51() = lang::rascal::tests::imports::Mbase::n == 2; + +test bool Test52() = lang::rascal::tests::imports::Mbase::f(3) == 6; + +test bool Test53() = lang::rascal::tests::imports::M5::m == 3; + +test bool Test54() = lang::rascal::tests::imports::M5::g(3) == 9; + +test bool Test55() = lang::rascal::tests::imports::M5::h(3) == 6; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/Imports6.rsc| +module lang::rascal::tests::imports::Imports6 + +import lang::rascal::tests::imports::M6; + +test bool Test61() = f() == 3; + +test bool Test62() = g() == 3; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/Imports7.rsc| +module lang::rascal::tests::imports::Imports7 + +import lang::rascal::tests::imports::M7; + +test bool Test71() = natural() == natural(); + +test bool Test72() = string() == string(); + +test bool Test73() = natural() != string(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/Imports8.rsc| +module lang::rascal::tests::imports::Imports8 + +import lang::rascal::tests::imports::IMP1; +import lang::rascal::tests::imports::IMP2; + +test bool import1() = main_imp1([]) == "IMP1;IMP1;_imp1"; +test bool import2() + = main_imp2([]) == ["IMP1;IMP1;_imp1", "IMP2IMP2_imp1", "IMP2;IMP2;_imp2"]; +test bool import3() = dup_imp1("IMP3") == "IMP3IMP3_imp1"; +test bool import4() = dup_imp2("IMP3") == "IMP3IMP3_imp2"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/Imports9.rsc| +module lang::rascal::tests::imports::Imports9 + +// Note: import is only used when running tests with interpreter +import lang::rascal::tests::imports::M9; + +test bool Test91() { + int f(3) = 3; + return f(3) == 3; +} + +@ignoreCompiler{INCOMPATIBILITY: Overloading of functions across scopes not supported in compiler} +test bool Test92() { + int f(3) = 30; + return f(1) == 1 && f(3) == 30; +} + +@ignoreCompiler{INCOMPATIBILITY: Overloading of functions across scopes not supported in compiler} +test bool Test93() { + int f(3) = 300; + return f(5) == 50 && f(3) == 300; +} + +test bool Test94() { + int f(3) = 3000; + default int f(int n) = 20 * n; + return f(5) == 100; +} + +@ignoreCompiler{INCOMPATIBILITY: Overloading of functions across scopes not supported in compiler} +test bool Test95() { + int f(3) = 3; + default int f(int n) = 20 * n; + + int g(int n) { + int f(4) = 44; + default int f(int n) = 15 * n; + + return f(n); + } + + int h(int n) { + int f(4) = 48; + default int f(int n) = 20 * n; + + return f(n); + } + return + f(1) == 1 && f(3) == 3 && f(5) == 100 && g(4) == 44 && g(5) == 75 && h(4) == 48 && h(5) == 100; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/M1.rsc| +module lang::rascal::tests::imports::M1 + +public int f(int n) { + return 2 * n; +} + +private int g(int n) { + return 2 * n; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/M2.rsc| +module lang::rascal::tests::imports::M2 + +public int n = 3 ; + +/* unused on purpose; to test visibility constraints */private int m = 3 ; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/M4.rsc| +module lang::rascal::tests::imports::M4 + +import lang::rascal::tests::imports::Mbase; + +public int m = n ; + +public int f() { + return n; +} + +public int g() { + return m; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/M5.rsc| +module lang::rascal::tests::imports::M5 + +import lang::rascal::tests::imports::Mbase; + +public int g(int n) { + return 3 * n; +} + +public int h(int n) { + return f(n); +} + +public int m = 3 ; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/M6.rsc| +module lang::rascal::tests::imports::M6 + +import Set; + +public set[int] Procs = {1, 2, 3} ; + +public int f() { + int nProcs = Set::size(Procs); + return nProcs; +} + +public int g() { + int nProcs = size(Procs); + return nProcs; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/M7.rsc| +module lang::rascal::tests::imports::M7 + +public data TYPE + = natural() + | string() + ; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/M9.rsc| +module lang::rascal::tests::imports::M9 + +int f(1) = 1; +int f(2) = 2; + +default int f(int n) = 10 * n; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/Mbase.rsc| +module lang::rascal::tests::imports::Mbase + +public int f(int n) { + return 2 * n; +} + +public int n = 2 ; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/imports/ModuleInitRange.rsc| +module lang::rascal::tests::imports::ModuleInitRange + +list[int] ints = [0..10] ; + +test bool rangeInit() = ints == [0..10]; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/Boolean.rsc| +module lang::rascal::tests::library::Boolean + +/******************************************************************************* + * Copyright (c) 2009-2015 CWI + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + + * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI + * * Paul Klint - Paul.Klint@cwi.nl - CWI + * * Bert Lisser - Bert.Lisser@cwi.nl - CWI + *******************************************************************************/import Boolean; + +// arb +test bool arb1() { + bool B = Boolean::arbBool(); + return (B == true) || (B == false); +} + +test bool arb2() { + bool B = arbBool(); + return (B == true) || (B == false); +} + +// toInt +test bool toInt1() = Boolean::toInt(false) == 0; +test bool toInt2() = Boolean::toInt(true) == 1; + +test bool toInt3() = toInt(false) == 0; +test bool toInt4() = toInt(true) == 1; + +// toReal +test bool toReal1() = Boolean::toReal(false) == 0.0; +test bool toReal2() = Boolean::toReal(true) == 1.0; + +test bool toReal3() = toReal(false) == 0.0; +test bool toReal4() = toReal(true) == 1.0; + +// testToString +test bool testToString1() = Boolean::toString(false) == "false"; +test bool testToString2() = Boolean::toString(true) == "true"; +test bool testToString3() = toString(false) == "false"; +test bool testToString4() = toString(true) == "true"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/DateTime.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jouke Stoel - Jouke.Stoel@cwi.nl - CWI} +module lang::rascal::tests::library::DateTime + +import DateTime; + +import String; + +test bool createDate_sampled(datetime gen) + = date.year == gen.year + && date.month == gen.month + && date.day == gen.day + when date := createDate(gen.year, gen.month, gen.day); + +test bool createTime_sampled(datetime gen) + = time.hour == gen.hour + && time.minute == gen.minute + && time.second == gen.second + && time.millisecond == gen.millisecond + when time := createTime(gen.hour, gen.minute, gen.second, gen.millisecond); + +test bool printDate_simpleFormat(datetime gen) + = printDate(gen) == formattedDate(gen); + +test bool printTime_simpleFormat(datetime gen) + = printTime(gen) == formattedTime(gen); + +test bool printDateTime_simpleFormat(datetime gen) + = printDateTime(gen) == " "; + +test bool incrementDays_withOneDay(datetime gen) + = gen.year > 1751 + ? incrementDays(createDate(gen.year, gen.month, gen.day)) == incDateByOneDay(gen) + : true; + +// TIL; apparently before the year 1752 the US was still on the Julian calendar which calculated a leap year every 4 years. The algorithm used here only works for the Gregorian calendar +// Increment a date by a day according to the Gregorian calendar algorithm for leap year calculation +datetime incDateByOneDay(datetime dt) { + if (dt.day < 28) { + return createDate(dt.year, dt.month, dt.day + 1); + } + else if (dt.month in {1, 3, 5, 7, 8, 10, 12}) { + if (dt.day < 31) { + return createDate(dt.year, dt.month, dt.day + 1); + } + else if (dt.month < 12) { + return createDate(dt.year, dt.month + 1, 1); + } + else { + return createDate(dt.year + 1, 1, 1); + } + } + else if (dt.month == 2) { + if (dt.day < 27) { + return createDate(dt.year, dt.month, dt.day + 1); + } + else if (dt.day == 28 && ((dt.year % 400 == 0) || (dt.year % 4 == 0 && dt.year % 100 > 0))) { + // leap year + return createDate(dt.year, dt.month, dt.day + 1); + } + else { + return createDate(dt.year, dt.month + 1, 1); + } + } + else { + if (dt.day < 30) { + return createDate(dt.year, dt.month, dt.day + 1); + } + else { + return createDate(dt.year, dt.month + 1, 1); + } + } +} + +str formattedDate(datetime dt) + = "--"; + +str formattedTime(datetime dt) + = "::.+"; + +private str fill(int val) = fill(val, 2); +private str fill(int val, int n) = right("", n, "0"); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/IO.rsc| +module lang::rascal::tests::library::IO + +import IO; +import DateTime; +import String; + +test bool testFileCopyCompletely() { + writeFile(|tmp:///longFile|, "123456789"); + writeFile(|tmp:///shortFile|, "321"); + + copy(|tmp:///shortFile|, |tmp:///longFile|, overwrite = true); + + return readFile(|tmp:///longFile|) == readFile(|tmp:///shortFile|); +} + +test bool testFileCopyRecursive() { + writeFile(|tmp:///a/b/c/d/longFile|, "123456789"); + writeFile(|tmp:///a/b/e/shortFile|, "321"); + copy(|tmp:///a/|, |tmp:///g/|, recursive = true, overwrite = true); + return + readFile(|tmp:///a/b/c/d/longFile|) == readFile(|tmp:///g/b/c/d/longFile|); +} + +test bool watchDoesNotCrashOnURIRewrites() { + writeFile( + |tmp:///watchDoesNotCrashOnURIRewrites/someFile.txt|, "123456789" + ); + watch( + |tmp:///watchDoesNotCrashOnURIRewrites|, true, + void (FileSystemChange event) { + // this should trigger the failing test finally + remove(event.file); + }); + return true; +} + +test bool createdDoesNotCrashOnURIRewrites() { + loc l = |tmp:///createdDoesNotCrashOnURIRewrites/someFile.txt|; + remove(l); + // remove the file if it exists + writeFile(l, "123456789"); + return IO::created(l) <= now(); +} + +test bool testWriteBase32() { + str original = "Hello World!"; + writeBase32(|memory:///base32Test/writeTest.txt|, toBase32(original)); + return original == readFile(|memory:///base32Test/writeTest.txt|); +} + +test bool testReadBase32() { + str original = "Hello World!"; + writeFile(|memory:///base32Test/readTest.txt|, original); + str encoded = readBase32(|memory:///base32Test/readTest.txt|); + return original == fromBase32(encoded); +} + +test bool testRenameWithinFileScheme() { + remove(|tmp:///bye.txt|); + writeFile(|tmp:///hello.txt|, "Hello World!"); + rename(|tmp:///hello.txt|, |tmp:///bye.txt|); + return readFile(|tmp:///bye.txt|) == "Hello World!"; +} + +test bool testRenameWithinMemoryScheme() { + remove(|memory:///bye.txt|); + writeFile(|memory://testRename/hello.txt|, "Hello World!"); + rename(|memory://testRename/hello.txt|, |memory:///bye.txt|); + return readFile(|memory:///bye.txt|) == "Hello World!"; +} + +test bool testRenameCrossScheme() { + remove(|tmp:///bye.txt|); + writeFile(|memory://testRename/hello.txt|, "Hello World!"); + rename(|memory://testRename/hello.txt|, |tmp:///bye.txt|); + return readFile(|tmp:///bye.txt|) == "Hello World!"; +} + +test bool renameDirectory() { + remove(|tmp:///RenamedFolder|, recursive = true); + writeFile(|tmp:///Folder/hello.txt|, "Hello World!"); + rename(|tmp:///Folder|, |tmp:///RenamedFolder|); + return readFile(|tmp:///RenamedFolder/hello.txt|) == "Hello World!"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/Integer.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::library::Integer + +import util::Math; + +// abs +test bool abs1() = abs(0) == 0; +test bool abs2() = abs(-1) == 1; +test bool abs3() = abs(1) == 1; + +// arbInt +test bool arbInt1() { + int N = arbInt(10); + return (N >= 0) && (N < 10); +} + +test bool arbInt2() { + arbInt(); + return true; +} + +// max +test bool max1() = max(3, 10) == 10; +test bool max2() = max(10, 10) == 10; + +// min +test bool min1() = min(3, 10) == 3; +test bool min2() = min(10, 10) == 10; + +// toReal +test bool toReal1() = toReal(3) == 3.0; + +// testToString +test bool testToString1() = toString(314) == "314"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/List.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +@contributor{Vadim Zaytsev - vadim@grammarware.net - UvA} +module lang::rascal::tests::library::List + +import Exception; +import List; + +// concat +//concat1 violates stricter typing rules of compiler +//test bool concat1() = concat([]) == []; +test bool concat2() = concat([[]]) == []; +test bool concat3() = concat(concat([[[]]])) == []; +test bool concat4() = concat([[1]]) == [1]; +test bool concat5() = concat([[1], [], [2, 3]]) == [1, 2, 3]; +test bool concat6() = concat([[1, 2], [3], [4, 5], []]) == [1, 2, 3, 4, 5]; + +// delete +test bool delete1() = delete([0, 1, 2], 0) == [1, 2]; +test bool delete2() = delete([0, 1, 2], 1) == [0, 2]; +test bool delete3() = delete([0, 1, 2], 2) == [0, 1]; +@expected{IndexOutOfBounds} +test bool delete4() { + delete([0, 1, 2], 3); + return false; +} + +// distribution +test bool distribution1() = distribution([]) == (); +test bool distribution2() = distribution([1]) == (1 : 1); +test bool distribution3() = distribution([1, 2]) == (1 : 1, + 2 : 1); +test bool distribution4() = distribution([1, 2, 2]) == (1 : 1, + 2 : 2); +test bool distribution5() = distribution([2, 2, 2]) == (2 : 3); +test bool distribution6() = distribution([[]]) == ([] : 1); + +// drop +test bool drop1() = drop ( + 0, + [] + ) == []; +test bool drop2() = drop ( + 0, + [1] + ) == [1]; +test bool drop3() = drop ( + 0, + [1, 2] + ) == [1, 2]; +test bool drop4() = drop ( + 1, + [] + ) == []; +test bool drop5() = drop ( + 1, + [1] + ) == []; +test bool drop6() = drop ( + 1, + [1, 2] + ) == [2]; + +// dup +test bool dup1() = dup([]) == []; +test bool dup2() = dup([1]) == [1]; +test bool dup3() = dup([1, 2]) == [1, 2]; +test bool dup4() = dup([1, 1]) == [1]; +test bool dup5() = dup([1, 1, 2]) == [1, 2]; +test bool dup6() = dup([1, 1, 2, 2]) == [1, 2]; + +// elementAt - deprecated! +@ignoreCompiler{Remove-after-transtion-to-compiler: Other exception} +@expected{NoSuchElement} +test bool elementAt1() { + [][0]; + return false; +} +@ignoreInterpreter{Other exception} +@expected{IndexOutOfBounds} +test bool elementAt1() { + [][0]; + return false; +} +test bool elementAt2() = [1, 2, 3][0] == 1; +test bool elementAt3() = [1, 2, 3][1] == 2; +test bool elementAt4() = [1, 2, 3][2] == 3; +@expected{IndexOutOfBounds} +test bool elementAt5() { + [1, 2, 3][3]; + return false; +} +test bool elementAt6() = [1, 2, 3][-1] == 3; +test bool elementAt7() = [1, 2, 3][-2] == 2; +test bool elementAt8() = [1, 2, 3][-3] == 1; +@expected{IndexOutOfBounds} +test bool elementAt9() { + [1, 2, 3][-4]; + return false; +} + +// getOneFrom +@expected{EmptyList} +test bool getOneFrom1() { + getOneFrom([]); + return false; +} +test bool getOneFrom2() { + int N = getOneFrom([1]); + return N == 1; +} +test bool getOneFrom3() { + int N = getOneFrom([1, 2]); + return (N == 1) || (N == 2); +} +test bool getOneFrom4() { + int N = getOneFrom([1, 2, 3]); + return (N == 1) || (N == 2) || (N == 3); +} +test bool getOneFrom5() { + real D = getOneFrom([1.0, 2.0]); + return (D == 1.0) || (D == 2.0); +} +test bool getOneFrom6() { + str S = getOneFrom(["abc", "def"]); + return (S == "abc") || (S == "def"); +} + +// head/1 +@expected{EmptyList} +test bool head1a() { + head([]); + return false; +} +test bool head2a() = head([1]) == 1; +test bool head3a() = head([1, 2]) == 1; + +// head/2 +test bool head1b() = head([1, 2, 3, 4], 0) == []; +test bool head2b() = head([1, 2, 3, 4], 1) == [1]; +test bool head3b() = head([1, 2, 3, 4], 2) == [1, 2]; +test bool head4b() = head([1, 2, 3, 4], 3) == [1, 2, 3]; +test bool head5b() = head([1, 2, 3, 4], 4) == [1, 2, 3, 4]; +@expected{IndexOutOfBounds} +test bool head6b() { + head([], 1); + return false; +} +@expected{IndexOutOfBounds} +test bool head7b() { + head([], 3); + return false; +} +@expected{IndexOutOfBounds} +test bool head8b() { + head([1, 2], 3); + return false; +} +@expected{IndexOutOfBounds} +test bool head9b() { + head([], -1); + return false; +} + +// headTail - see pop +// index +test bool index1() = index([]) == []; +test bool index2() = index([1]) == [0]; +test bool index3() = index([1, 2]) == [0, 1]; +test bool index4() = index([10..1]) == [0..9]; + +// indexOf +test bool indexOf1() = indexOf([], 1) == -1; +test bool indexOf2() = indexOf([1], 1) == 0; +test bool indexOf3() = indexOf([1, 2], 1) == 0; +test bool indexOf4() = indexOf([2, 1], 1) == 1; +test bool indexOf5() = indexOf([1, 2, 1], 1) == 0; + +// insertAt +test bool insertAt1() = insertAt([], 0, 1) == [1]; +@expected{IndexOutOfBounds} +test bool insertAt2() { + insertAt([], 1, 1) == [1]; + return false; +} +test bool insertAt3() = insertAt([2, 3], 0, 1) == [1, 2, 3]; +test bool insertAt4() = insertAt([2, 3], 1, 1) == [2, 1, 3]; +test bool insertAt5() = insertAt([2, 3], 2, 1) == [2, 3, 1]; + +// intercalate +test bool intercalate1() = intercalate ( + ",", + [] + ) == ""; +test bool intercalate2() = intercalate ( + "!", + [1] + ) == "1"; +test bool intercalate3() = intercalate ( + ",", + [1, 2] + ) == "1,2"; + +// intersperse +test bool intersperse1() = intersperse ( + 0, + [] + ) == []; +test bool intersperse2() = intersperse ( + 0, + [1] + ) == [1]; +test bool intersperse3() = intersperse ( + 0, + [1, 2] + ) == [1, 0, 2]; + +// isEmpty +test bool isEmpty1() = isEmpty([]); +test bool isEmpty2() = !isEmpty([0]); +test bool isEmpty3() = !isEmpty([1, 2]); + +// last +@expected{EmptyList} +test bool last1() { + last([]); + return false; +} +test bool last2() = last([1]) == 1; +test bool last3() = last([1, 2]) == 2; + +// lastIndexOf +test bool lastIndexOf1() = lastIndexOf([], 1) == -1; +test bool lastIndexOf2() = lastIndexOf([1], 1) == 0; +test bool lastIndexOf3() = lastIndexOf([1, 2], 1) == 0; +test bool lastIndexOf4() = lastIndexOf([2, 1], 1) == 1; +test bool lastIndexOf5() = lastIndexOf([1, 2, 1], 1) == 2; + +// mapper +test bool mapper1() = mapper([], int (int n) { + return n * 10; + }) == []; +test bool mapper2() = mapper([1, 2], int (int n) { + return n; + }) == [1, 2]; +test bool mapper3() = mapper([1, 2, 3], int (int n) { + return n + 1; + }) == [2, 3, 4]; + +// max +@expected{EmptyList} +test bool max1() { + max([]); + return false; +} +test bool max2() = max([1, 1, 1]) == 1; +test bool max3() = max([1, 2, 3, 2, 1]) == 3; +test bool max4() = max([-1, -2, -3]) == -1; + +// merge +test bool merge1() = merge ( + [], + [] + ) == []; +test bool merge2() = merge ( + [1], + [] + ) == [1]; +test bool merge3() = merge ( + [], + [2] + ) == [2]; +test bool merge4() = merge ( + [1], + [2] + ) == [1, 2]; +test bool merge5() = merge ( + [2], + [2] + ) == [2, 2]; +test bool merge6() = merge ( + [3], + [2] + ) == [2, 3]; + +// min +@expected{EmptyList} +test bool min1() { + min([]); + return false; +} +test bool min2() = min([1, 1, 1]) == 1; +test bool min3() = min([1, 2, 3, 2, 1]) == 1; +test bool min4() = min([-1, -2, -3]) == -3; + +// mix +test bool mix1() = mix ( + [], + [] + ) == []; +test bool mix2() = mix ( + [], + [1] + ) == [1]; +test bool mix3() = mix ( + [], + [1, 2] + ) == [1, 2]; +test bool mix4() = mix ( + [1], + [] + ) == [1]; +test bool mix5() = mix ( + [1, 2], + [] + ) == [1, 2]; +test bool mix6() = mix ( + [1, 3], + [2] + ) == [1, 2, 3]; +test bool mix7() = mix ( + [1, 3], + [2, 4] + ) == [1, 2, 3, 4]; + +// permutations +test bool permutations1() = permutations([]) == {[]}; +test bool permutations2() = permutations([1]) == {[1]}; +test bool permutations3() = permutations([1, 2]) == {[1, 2], [2, 1]}; +test bool permutations4() + = permutations([1, 2, 3]) == {[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]}; +test bool permutations5() = permutations([1, 1]) == {[1, 1]}; +test bool permutations6() = permutations([1, 1, 1]) == {[1, 1, 1]}; + +// pop +@expected{EmptyList} +test bool pop1() { + pop([]); + return false; +} +test bool pop2() = pop([1]) == <1, []>; +test bool pop3() = pop([1, 2]) == <1, [2]>; +test bool pop4() = pop([1, 2, 3]) == <1, [2, 3]>; + +// prefix +test bool prefix1() = prefix([]) == []; +test bool prefix2() = prefix([1]) == []; +test bool prefix3() = prefix([1, 2]) == [1]; +test bool prefix4() = prefix(prefix([1, 2])) == []; + +// push +test bool push1() = push ( + 0, + [] + ) == [0]; +test bool push2() = push ( + 1, + [2] + ) == [1, 2]; +test bool push3() = push ( + 1, + [2, 3] + ) == [1, 2, 3]; + +// reducer - deprecated! +// remove +test bool remove1() = remove([], 0) == []; +test bool remove2() = remove([1], 0) == []; +test bool remove3() = remove([1, 2], 0) == [2]; +test bool remove4() = remove([1, 2], 1) == [1]; +test bool remove5() = remove([1, 2], 2) == [1, 2]; +test bool remove6() = remove([1, 2], -1) == [1, 2]; +test bool remove7() = remove([1], -1) == [1]; +test bool remove8() = remove([], 0) == []; + +// reverse +test bool reverse1() = reverse([]) == []; +test bool reverse2() = reverse([1]) == [1]; +test bool reverse3() = reverse([1, 1]) == [1, 1]; +test bool reverse4() = reverse([1, 2]) == [2, 1]; +test bool reverse5() = reverse([1, 2, 3]) == [3, 2, 1]; + +// size +test bool size1() = 0 == size([]); +test bool size2() = 1 == size([1]); +test bool size3() = 2 == size([1, 2]); +test bool size4() = 3 == size([1, 2, 3]); + +// slice +test bool slice1() = slice([1, 2, 3, 4], 0, 0) == []; +test bool slice2() = slice([1, 2, 3, 4], 0, 1) == [1]; +test bool slice3() = slice([1, 2, 3, 4], 0, 2) == [1, 2]; +test bool slice4() = slice([1, 2, 3, 4], 0, 3) == [1, 2, 3]; +test bool slice5() = slice([1, 2, 3, 4], 0, 4) == [1, 2, 3, 4]; +test bool slice6() = slice([1, 2, 3, 4], 1, 0) == []; +test bool slice7() = slice([1, 2, 3, 4], 1, 1) == [2]; +test bool slice8() = slice([1, 2, 3, 4], 1, 2) == [2, 3]; +test bool slice9() = slice([1, 2, 3, 4], 3, 0) == []; +test bool slice10() = slice([1, 2, 3, 4], 3, 1) == [4]; + +// sort/1 +test bool sort1a() = sort([]) == []; +test bool sort1b() = sort([1]) == [1]; +test bool sort1c() = sort([1, 2]) == [1, 2]; +test bool sort1d() = sort([2, 1]) == [1, 2]; +test bool sort1e() = sort([2, -1, 4, -2, 3]) == [-2, -1, 2, 3, 4]; +test bool sort1f() = sort([1, 2, 3, 4, 5, 6]) == [1, 2, 3, 4, 5, 6]; +test bool sort1g() = sort([1, 1, 1, 1, 1, 1]) == [1, 1, 1, 1, 1, 1]; +test bool sort1h() = sort([1, 1, 0, 1, 1]) == [0, 1, 1, 1, 1]; +test bool sort1i() + = sort([ + "mango", "strawberry", "pear", "pineapple", "banana", "grape", "kiwi" + ]) == ["banana", "grape", "kiwi", "mango", "pear", "pineapple", "strawberry"]; + +// sort/2 +test bool sort2a() = sort([1, 2, 3], bool (int a, int b) { + return a < b; + }) == [1, 2, 3]; +test bool sort2b() = sort([1, 3, 2], bool (int a, int b) { + return a < b; + }) == [1, 2, 3]; +test bool sort2c() = sort([1, 3, 2], bool (int a, int b) { + return a > b; + }) == [3, 2, 1]; +test bool sort2d() = sort([3, 2, 1], bool (int a, int b) { + return a > b; + }) == [3, 2, 1]; +@expected{IllegalArgument} +test bool sort2e() { + sort([1, 2, 3], bool (int a, int b) { + return a <= b; + }); + return false; +} +@expected{IllegalArgument} +test bool sort2f() { + sort([1, 2, 3], bool (int a, int b) { + return a >= b; + }); + return false; +} + +bool less(int a, int b) = a < b; +bool lesseq(int a, int b) = a <= b; +bool greater(int a, int b) = a > b; +bool greatereq(int a, int b) = a >= b; + +test bool sort2g() = sort([1, 2, 3], less) == [1, 2, 3]; +test bool sort2h() = sort([1, 3, 2], less) == [1, 2, 3]; +test bool sort2i() = sort([1, 3, 2], greater) == [3, 2, 1]; +test bool sort2j() = sort([3, 2, 1], greater) == [3, 2, 1]; +@expected{IllegalArgument} +test bool sort2k() { + sort([1, 2, 3], lesseq); + return false; +} +@expected{IllegalArgument} +test bool sort2l() { + sort([1, 2, 3], greatereq); + return false; +} + +test bool shuffleFirstIndex(list[value] v) + = v == [] || ({shuffle(v)[0]| _ <- [0..50 * size(v)]} == {*v}); +test bool shuffleLastIndex(list[value] v) + = v == [] || ({shuffle(v)[size(v) - 1]| _ <- [0..50 * size(v)]} == {*v}); +test bool shuffleStable(list[value] v, int x) + = v == [] || shuffle(v, x) == shuffle(v, x); + +// split +test bool split1() = split([]) == <[], []>; +test bool split2() = split([1]) == <[], [1]>; +test bool split3() = split([1, 2]) == <[1], [2]>; +test bool split4() = split([1, 2, 3]) == <[1], [2, 3]>; +test bool split5() = split([1, 2, 3, 4]) == <[1, 2], [3, 4]>; + +// sum +test bool sum1() = sum([0]) == 0; +test bool sum2() = sum([1]) == 1; +test bool sum3() = sum([1, 2]) == 3; +test bool sum4() = sum([1, 2, 3]) == 6; +@expected{EmptyList} +test bool sum5() { + sum([]); + return false; +} + +// tail/1 +@expected{EmptyList} +test bool tail1a() { + tail([]); + return false; +} +test bool tail2a() = tail([1]) == []; +test bool tail3a() = tail([1, 2]) == [2]; +test bool tail4a() = tail(tail([1, 2])) == []; + +// tail/2 +test bool tail1b() = tail([1, 2, 3, 4], 0) == []; +test bool tail2b() = tail([1, 2, 3, 4], 1) == [4]; +test bool tail3b() = tail([1, 2, 3, 4], 2) == [3, 4]; +test bool tail4b() = tail([1, 2, 3, 4], 3) == [2, 3, 4]; +test bool tail5b() = tail([1, 2, 3, 4], 4) == [1, 2, 3, 4]; +@expected{IndexOutOfBounds} +test bool tail6b() { + tail([], 1); + return false; +} +@expected{IndexOutOfBounds} +test bool tail7b() { + tail([], 3); + return false; +} +@expected{IndexOutOfBounds} +test bool tail8b() { + tail([1, 2], 3); + return false; +} +@expected{IndexOutOfBounds} +test bool tail9b() { + tail([], -1); + return false; +} + +// takeOneFrom +test bool takeOneFrom1() { + = takeOneFrom([1]); + return (E == 1) && (L == []); +} +test bool takeOneFrom2() { + = takeOneFrom([1, 2]); + return ((E == 1) && (L == [2])) || ((E == 2) && (L == [1])); +} +@expected{EmptyList} +test bool takeOneFrom3() { + takeOneFrom([]); + return false; +} + +// takeWhile +test bool takeWhile1() = takeWhile([], bool (int x) { + return x mod 2 == 0; + }) == []; +test bool takeWhile2() + = takeWhile([1, 2], bool (int x) { + return x mod 2 == 0; + }) == []; +test bool takeWhile3() + = takeWhile([2, 1], bool (int x) { + return x mod 2 == 0; + }) == [2]; +test bool takeWhile4() = takeWhile([5..-5], bool (int x) { + return x < 0; + }) == []; +test bool takeWhile5() + = takeWhile([5..-5], bool (int x) { + return x > 0; + }) == [5..0]; +test bool takeWhile6() + = takeWhile([-20..20], bool (int x) { + return x < 0; + }) == [-20..0]; +test bool takeWhile7() = takeWhile([-20..20], bool (int x) { + return x > 0; + }) == []; + +// toMap +test bool toMap1() = toMap([]) == (); +test bool toMap2() = toMap([<1, 10>, <2, 20>]) == (1 : [10], + 2 : [20]); +test bool toMap3() = toMap([<1, 10>, <2, 20>, <1, 30>]) == (1 : [10, 30], + 2 : [20]); + +// toMapUnique +test bool toMapUnique1() = toMapUnique([]) == (); +test bool toMapUnique2() = toMapUnique([<1, 10>, <2, 20>]) == (1 : 10, + 2 : 20); +@expected{MultipleKey} +test bool toMapUnique3() { + toMapUnique([<1, 10>, <1, 20>]); + return false; +} + +// top - see head +// toRel +test bool toRel1() = toRel([]) == {}; +test bool toRel2() = toRel([1]) == {}; +test bool toRel3() = toRel([1, 2]) == {<1, 2 >}; +test bool toRel4() = toRel([1, 2, 3]) == {<1, 2 >, + <2, 3 >}; +test bool toRel5() = toRel([1, 2, 3, 4]) == {<1, 2 >, + <2, 3 >, + <3, 4 >}; + +// toSet +test bool toSet1() = toSet([]) == {}; +test bool toSet2() = toSet([1]) == {1}; +test bool toSet3() = toSet([1, 2]) == {1, 2}; +test bool toSet4() = toSet([1, 2, 1]) == {1, 2}; + +// toString +test bool toString1() = toString([]) == "[]"; +test bool toString2() = toString([1]) == "[1]"; +test bool toString3() = toString([1, 2]) == "[1,2]"; + +// itoString +test bool itoString1() = itoString([]) == "[]"; +test bool itoString2() = itoString([1]) == "[1]"; +test bool itoString3() = itoString([1, 2]) == "[1,2]"; +test bool itoString4() = itoString([1, [], 2]) == "[\n 1,\n [],\n 2\n]"; + +// listExpressions +test bool listExpressions() { + value n = 1; + value s = "string"; + return list[int] _ := [n] && list[str] _ := [s, s, *[s, s]]; +} + +// Tests related to the correctness of the dynamic types of lists produced by the library functions; +// incorrect dynamic types make pattern matching fail; +// testDynamicTypes +test bool dynamicTypes1() { + list[value] lst = ["1", 2, 3]; + return list[int] _ := slice(lst, 1, 2); +} +test bool dynamicTypes2() { + list[value] lst = ["1", 2, 3]; + return list[int] _ := lst - "1"; +} +test bool dynamicTypes3() { + list[value] lst = ["1", 2, 3]; + return list[int] _ := lst - ["1"]; +} +test bool dynamicTypes4() { + list[value] lst = ["1", 2, 3]; + return list[int] _ := delete(lst, 0); +} +test bool dynamicTypes5() { + list[value] lst = ["1", 2, 3]; + return list[int] _ := drop(1, lst); +} +test bool dynamicTypes6() { + list[value] lst = [1, 2, "3"]; + return list[int] _ := head(lst, 2); +} +test bool dynamicTypes7() { + list[value] lst = [1, 2, "3"]; + return list[int] _ := prefix(lst); +} +test bool dynamicTypes8() { + list[value] lst = ["1", 2, 3]; + return list[int] _ := tail(lst); +} +test bool dynamicTypes9() { + list[value] lst = [1, 2, "3"]; + return list[int] _ := take(2, lst); +} +test bool dynamicTypes10() { + return [str _, *int _] := ["1", 2, 3]; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/ListRelation.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Vadim Zaytsev - vadim@grammarware.net - UvA} +module lang::rascal::tests::library::ListRelation + +import ListRelation; +import List; + +// needed for slices used for dynamic type checks +// carrier +test bool carrier1() = carrier([<1, 10>, <2, 20>]) == [1, 10, 2, 20]; +test bool carrier2() + = carrier([<1, 10, 100>, <2, 20, 200>]) == [1, 10, 100, 2, 20, 200]; +test bool carrier3() + = carrier([<1, 10, 100, 1000>, <2, 20, 200, 2000>]) == [1, 10, 100, 1000, 2, 20, 200, 2000]; +test bool carrier4() + = carrier([<1, 10, 100, 1000, 10000>, <2, 20, 200, 2000, 20000>]) == [1, 10, 100, 1000, 10000, 2, 20, 200, 2000, 20000]; + +// TODO: duplicates? +// The current implementation is: carrier([<1,1>,<1,1>]) == [1,1,1,1] +// carrierR (set) +test bool carrierRs01() = carrierR([<1, 10 >, + <2, 20 >], {}) == []; +test bool carrierRs02() = carrierR([<1, 10 >, + <2, 20 >], {1, 2}) == []; +test bool carrierRs03() = carrierR([<1, 10 >, + <2, 20 >], {2, 20}) == [<2, 20 >]; +test bool carrierRs04() + = carrierR([<1, 10 >, + <2, 20 >], {1, 2, 10, 20}) == [<1, 10 >, + <2, 20 >]; + +test bool carrierRs05() = carrierR([<1, 10, 100 >, + <2, 20, 200 >], {}) == []; +test bool carrierRs06() = carrierR([<1, 10, 100 >, + <2, 20, 200 >], {1, 2}) == []; +test bool carrierRs07() + = carrierR([<1, 10, 100 >, + <2, 20, 200 >], {1, 2, 10, 20}) == []; +test bool carrierRs08() + = carrierR([<1, 10, 100 >, + <2, 20, 200 >], {2, 20, 200}) == [<2, 20, 200 >]; +test bool carrierRs09() + = carrierR([<1, 10, 100 >, + <2, 20, 200 >], {1, 2, 10, 20, 100, 200}) == [<1, 10, 100 >, + <2, 20, 200 >]; + +test bool carrierRs10() + = carrierR([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {}) == []; +test bool carrierRs11() + = carrierR([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {1, 2}) == []; +test bool carrierRs12() + = carrierR([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {1, 2, 10, 20}) == []; +test bool carrierRs13() + = carrierR([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {1, 2, 10, 20, 100, 200}) == []; +test bool carrierRs14() + = carrierR([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {2, 20, 200, 2000}) == [<2, 20, 200, 2000 >]; +test bool carrierRs15() + = carrierR( + [<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {1, 2, 10, 20, 100, 200, 1000, 2000} + ) == [<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >]; + +test bool carrierRs16() + = carrierR([<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {}) == []; +test bool carrierRs17() + = carrierR([<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {1, 2}) == []; +test bool carrierRs18() + = carrierR([<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {1, 2, 10, 20}) == []; +test bool carrierRs19() + = carrierR( + [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {1, 2, 10, 20, 100, 200} + ) == []; +test bool carrierRs20() + = carrierR( + [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], + {1, 2, 10, 20, 100, 200, 1000, 2000} + ) == []; +test bool carrierRs21() + = carrierR( + [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {2, 20, 200, 2000, 20000} + ) == [<2, 20, 200, 2000, 20000 >]; +test bool carrierRs22() + = carrierR( + [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], + {1, 2, 10, 20, 100, 200, 1000, 2000, 10000, 20000} + ) == [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >]; + +// carrierX (set) +test bool carrierXs01() = carrierX([<1, 10 >, + <2, 20 >], {}) == [<1, 10 >, + <2, 20 >]; +test bool carrierXs02() + = carrierX([<1, 10 >, + <2, 20 >], {3, 30}) == [<1, 10 >, + <2, 20 >]; +test bool carrierXs03() = carrierX([<1, 10 >, + <2, 20 >], {2}) == [<1, 10 >]; +test bool carrierXs04() = carrierX([<1, 10 >, + <2, 20 >], {10}) == [<2, 20 >]; +test bool carrierXs05() = carrierX([<1, 10 >, + <2, 20 >], {1, 2}) == []; + +test bool carrierXs06() + = carrierX([<1, 10, 100 >, + <2, 20, 200 >], {}) == [<1, 10, 100 >, + <2, 20, 200 >]; +test bool carrierXs07() + = carrierX([<1, 10, 100 >, + <2, 20, 200 >], {3, 30, 300}) == [<1, 10, 100 >, + <2, 20, 200 >]; +test bool carrierXs08() + = carrierX([<1, 10, 100 >, + <2, 20, 200 >], {2}) == [<1, 10, 100 >]; +test bool carrierXs09() + = carrierX([<1, 10, 100 >, + <2, 20, 200 >], {10}) == [<2, 20, 200 >]; +test bool carrierXs10() + = carrierX([<1, 10, 100 >, + <2, 20, 200 >], {200}) == [<1, 10, 100 >]; +test bool carrierXs11() = carrierX([<1, 10, 100 >, + <2, 20, 200 >], {1, 2}) == []; + +test bool carrierXs12() + = carrierX([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {}) == [<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >]; +test bool carrierXs13() + = carrierX([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {3, 30, 300, 3000}) == [<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >]; +test bool carrierXs14() + = carrierX([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {2}) == [<1, 10, 100, 1000 >]; +test bool carrierXs15() + = carrierX([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {10}) == [<2, 20, 200, 2000 >]; +test bool carrierXs16() + = carrierX([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {200}) == [<1, 10, 100, 1000 >]; +test bool carrierXs17() + = carrierX([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {1000}) == [<2, 20, 200, 2000 >]; +test bool carrierXs18() + = carrierX([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {1, 2}) == []; + +test bool carrierXs19() + = carrierX([<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {}) == [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >]; +test bool carrierXs20() + = carrierX( + [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {3, 30, 300, 3000, 30000} + ) == [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >]; +test bool carrierXs21() + = carrierX([<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {2}) == [<1, 10, 100, 1000, 10000 >]; +test bool carrierXs22() + = carrierX([<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {10}) == [<2, 20, 200, 2000, 20000 >]; +test bool carrierXs23() + = carrierX([<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {200}) == [<1, 10, 100, 1000, 10000 >]; +test bool carrierXs24() + = carrierX([<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {1000}) == [<2, 20, 200, 2000, 20000 >]; +test bool carrierXs25() + = carrierX([<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {20000}) == [<1, 10, 100, 1000, 10000 >]; +test bool carrierXs26() + = carrierX([<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {1, 2}) == []; + +// complement +test bool complement1() = complement([<1, 10>, <2, 20>]) == [<1, 20 >, + <2, 10 >]; +test bool complement2() = complement([<1, 1>, <2, 2>]) == [<1, 2 >, + <2, 1 >]; +test bool complement3() + = complement([<1, 10, 100>, <2, 20, 100>]) == [<1, 20, 100 >, + <2, 10, 100 >]; +test bool complement4() + = complement([<1, 10, 100>, <2, 20, 200>]) == [<1, 10, 200 >, + <1, 20, 100 >, + <1, 20, 200 >, + <2, 10, 100 >, + <2, 10, 200 >, + <2, 20, 100 >]; +test bool complement5() + = complement([<1, 10, 100, 1000>, <2, 20, 200, 2000>]) == [<1, 10, 100, 2000 >, + <1, 10, 200, 1000 >, + <1, 10, 200, 2000 >, + <1, 20, 100, 1000 >, + <1, 20, 100, 2000 >, + <1, 20, 200, 1000 >, + <1, 20, 200, 2000 >, + <2, 10, 100, 1000 >, + <2, 10, 100, 2000 >, + <2, 10, 200, 1000 >, + <2, 10, 200, 2000 >, + <2, 20, 100, 1000 >, + <2, 20, 100, 2000 >, + <2, 20, 200, 1000 >]; +test bool complement6() + = complement([<1, 2, 2, 2, 2>, <2, 2, 2, 2, 1>]) == [<1, 2, 2, 2, 1 >, + <2, 2, 2, 2, 2 >]; + +// domain +test bool domain1() = domain([<1, 10>, <2, 20>]) == [1, 2]; +test bool domain2() = domain([<1, 10>, <1, 20>]) == [1]; +test bool domain3() = domain([<1, 10, 100>, <2, 20, 200>]) == [1, 2]; +test bool domain4() = domain([<1, 10, 100>, <1, 20, 200>]) == [1]; +test bool domain5() = domain([<1, 10, 100, 1000>, <2, 20, 200, 2000>]) == [1, 2]; +test bool domain6() = domain([<1, 10, 100, 1000>, <1, 20, 200, 2000>]) == [1]; +test bool domain7() + = domain([<1, 10, 100, 1000, 10000>, <2, 20, 200, 2000, 20000>]) == [1, 2]; +test bool domain8() + = domain([<1, 10, 100, 1000, 10000>, <1, 20, 200, 2000, 20000>]) == [1]; + +// domainR (list) +test bool domainRl01() = domainR ( + [<1, 10 >, + <2, 20 >], + [] + ) == []; +test bool domainRl02() = domainR ( + [<1, 10 >, + <2, 20 >], + [2] + ) == [<2, 20 >]; +test bool domainRl03() = domainR ( + [<2, 10 >, + <2, 20 >], + [2] + ) == [<2, 10 >, + <2, 20 >]; +test bool domainRl04() = domainR ( + [<1, 10 >, + <2, 20 >], + [1, 2] + ) == [<1, 10 >, + <2, 20 >]; +test bool domainRl05() = domainR ( + [<1, 10 >, + <2, 20 >], + [2, 1] + ) == [<2, 20 >, + <1, 10 >]; +test bool domainRl06() = domainR ( + [<1, 10, 100 >, + <2, 20, 200 >], + [] + ) == []; +test bool domainRl07() + = domainR ( + [<1, 10, 100 >, + <2, 20, 200 >], + [2, 5] + ) == [<2, 20, 200 >]; +test bool domainRl08() + = domainR ( + [<1, 10, 100 >, + <1, 20, 200 >], + [0, 1] + ) == [<1, 10, 100 >, + <1, 20, 200 >]; +test bool domainRl09() + = domainR ( + [<1, 10, 100 >, + <2, 20, 200 >], + [2, 1, 10] + ) == [<2, 20, 200 >, + <1, 10, 100 >]; +test bool domainRl10() + = domainR ( + [<1, 10, 100 >, + <2, 20, 200 >], + [1, 2, 5] + ) == [<1, 10, 100 >, + <2, 20, 200 >]; +test bool domainRl11() + = domainR ( + [<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], + [] + ) == []; +test bool domainRl12() + = domainR ( + [<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], + [2, 5] + ) == [<2, 20, 200, 2000 >]; +test bool domainRl13() + = domainR ( + [<-1, 10, 100, 1000 >, + <-1, 20, 200, 2000 >], + [0, -1] + ) == [<-1, 10, 100, 1000 >, + <-1, 20, 200, 2000 >]; +test bool domainRl14() + = domainR ( + [<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], + [2, 3, 1] + ) == [<2, 20, 200, 2000 >, + <1, 10, 100, 1000 >]; +test bool domainRl15() + = domainR ( + [<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], + [1, 7, 2] + ) == [<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >]; +test bool domainRl16() + = domainR ( + [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], + [] + ) == []; +test bool domainRl17() + = domainR ( + [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], + [2, 5] + ) == [<2, 20, 200, 2000, 20000 >]; +test bool domainRl18() + = domainR ( + [<10, 10, 100, 1000, 10000 >, + <10, 20, 200, 2000, 20000 >], + [5, 10, 15] + ) == [<10, 10, 100, 1000, 10000 >, + <10, 20, 200, 2000, 20000 >]; +test bool domainRl19() + = domainR ( + [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], + [2, 5, 1] + ) == [<2, 20, 200, 2000, 20000 >, + <1, 10, 100, 1000, 10000 >]; +test bool domainRl20() + = domainR ( + [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], + [1, 2, 100, 200] + ) == [<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >]; + +// domainX +test bool domainX1() = domainX([<1, 10 >, + <2, 20 >], {}) == [<1, 10 >, + <2, 20 >]; +test bool domainX2() = domainX([<1, 10 >, + <2, 20 >], {2}) == [<1, 10 >]; +test bool domainX3() + = domainX([<1, 10, 100 >, + <2, 20, 200 >], {2, 5}) == [<1, 10, 100 >]; +test bool domainX4() + = domainX([<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >], {1, 3}) == [<2, 20, 200, 2000 >]; +test bool domainX5() + = domainX([<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >], {2, 5}) == [<1, 10, 100, 1000, 10000 >]; + +// groupDomainByRange +test bool groupDomainByRange1() = groupDomainByRange([<1, 1>]) == [[1]]; +test bool groupDomainByRange2() + = groupDomainByRange([<1, 2>, <1, 1>]) == [[1]]; +test bool groupDomainByRange3() + = groupDomainByRange([<1, 1>, <2, 2>]) == [[1], [2]]; +test bool groupDomainByRange4() + = groupDomainByRange([<1, 2>, <2, 2>]) == [[1, 2]]; +test bool groupDomainByRange5() + = groupDomainByRange([<2, 2>, <1, 2>]) == [[2, 1]]; +test bool groupDomainByRange6() + = groupDomainByRange([<1, 3>, <2, 1>, <2, 3>]) == [[1, 2], [2]]; + +// groupRangeByDomain +test bool groupRangeByDomain1() = groupRangeByDomain([<1, 1>]) == [[1]]; +test bool groupRangeByDomain2() + = groupRangeByDomain([<1, 2>, <1, 1>]) == [[2, 1]]; +test bool groupRangeByDomain3() + = groupRangeByDomain([<1, 1>, <2, 2>]) == [[1], [2]]; +test bool groupRangeByDomain4() + = groupRangeByDomain([<2, 1>, <2, 2>]) == [[1, 2]]; +test bool groupRangeByDomain5() + = groupRangeByDomain([<2, 2>, <2, 1>]) == [[2, 1]]; +test bool groupRangeByDomain6() + = groupRangeByDomain([<2, 1>, <1, 3>, <2, 3>]) == [[1, 3], [3]]; + +// ident +test bool ident1() = ident([]) == []; +test bool ident2() = ident([1]) == [<1, 1 >]; +test bool ident3() = ident([1, 2]) == [<1, 1 >, + <2, 2 >]; +test bool ident4() = ident([1, 2, 3]) == [<1, 1 >, + <2, 2 >, + <3, 3 >]; + +// invert +test bool invert1() = invert([<1, 10>, <2, 20>]) == [<10, 1 >, + <20, 2 >]; +test bool invert2() + = invert([<1, 10, 100>, <2, 20, 200>]) == [<100, 10, 1 >, + <200, 20, 2 >]; +test bool invert3() + = invert([<1, 10, 100, 1000>, <2, 20, 200, 2000>]) == [<1000, 100, 10, 1 >, + <2000, 200, 20, 2 >]; +test bool invert4() + = invert([<1, 10, 100, 1000, 10000>, <2, 20, 200, 2000, 20000>]) == [<10000, 1000, 100, 10, 1 >, + <20000, 2000, 200, 20, 2 >]; + +// range +test bool range1() = range([<1, 10>, <2, 20>]) == [10, 20]; +test bool range2() = range([<1, 10, 100>, <2, 20, 200>]) == [<10, 100 >, + <20, 200 >]; +test bool range3() + = range([<1, 10, 100, 1000>, <2, 20, 200, 2000>]) == [<10, 100, 1000 >, + <20, 200, 2000 >]; +test bool range4() + = range([<1, 10, 100, 1000, 10000>, <2, 20, 200, 2000, 20000>]) == [<10, 100, 1000, 10000 >, + <20, 200, 2000, 20000 >]; + +// rangeR +test bool rangeRs1() = rangeR([<1, 10 >, + <2, 20 >], {}) == []; +test bool rangeRs2() = rangeR([<1, 10 >, + <2, 20 >], {20}) == [<2, 20 >]; +test bool rangeRl1() = rangeR ( + [<1, 10 >, + <2, 20 >], + [] + ) == []; +test bool rangeRl2() = rangeR ( + [<1, 10 >, + <2, 20 >], + [20] + ) == [<2, 20 >]; +test bool rangeRl3() = rangeR ( + [<1, 10 >, + <2, 20 >], + [10, 20] + ) == [<1, 10 >, + <2, 20 >]; +test bool rangeRl4() = rangeR ( + [<1, 10 >, + <2, 20 >], + [20, 10] + ) == [<2, 20 >, + <1, 10 >]; + +// rangeX +test bool rangeXs1() = rangeX([<1, 10 >, + <2, 20 >], {}) == [<1, 10 >, + <2, 20 >]; +test bool rangeXs2() = rangeX([<1, 10 >, + <2, 20 >], {20}) == [<1, 10 >]; +test bool rangeXs3() = rangeX([<1, 10 >, + <2, 20 >], {10, 20}) == []; +test bool rangeXl4() = rangeX ( + [<1, 10 >, + <2, 20 >], + [] + ) == [<1, 10 >, + <2, 20 >]; +test bool rangeXl5() = rangeX ( + [<1, 10 >, + <2, 20 >], + [20] + ) == [<1, 10 >]; +test bool rangeXl6() = rangeX ( + [<1, 10 >, + <2, 20 >], + [10, 20] + ) == []; +test bool rangeXl7() = rangeX ( + [<1, 10 >, + <2, 20 >], + [20, 10] + ) == []; + +// toMap +test bool toMap1() = toMap([]) == (); +test bool toMap2() = toMap([<1, 1>]) == (1 : [1]); +test bool toMap3() = toMap([<1, 1>, <1, 2>]) == (1 : [1, 2]); +test bool toMap4() = toMap([<1, 1>, <2, 2>]) == (1 : [1], + 2 : [2]); +test bool toMap5() = toMap([<2, 1>, <2, 2>]) == (2 : [1, 2]); +test bool toMap6() = toMap([<2, 2>, <2, 1>]) == (2 : [2, 1]); + +// Tests related to the correctness of the dynamic types of list relations produced by the library functions; +// incorrect dynamic types make pattern matching fail; +test bool dynamicTypes1() { + lrel[value, value] lr = [<"1", "1" >, + <2, 2 >, + <3, 3 >]; + return lrel[int, int] _ := slice(lr, 1, 2); +} + +test bool dynamicTypes2() { + lrel[value, value] lr = [<"1", "1" >, + <2, 2 >, + <3, 3 >]; + return lrel[int, int] _ := lr - <"1", "1">; +} + +test bool dynamicTypes3() { + lrel[value a, value b] lr = [<"1", "1" >, + <2, 2 >, + <3, 3 >]; + return + lrel[int, int] _ := lr - [<"1", "1" >] + && (lr - [<"1", "1" >]).a == [2, 3] + && (lr - [<"1", "1" >]).b == [2, 3]; +} + +test bool dynamicTypes4() { + lrel[value, value] lr = [<"1", "1" >, + <2, 2 >, + <3, 3 >]; + return lrel[int, int] _ := delete(lr, 0); +} + +test bool dynamicTypes5() { + lrel[value, value] lr = [<"1", "1" >, + <2, 2 >, + <3, 3 >]; + return lrel[int, int] _ := drop(1, lr); +} + +test bool dynamicTypes6() { + lrel[value, value] lr = [<1, 1 >, + <2, 2 >, + <"3", "3" >]; + return lrel[int, int] _ := head(lr, 2); +} + +test bool dynamicTypes7() { + lrel[value, value] lr = [<1, 1 >, + <2, 2 >, + <"3", "3" >]; + return lrel[int, int] _ := prefix(lr); +} + +test bool dynamicTypes8() { + lrel[value, value] lr = [<"1", "1" >, + <2, 2 >, + <3, 3 >]; + return lrel[int, int] _ := tail(lr); +} + +test bool dynamicTypes9() { + lrel[value, value] lr = [<1, 1 >, + <2, 2 >, + <"3", "3" >]; + return lrel[int, int] _ := take(2, lr); +} + +test bool dynamicTypes10() { + return [tuple[str, str] _, *tuple[int, int] _] := [<"1", "1" >, + <2, 2 >, + <3, 3 >]; +} + +test bool dynamicTypes11() { + lrel[value a, value b] lr1 = [<"1", "1" >, + <2, 2 >, + <3, 3 >]; + lrel[value a, value b] lr2 = [<2, 2 >, + <3, 3 >]; + return + lrel[int, int] _ := lr1 & lr2 && (lr1 & lr2).a == [2, 3] && (lr2 & lr1).b == [2, 3]; +} + +test bool dynamicTypes12() { + lrel[value, value] lr = [<"1", "1" >, + <2, 2 >, + <3, 3 >]; + return lrel[int, int] _ := delete(lr, 0); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/Map.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Anastasia Izmaylova - A.Izmaylova@cwi.nl - CWI} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +@contributor{Vadim Zaytsev - vadim@grammarware.net - UvA} +module lang::rascal::tests::library::Map + +import Map; +import Set; +import Relation; +import List; +import IO; +import Exception; +import Type; + +// delete +test bool delete1(&K k) = isEmpty(delete((), k)); +test bool delete2() = delete((1 : 10, + 2 : 20), 0) == (1 : 10, + 2 : 20); +test bool delete3() = delete((1 : 10, + 2 : 20), 10) == (1 : 10, + 2 : 20); +test bool delete4() = delete((1 : 10, + 2 : 20), 1) == (2 : 20); +test bool delete5(map[&K, &V] M) { + X = ( M | delete(it, k) | &K k <- M ); + if (X != ()) { + println(X); + println(typeOf(X)); + println(M); + return false; + } + else { + return true; + } +} +test bool delete6(map[&K, &V] M) + = isEmpty(M) || size(M) == size(delete(M, getOneFrom(M))) + 1; +test bool delete7(map[str, &V] M) = size(M) == size(delete(M, 1)); +test bool delete8(map[int, &V] M) = size(M) == size(delete(M, "1")); + +// domain +test bool domain1() = domain(()) == {}; +test bool domain2() = domain((1 : 10)) == {1}; +test bool domain3() = domain((1 : 10, + 2 : 20)) == {1, 2}; +test bool domain4(map[&K, &V] M) = domain(M) == M<0>; +test bool domain5(map[&K, &V] M) = domain(M) == {k| k <- M}; +test bool domain6(map[&K k, &V v] M) = domain(M) == M.k; + +// domainR +test bool domainR1(map[&K, &V] M) = isEmpty(domainR(M, {})); +test bool domainR2(set[&K] D) = isEmpty(domainR((), D)); +test bool domainR3(map[&K, &V] M) { + if (isEmpty(M)) + return true; + S = {getOneFrom(M), getOneFrom(M)}; + return all(&K k <- domainR(M, S), k in S); +} + +// domainX +test bool domainX1(map[&K, &V] M) = domainX(M, {}) == M; +test bool domainX2(set[&K] D) = isEmpty(domainX((), D)); +test bool domainX3(map[&K, &V] M) { + if (isEmpty(M)) + return true; + S = {getOneFrom(M), getOneFrom(M)}; + XM = domainX(M, S); + return isEmpty(XM) || all(&K k <- XM, k notin S); +} + +// getOneFrom +@expected{EmptyMap} +test bool getOneFrom1() { + map[int, int] m = (); + v = getOneFrom(m); + return true; +} +test bool getOneFrom2() = getOneFrom((1 : 10)) == 1; +test bool getOneFrom3() = getOneFrom((1 : 10, + 2 : 20)) in {1, 2}; +test bool getOneFrom4(map[&K, &V] M) + = isEmpty(M) || getOneFrom(M) in domain(M); + +// invert +test bool invert1() = invert(()) == (); +test bool invert2() = invert((1 : 10)) == (10 : {1}); +test bool invert3() = invert((1 : 10, + 2 : 20)) == (10 : {1}, + 20 : {2}); +test bool invert4() = invert((1 : 10, + 2 : 10, + 3 : 30, + 4 : 30)) == (10 : {1, 2}, + 30 : {3, 4}); +test bool invert5(map[&K, &V] M) = range(M) == domain(invert(M)); +test bool invert6(map[&K, &V] M) = domain(M) == {*invert(M)[v]| v <- invert(M)}; + +// invertUnique +test bool invertUnique1() = invertUnique(()) == (); +test bool invertUnique2() = invertUnique((1 : 10)) == (10 : 1); +test bool invertUnique3() = invertUnique((1 : 10, + 2 : 20)) == (10 : 1, + 20 : 2); +test bool invertUnique4() + = invertUnique(([[]] : 0, + [[2]] : 2, + [[1, 2], [2, 1]] : 1, + [[1]] : 3)) == (0 : [[]], + 2 : [[2]], + 1 : [[1, 2], [2, 1]], + 3 : [[1]]); +@expected{MultipleKey} +test bool invertUnique5() { + invertUnique((1 : 10, + 2 : 10)); + return true; +} + +test bool invertUnique6(map[&K, &V] M) { + try { + map[&V, &K] RM = invertUnique(M); + return range(M) == domain(RM); + } + catch MultipleKey(_, _, _): + return true; +} + +test bool invertUnique7(map[&K, &V] M) { + try { + map[&V, &K] RM = invertUnique(M); + return range(RM) == domain(M); + } + catch MultipleKey(_, _, _): + return true; +} + +test bool invertUnique8(set[int] D, set[int] R) { + if (isEmpty(D) || isEmpty(R)) { + return true; + } + dList = toList(D); + rList = toList(R); + M + = (dList[i]: rList[i]| i <- [0..size(D) > size(R) ? Set::size(R) : Set::size(D)]); + + return + domain(M) == range(invertUnique(M)) && range(M) == domain(invertUnique(M)); +} + +// isEmpty +test bool isEmpty1() = isEmpty(()); +test bool isEmpty2() = !isEmpty((1 : 10)); + +// mapper +private int inc(int n) = n + 1; +private int dec(int n) = n - 1; +test bool mapper1() = mapper((), inc, inc) == (); +test bool mapper2() = mapper((1 : 10, + 2 : 20), inc, inc) == (2 : 11, + 3 : 21); +test bool mapper3() = mapper((), inc, dec) == (); +test bool mapper4() = mapper((1 : 10, + 2 : 20), inc, dec) == (2 : 9, + 3 : 19); + +// range +test bool range1() = range(()) == {}; +test bool range2() = range((1 : 10)) == {10}; +test bool range3() = range((1 : 10, + 2 : 20)) == {10, 20}; +test bool range4(map[&K, &V] M) = range(M) == M<1>; +test bool range5(map[&K, &V] M) = range(M) == {M[k]| k <- M}; +test bool range6(map[&K k, &V v] M) = range(M) == M.v; + +// rangeR +test bool rangeR1(map[&K, &V] M) = isEmpty(rangeR(M, {})); +test bool rangeR2(set[&K] D) = isEmpty(rangeR((), D)); +test bool rangeR3(map[&K, &V] M) { + if (isEmpty(M)) + return true; + S = {M[getOneFrom(M)], M[getOneFrom(M)]}; + return all(&K k <- rangeR(M, S), M[k] in S); +} + +// rangeX +test bool rangeX1(map[&K, &V] M) = rangeX(M, {}) == M; +test bool rangeX2(set[&K] D) = isEmpty(rangeX((), D)); +test bool rangeX3(map[&K, &V] M) { + if (isEmpty(M)) + return true; + S = {M[getOneFrom(M)], M[getOneFrom(M)]}; + XM = rangeX(M, S); + return isEmpty(XM) || all(&K k <- XM, M[k] notin S); +} + +// size +test bool size1() = size(()) == 0; +test bool size2() = size((1 : 10)) == 1; +test bool size3() = size((1 : 10, + 2 : 20)) == 2; +test bool size4(map[&K, &V] M) = size(M) == Set::size(domain(M)); +test bool size5(map[&K, &V] M) = size(M) >= Set::size(range(M)); + +// toList +test bool toList1() = toList(()) == []; +test bool toList2() = toList((1 : 10)) == [<1, 10 >]; +test bool toList3() + = toList((1 : 10, + 2 : 20)) in [[<1, 10 >, + <2, 20 >], [<2, 20 >, + <1, 10 >]]; +test bool toList4(map[&K, &V] M) = size(M) == List::size(toList(M)); +test bool toList5(map[&K, &V] M) + = isEmpty(M) || all(k <- M, in toList(M)); + +// toRel (on plain maps) +//@ignoreCompiler{FIX: Typechecker says: Multiple functions found which could be applied} +test bool toRel_g1() = toRel(()) == {}; +test bool toRel_g2() = toRel((1 : 10)) == {<1, 10 >}; +test bool toRel_g3() = toRel((1 : 10, + 2 : 20)) == {<1, 10 >, + <2, 20 >}; + +// NB: basically could be &V, but not list[&V] or set[&V] +test bool toRel_g4(map[&K, int] M) + = isEmpty(M) || all(k <- M, in toRel(M)); +test bool toRel_g5(map[&K, str] M) + = isEmpty(M) || all(k <- M, in toRel(M)); +test bool toRel_g6(map[&K, bool] M) + = isEmpty(M) || all(k <- M, in toRel(M)); + +// NB: basically could be &V, but not void; void screws up comprehensions +test bool toRel_v1(map[&K, int] M) = Relation::domain(toRel(M)) == domain(M); +test bool toRel_v2(map[&K, int] M) = Relation::range(toRel(M)) == range(M); +test bool toRel_v3(map[&K, str] M) = Relation::domain(toRel(M)) == domain(M); +test bool toRel_v4(map[&K, str] M) = Relation::range(toRel(M)) == range(M); +test bool toRel_v5(map[&K, bool] M) + = Relation::domain(toRel(M)) == domain(M); +test bool toRel_v6(map[&K, bool] M) = Relation::range(toRel(M)) == range(M); + +// toRel (on maps to sets) +test bool toRel_s1() = toRel((1 : {10})) == {<1, 10 >}; +test bool toRel_s2() = toRel((1 : {10}, + 2 : {20})) == {<1, 10 >, + <2, 20 >}; +test bool toRel_s3() = toRel((1 : {10, 20}, + 2 : {20})) == {<1, 10 >, + <1, 20 >, + <2, 20 >}; +test bool toRel_s4(map[&K, set[int]] M) + = any(v <- M<1>, Set::isEmpty(v)) || Relation::domain(toRel(M)) == domain(M); +test bool toRel_s5(map[&K, set[int]] M) + = any(v <- M<1>, Set::isEmpty(v)) + || Relation::range(toRel(M)) == {*R| R <- range(M)}; +test bool toRel_s6(map[&K, set[str]] M) + = any(v <- M<1>, Set::isEmpty(v)) || Relation::domain(toRel(M)) == domain(M); +test bool toRel_s7(map[&K, set[str]] M) + = any(v <- M<1>, Set::isEmpty(v)) + || Relation::range(toRel(M)) == {*R| R <- range(M)}; + +// toRel (on maps to lists) +test bool toRel_l1() = toRel((1 : [10])) == {<1, 10 >}; +test bool toRel_l2() = toRel((1 : [10], + 2 : [20])) == {<1, 10 >, + <2, 20 >}; +test bool toRel_l3() = toRel((1 : [10, 20], + 2 : [20])) == {<1, 10 >, + <1, 20 >, + <2, 20 >}; +test bool toRel_l4(map[&K, list[int]] M) + = any(v <- M<1>, List::isEmpty(v)) || Relation::domain(toRel(M)) == domain(M); +test bool toRel_l5(map[&K, list[int]] M) + = any(v <- M<1>, List::isEmpty(v)) + || Relation::range(toRel(M)) == {*R| R <- range(M)}; +test bool toRel_l6(map[&K, list[str]] M) + = any(v <- M<1>, List::isEmpty(v)) || Relation::domain(toRel(M)) == domain(M); +test bool toRel_l7(map[&K, list[str]] M) + = any(v <- M<1>, List::isEmpty(v)) + || Relation::range(toRel(M)) == {*R| R <- range(M)}; +test bool toRel_l8(map[&K, list[bool]] M) + = any(v <- M<1>, List::isEmpty(v)) || Relation::domain(toRel(M)) == domain(M); +test bool toRel_l9(map[&K, list[bool]] M) + = any(v <- M<1>, List::isEmpty(v)) + || Relation::range(toRel(M)) == {*R| R <- range(M)}; + +// toString +test bool toString1() = toString(()) == "()"; +test bool toString2() = toString((1 : 10)) == "(1:10)"; +test bool toString3() = toString((1 : [], + 2 : [])) == "(1:[],2:[])"; + +// itoString +test bool itoString1() = itoString(()) == "()"; +test bool itoString2() = itoString((1 : 10)) == "(1:10)"; +test bool itoString3() = itoString((1 : [], + 2 : [])) == "(\n 1:[],\n 2:[]\n)"; + +// toString and itoString should produce the same for flat types +test bool toStrings1(map[int, int] M) = toString(M) == itoString(M); +test bool toStrings2(map[bool, bool] M) = toString(M) == itoString(M); +test bool toStrings3(map[str, str] M) = toString(M) == itoString(M); + +// Tests related to the correctness of the dynamic types of maps produced by the library functions; +// incorrect dynamic types make pattern matching fail; +// testDynamicTypes +test bool testDynamicTypes1() { + map[value a, value b] m = ("1" : "1", + 2 : 2, + 3 : 3); + return + map[int, int] _ := m - ("1" : "1") + && (m - ("1" : "1")).a == {2, 3} + && (m - ("1" : "1")).b == {2, 3}; +} +test bool testDynamicTypes2() { + map[value a, value b] m1 = ("1" : "1", + 2 : 2, + 3 : 3); + map[value a, value b] m2 = (2 : 2, + 3 : 3); + return map[int, int] _ := m1 & m2 && (m1 & m2).a == {2, 3} && (m2 & m1).b == {2, 3}; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/Math.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::library::Math + +import util::Math; + +real eps = 0.000001 ; + +// abs +test bool abs1() = abs(0) == 0; +test bool abs2() = abs(0r) == 0r; +test bool abs3() = abs(-1) == 1; +test bool abs4() = abs(-1r1) == 1r1; +test bool abs5() = abs(1) == 1; +test bool abs6() = abs(1.5) == 1.5; +test bool abs7() = abs(3r2) == 3r2; +test bool abs8() = abs(-1.5) == 1.5; +test bool abs9() = abs(-3r2) == 3r2; + +// arbInt +test bool absInt1() { + int N = arbInt(10); + return (N >= 0) && (N < 10); +} +test bool absInt2() { + int _ = arbInt(); + return true; +} + +// arbReal +test bool arbReal1() { + real D = arbReal(); + return (D >= 0.0) && (D <= 1.0); +} + +// arbRat +test bool arbRat1() { + rat R = arbRat(10, 100); + return (R >= 0) && (R <= 10); +} + +// ceil +test bool ceil1() = ceil(-3) == -3; +test bool ceil2() = ceil(0) == 0; +test bool ceil3() = ceil(3) == 3; + +test bool ceil4() = ceil(-3.3) == -3; +test bool ceil5() = ceil(-3.0) == -3; +test bool ceil6() = ceil(-0.001) == 0; +test bool ceil7() = ceil(0.0) == 0; +test bool ceil8() = ceil(0.001) == 1; +test bool ceil9() = ceil(3.0) == 3.0; +test bool ceil10() = ceil(3.3) == 4; + +// cos +test bool cosTest1() = abs(cos(0) - 1) < eps; + +test bool cosTest2() = abs(cos(PI() / 2)) < eps; + +test bool cosTest3() = abs(cos(PI()) + 1) < eps; + +test bool cosTest4() = abs(cos(3 * PI() / 2)) < eps; + +test bool cosTest5() = abs(cos(2 * PI()) - 1) < eps; + +// denominator +test bool denominatorTest1() = denominator(2r3) == 3; + +test bool denominatorTest2() = denominator(4r6) == 3; + +test bool denominatorTest3() = denominator(-2r3) == 3; + +// E +test bool ETest() = E() > 2.7 && E() < 2.8; + +// exp +test bool ExpTest1() = abs(exp(0) - 1) < eps; + +test bool ExpTest2() = abs(exp(1) - E()) < eps; + +test bool ExpTest3() = abs(exp(2) - E() * E()) < eps; + +// floor +test bool floor1() = floor(-3) == -3; + +test bool floor2() = floor(0) == 0; + +test bool floor3() = floor(3) == 3; + +test bool floor4() = floor(0.0) == 0; + +test bool floor5() = floor(1.0) == 1; + +test bool floor6() = floor(1.1) == 1; + +test bool floor7() = floor(1.5) == 1; + +test bool floor8() = floor(1.9) == 1; + +test bool floor9() = floor(-1.0) == -1; + +test bool floor10() = floor(-1.1) == -2; + +test bool floor11() = floor(-1.5) == -2; + +test bool floor12() = floor(-1.9) == -2; + +// ln +test bool lnTest1() { + real D = ln(exp(2)); + return abs(D - 2) < 0.000001; +} + +test bool lnTest2() { + real D = ln(exp(3.5)); + return abs(D - 3.5) < 0.000001; +} + +// log +test bool logTest1() { + real D = log(9, 3); + return abs(D - 2) < 0.000001; +} + +test bool logTest2() { + real D = log(81, 9); + return abs(D - 2) < 0.000001; +} + +test bool logTest3() { + real D = log(343, 7); + return abs(D - 3) < 0.000001; +} + +// log10 +test bool log10Test1() { + real D = log10(10); + return abs(D - 1) < 0.000001; +} + +test bool log10Test2() { + real D = log10(100); + return abs(D - 2) < 0.000001; +} + +test bool log10Test3() { + real D = log10(pow(10, 5)); + return abs(D - 5) < 0.000001; +} + +// log2 +test bool log2Test1() { + real D = log2(4); + return abs(D - 2) < 0.000001; +} + +test bool log2Test2() { + real D = log2(16); + return abs(D - 4) < 0.000001; +} + +// max +test bool max1() = max(3, 10) == 10; + +test bool max2() = max(10, 10) == 10; + +test bool max3() = max(2r3, 2r4) == 2r3; + +test bool max4() = max(2r3, 2r3) == 2r3; + +test bool max5() = max(-2r3, 2r4) == 2r4; + +test bool max6() = max(3.0, 10.0) == 10.0; + +test bool max7() = max(10.0, 10.0) == 10.0; + +test bool max8() = max(3.5, 10) == 10; + +test bool max9() = max(3, 10.5) == 10.5; + +// min +test bool min1() = min(3, 10) == 3; + +test bool min2() = min(10, 10) == 10; + +test bool min3() = min(2r3, 2r4) == 2r4; + +test bool min4() = min(2r3, 2r3) == 2r3; + +test bool min5() = min(-2r3, 2r4) == -2r3; + +test bool min6() = min(3.0, 10.0) == 3.0; + +test bool min7() = min(3.0, 10.0) == 3.0; + +test bool min8() = min(10.0, 10.0) == 10.0; + +test bool min9() = min(3.5, 10) == 3.5; + +test bool min10() = min(3, 10.5) == 3; + +// numerator +test bool numerator1() = numerator(2r3) == 2; + +test bool numerator2() = numerator(4r6) == 2; + +test bool numerator3() = numerator(-2r3) == -2; + +test bool numerator4() = numerator(-4r6) == -2; + +// nroot +test bool nroot1() { + real D = nroot(10, 1); + return abs(D - 10) < 0.000001; +} + +test bool nroot2() { + real D = nroot(10, 2); + return abs(D * D - 10) < 0.000001; +} + +test bool nroot3() { + real D = nroot(10, 3); + return abs(D * D * D - 10) < 0.000001; +} + +// PI +test bool PI1() = PI() > 3.14; + +test bool PI2() = PI() < 3.15; + +// pow +test bool pow1() { + real D = pow(7, 0); + return abs(D - 1) < 0.000001; +} + +test bool pow2() { + real D = pow(7, 1); + return abs(D - 7) < 0.000001; +} + +test bool pow3() { + real D = pow(7, 2); + return abs(D - 7 * 7) < 0.000001; +} + +test bool pow4() { + real D = pow(7, 3); + return abs(D - 7 * 7 * 7) < 0.000001; +} + +// remainder +test bool remainder1() = remainder(2r3) == 2; + +test bool remainder2() = remainder(3r2) == 1; + +test bool remainder3() = remainder(4r2) == 0; + +test bool remainder4() = remainder(-2r3) == -2; + +// sin +test bool sin1() { + real D = sin(0); + return abs(D) < 0.000001; +} +test bool sin2() { + real D = sin(PI() / 2); + return abs(D - 1) < 0.000001; +} +test bool sin3() { + real D = sin(PI()); + return abs(D) < 0.000001; +} +test bool sin4() { + real D = sin(3 * PI() / 2); + return abs(D + 1) < 0.000001; +} +test bool sin5() { + real D = sin(2 * PI()); + return abs(D) < 0.000001; +} + +// sqrt +//TODO: handling 0 gracefully +//test bool sqrt1 () {real D = sqrt(0); return abs(D) < 0.000001;} +test bool sqrt1() { + real D = sqrt(1); + return abs(D - 1) < 0.000001; +} + +test bool sqrt2() { + real D = sqrt(2); + return abs(D * D - 2) < 0.000001; +} + +// tan +// TODO: arg < -pi/2 or > pi/2 +test bool tan1() { + real D = tan(0); + return abs(D) < 0.000001; +} + +test bool tan2() { + real D = tan(PI() / 4); + return abs(D - 1) < 0.000001; +} + +test bool tan3() { + real D = tan(-PI() / 4); + return abs(D + 1) < 0.000001; +} + +// toInt +test bool toInt1() = toInt(3) == 3; + +test bool toInt2() = toInt(3.14) == 3; + +test bool toInt3() = toInt(3r2) == 1; + +test bool toInt4() = toInt(4r2) == 2; + +// toReal +test bool toReal1() = toReal(3) == 3.0; + +test bool toReal2() = toReal(3.14) == 3.14; + +// toString +test bool testToString7() = toString(314) == "314"; + +test bool testToString8() = toString(3.14) == "3.14"; + +test bool testToString9() = toString(4r8) == "1r2"; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/Node.rsc| +module lang::rascal::tests::library::Node + +import Node; +import ValueIO; +import IO; +import util::UUID; + +// arity +data XNODE + = xf() + | xf(int) + | xf(int, int) + | xf(int, int, int) + ; + +test bool arity1() = arity(xf()) == 0; + +test bool arity2() = arity(xf(1)) == 1; + +test bool arity3() = arity(xf(1, 2)) == 2; + +// delAnnotation +data ANODE + = leaf(int n) + | a(ANODE left, ANODE right) + ; +anno + int + ANODE + @ + pos +; +anno + str + ANODE + @ + label +; + +public ANODE A1 = leaf(3) ; +public ANODE A2 = leaf(3)[@pos = 1][@label = "a"] ; +public ANODE A3 + = a(leaf(10)[@pos = 1][@label = "a"], leaf(20)[@pos = 2][@label = "b"])[@pos = 3][@label = "c"] +; + +test bool delAnnotation1() = !delAnnotation(A1, "pos")@pos?; +test bool delAnnotation2() = !delAnnotation(A2, "pos")@pos?; +test bool delAnnotation3() = delAnnotation(A2, "pos")@label == "a"; +test bool delAnnotation4() = !delAnnotation(A3, "pos")@pos?; +test bool delAnnotation5() = delAnnotation(A3, "pos")@label == "c"; + +// delAnnotations +test bool delAnnotations1() = !delAnnotations(A1)@pos?; +test bool delAnnotations2() = !delAnnotations(A1)@label?; + +test bool delAnnotations3() = !delAnnotations(A2)@pos?; +test bool delAnnotations4() = !delAnnotations(A2)@label?; + +test bool delAnnotations5() = !delAnnotations(A3)@pos?; +test bool delAnnotations6() = !delAnnotations(A3)@label?; + +test bool delAnnotations7() = ANODE n := delAnnotations(A3)[0] && n@pos == 1; +test bool delAnnotations8() + = ANODE n := delAnnotations(A3)[0] && n@label == "a"; + +test bool delAnnotations9() = ANODE n := delAnnotations(A3)[1] && n@pos == 2; +test bool delAnnotations10() + = ANODE n := delAnnotations(A3)[1] && n@label == "b"; + +// delAnnotationsRec +test bool delAnnotationsRec1() = !delAnnotationsRec(A1)@pos?; +test bool delAnnotationsRec2() = !delAnnotationsRec(A1)@label?; + +test bool delAnnotationsRec3() = !delAnnotationsRec(A2)@pos?; +test bool delAnnotationsRec4() = !delAnnotationsRec(A2)@label?; + +test bool delAnnotationsRec5() = !delAnnotationsRec(A3)@pos?; +test bool delAnnotationsRec6() = !delAnnotationsRec(A3)@label?; + +test bool delAnnotationsRec7() + = ANODE n := delAnnotationsRec(A3)[0] && !n@pos?; +test bool delAnnotationsRec8() + = ANODE n := delAnnotationsRec(A3)[0] && !n@label?; + +test bool delAnnotationsRec9() + = ANODE n := delAnnotationsRec(A3)[1] && !n@pos?; +test bool delAnnotationsRec10() + = ANODE n := delAnnotationsRec(A3)[1] && !n@label?; + +// getAnnotations +test bool getAnnotations1() = getAnnotations(A1) == (); + +test bool getAnnotations2() = getAnnotations(A2) == ("pos" : 1, + "label" : "a"); + +test bool getAnnotations3() = getAnnotations(A3) == ("pos" : 3, + "label" : "c"); + +// getChildren +data YNODE + = yf() + | yf(int) + | yf(int, int) + | yf(int, int, int) + ; + +test bool getChildren1() = getChildren(yf()) == []; + +test bool getChildren2() = getChildren(yf(1)) == [1]; + +test bool getChildren3() = getChildren(yf(1, 2)) == [1, 2]; + +// getName +data ZNODE + = zf() + | zf(int) + | zf(int, int) + | zf(int, int, int) + ; + +test bool getName1() = getName(zf()) == "zf"; +test bool getName2() = getName(zf(1, 2, 3)) == "zf"; + +// makeNode +test bool makeNode1() { + node n = makeNode("f"); + return getName(n) == "f" && arity(n) == 0 && getChildren(n) == []; +} + +test bool makeNode2() { + node n = makeNode("f", 1); + return getName(n) == "f" && arity(n) == 1 && getChildren(n) == [1]; +} + +test bool makeNode3() { + node n = makeNode("f", 1, 2); + return getName(n) == "f" && arity(n) == 2 && getChildren(n) == [1, 2]; +} + +test bool makeNode4() { + node n = makeNode("f", 1, 2, 3); + return getName(n) == "f" && arity(n) == 3 && getChildren(n) == [1, 2, 3]; +} + +// setAnnotations +test bool setAnnotations1() = setAnnotations(leaf(3), ()) == leaf(3); +test bool setAnnotations2() + = setAnnotations(leaf(3), ("pos" : 1, + "label" : "a"))@pos == 1; +test bool setAnnotations3() + = setAnnotations(leaf(3), ("pos" : 1, + "label" : "a"))@label == "a"; + +// unset +public node FG + = "f"(1, "g"(2, kwg1 = "a", kwg2 = 10, com = 15), kwf1 = true, kwf2 = "z", com = 15) +; + +test bool unsetKW1() = unset(FG) == "f"(1, "g"(2, kwg1 = "a", kwg2 = 10, com = 15)); + +test bool unsetSelKW1() + = unset(FG, "kwf1") == "f"(1, "g"(2, kwg1 = "a", kwg2 = 10, com = 15), kwf2 = "z", com = 15); + +test bool unsetSelKW2() = unset(FG, {}) == FG; +test bool unsetSelKW3() + = unset(FG, {"kwf1", "kwf2"}) == "f"(1, "g"(2, kwg1 = "a", kwg2 = 10, com = 15), com = 15); +test bool unsetSelKW4() + = unset(FG, {"kwf1"}) == "f"(1, "g"(2, kwg1 = "a", kwg2 = 10, com = 15), kwf2 = "z", com = 15); +test bool unsetSelKW5() + = unset(FG, {"kwf2"}) == "f"(1, "g"(2, kwg1 = "a", kwg2 = 10, com = 15), kwf1 = true, com = 15); +test bool unsetSelKW6() + = unset(FG, {"com"}) == "f"(1, "g"(2, kwg1 = "a", kwg2 = 10, com = 15), kwf1 = true, kwf2 = "z"); + +// unsetRec +test bool unsetRecKW1() = unsetRec(FG) == "f"(1, "g"(2)); + +test bool unsetRecSelKW1() + = unsetRec(FG, "kwf1") == "f"(1, "g"(2, kwg1 = "a", kwg2 = 10, com = 15), kwf2 = "z", com = 15); +test bool unsetRecSelKW2() = unsetRec(FG, {}) == FG; +test bool unseRecSelKW3() + = unsetRec(FG, {"kwf1", "kwf2"}) == "f"(1, "g"(2, kwg1 = "a", kwg2 = 10, com = 15), com = 15); +test bool unseRecSelKW4() + = unsetRec(FG, {"kwf1"}) == "f"(1, "g"(2, kwg1 = "a", kwg2 = 10, com = 15), kwf2 = "z", com = 15); +test bool unsetRecSelKW5() + = unsetRec(FG, {"kwf2"}) == "f"(1, "g"(2, kwg1 = "a", kwg2 = 10, com = 15), kwf1 = true, com = 15); +test bool unsetRecSelKW6() + = unsetRec(FG, {"com"}) == "f"(1, "g"(2, kwg1 = "a", kwg2 = 10), kwf1 = true, kwf2 = "z"); +test bool unsetRecSelKW7() + = unsetRec(FG, {"kwg1", "com"}) == "f"(1, "g"(2, kwg2 = 10), kwf1 = true, kwf2 = "z"); + +// kw args +data KNODE (int x = 2) + = z() + | y() + ; + +test bool setKW1() = setKeywordParameters(z(), ("x" : 4)) == z(x = 4); +test bool setKW2() = setKeywordParameters(z(x = 3), ("x" : 4)) == z(x = 4); +test bool setKW3() = setKeywordParameters(z(x = 3), ("x" : 4)).x == 4; + +/*TODO: clean up tmp */private bool textWriteRead(type[&T] typ, str termString, value termValue) { + tmp = |memory://test-tmp/xxx-node-< "" >.test|; + writeFile(tmp, termString); + try { + return readTextValueFile(typ, tmp) == termValue; + } + catch: + return false; +} + +test bool readTermFromFileInt1() + = textWriteRead(#node, "f(1)", makeNode("f", 1)); + +test bool readTermFromFileInt2() = textWriteRead(#node, "f(1)", "f"(1)); + +test bool readTermFromFileStr1() + = textWriteRead(#node, "f(\"abc\")", makeNode("f", "abc")); + +test bool readTermFromFileStr2() + = textWriteRead(#node, "f(\"abc\")", "f"("abc")); + +test bool readTermFromFileList1() + = textWriteRead(#node, "f([1,2,3])", makeNode ( + "f", + [[1, 2, 3]] + )); + +test bool readTermFromFileList2() + = textWriteRead(#node, "f([1,2,3])", "f"([1, 2, 3])); + +test bool readTermFromFileFun1() + = textWriteRead(#node, "f()", makeNode("f")); + +test bool readTermFromFileFun2() = textWriteRead(#node, "f()", "f"()); + +test bool readTermFromFileFunWithArgs1() + = textWriteRead(#node, "f(1,2,3)", makeNode("f", 1, 2, 3)); + +test bool readTermFromFileFunWithArgs2() + = textWriteRead(#node, "f(1,2,3)", "f"(1, 2, 3)); + +data FUN = f(int A, int B, int C); + +test bool readTermFromFileADT1() + = textWriteRead(#FUN, "f(1,2,3)", FUN::f(1, 2, 3)); + +test bool readTermFromFileADT2() + = textWriteRead(#FUN, "f(1,2,3)", f(1, 2, 3)); + +test bool toStringTest() { + node n = "f"(1, 2, 3); + return toString(n) == "\"f\"(1,2,3)"; +} + +test bool kwParamsInfluenceUnEqual() = ""() != ""(n = 1); + +test bool kwParamsInfluenceEqual() = !(""() == ""(n = 1)); + +test bool kwParamsInfluenceSetSemantics() + = ( 0 | it + 1 | _ <- {""(), ""(n = 1)} ) == 2; + +test bool kwParamsDoNotInfluenceMatch() { + node a = ""(); + node b = ""(n = 1); + return a := b && b := a; +} + +test bool kwParamsDoNotInfluenceNoMatch() { + node a = ""(); + node b = ""(n = 1); + return !(a !:= b) && !(b !:= a); +} + +test bool keywordParametersAreFields() { + node a = "x"(age = 1); + return a.age == 1 && a[age = 2].age == 2; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/Number.rsc| +module lang::rascal::tests::library::Number + +/******************************************************************************* + * Copyright (c) 2009-2015 CWI + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + + * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI + * * Paul Klint - Paul.Klint@cwi.nl - CWI + * * Bert Lisser - Bert.Lisser@cwi.nl - CWI + *******************************************************************************/// compare +test bool compare1() = 1r1 == 1; +test bool compare2() = 1r1 == 1.0; +test bool compare3() = -1r1 == -1; + +test bool compare4() = 1r2 < 1; +test bool compare5() = 1r2 <= 1; +test bool compare6() = 1r1 <= 1; +test bool compare7() = 3r2 > 1; +test bool compare8() = 3r2 >= 1; +test bool compare9() = 3r1 >= 1; + +test bool compare10() = 1r2 < 1.0; +test bool compare11() = 1r2 <= 1.0; +test bool compare12() = 1r1 <= 1.0; +test bool compare13() = 3r2 > 1.0; +test bool compare14() = 3r2 >= 1.0; +test bool compare15() = 3r1 >= 1.0; + +test bool compare16() = 1r2 < 2r2; +test bool compare17() = 1r2 <= 2r2; +test bool compare18() = 1r1 <= 2r2; +test bool compare19() = 3r2 > 2r2; +test bool compare20() = 3r2 >= 2r2; +test bool compare21() = 3r1 >= 2r2; + +// arithPromotion +test bool arithPromotion1() = 2r4 + 1r2 == 1r; +test bool arithPromotion2() = 2r4 - 1r2 == 0r; +test bool arithPromotion3() = 2r4 * 1r2 == 1r4; +test bool arithPromotion4() = 2r4 / 1r2 == 1r; + +test bool arithPromotion5() = 2r4 + 2 == 5r2; +test bool arithPromotion6() = 2r4 - 2 == -3r2; +test bool arithPromotion7() = 2r4 * 2 == 1r; +test bool arithPromotion8() = 2r4 / 2 == 1r4; + +test bool arithPromotion9() = 2r4 + 2.0 == 2.5; +test bool arithPromotion10() = 2r4 - 2.0 == -1.5; +test bool arithPromotion11() = 2r4 * 2.0 == 1.0; +test bool arithPromotion12() = 2r4 / 2.0 == 0.25; + +test bool arithPromotion13() = 2 + 1r2 == 5r2; +test bool arithPromotion14() = 2 - 1r2 == 3r2; +test bool arithPromotion15() = 2 * 1r2 == 1r; +test bool arithPromotion16() = 2 / 1r2 == 4r; + +test bool arithPromotion17() = 2.0 + 1r2 == 2.5; +test bool arithPromotion18() = 2.0 - 1r2 == 1.5; +test bool arithPromotion19() = 2.0 * 1r2 == 1.0; +test bool arithPromotion20() = 2.0 / 1r2 == 4.0; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/Relation.rsc| +module lang::rascal::tests::library::Relation + +/******************************************************************************* + * Copyright (c) 2009-2015 CWI + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + + * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI + * * Paul Klint - Paul.Klint@cwi.nl - CWI + * * Bert Lisser - Bert.Lisser@cwi.nl - CWI + */import Relation; + +// carrier +test bool carrier1() = carrier({<1, 10 >, + <2, 20 >}) == {1, 2, 10, 20}; +test bool carrier2() + = carrier({<1, 10, 100 >, + <2, 20, 200 >}) == {1, 2, 10, 20, 100, 200}; +test bool carrier3() + = carrier({<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >}) == {1, 2, 10, 20, 100, 200, 1000, 2000}; +test bool carrier4() + = carrier({<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >}) == {1, 2, 10, 20, 100, 200, 1000, 2000, 10000, 20000}; + +// carrierR +test bool carrierR1() = carrierR({<1, 10 >, + <2, 20 >}, {}) == {}; +test bool carrierR2() = carrierR({<1, 10 >, + <2, 20 >}, {2, 3}) == {}; +test bool carrierR3() = carrierR({<1, 10 >, + <2, 20 >}, {2, 20}) == {<2, 20 >}; +test bool carrierR4() + = carrierR({<1, 10, 100 >, + <2, 20, 200 >}, {2, 20, 200}) == {<2, 20, 200 >}; +test bool carrierR5() + = carrierR({<1, 10, 100 >, + <2, 20, 200 >}, {1, 2, 10, 20, 100, 200}) == {<1, 10, 100 >, + <2, 20, 200 >}; +test bool carrierR6() + = carrierR({<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >}, {1, 10, 100, 1000}) == {<1, 10, 100, 1000 >}; +test bool carrierR7() + = carrierR({<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >}, {2, 20, 200, 2000}) == {<2, 20, 200, 2000 >}; + +// carrierX +test bool carrierX1() = carrierX({<1, 10 >, + <2, 20 >}, {}) == {<1, 10 >, + <2, 20 >}; +test bool carrierX2() = carrierX({<1, 10 >, + <2, 20 >}, {2, 3}) == {<1, 10 >}; +test bool carrierX3() + = carrierX({<1, 10, 100 >, + <2, 20, 200 >}, {20}) == {<1, 10, 100 >}; +test bool carrierX4() = carrierX({<1, 10, 100 >, + <2, 20, 200 >}, {20, 100}) == {}; +test bool carrierX5() + = carrierX({<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >}, {1000}) == {<2, 20, 200, 2000 >}; +test bool carrierX6() + = carrierX({<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >}, {2}) == {<1, 10, 100, 1000 >}; + +// complement +test bool complement1() = complement({<1, 10 >, + <2, 20 >}) == {<2, 10 >, + <1, 20 >}; +test bool complement2() + = complement({<1, 10, 100 >, + <2, 20, 200 >}) == {<2, 20, 100 >, + <2, 10, 200 >, + <2, 10, 100 >, + <1, 20, 200 >, + <1, 20, 100 >, + <1, 10, 200 >}; +test bool complement3() + = complement({<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >}) == {<2, 20, 200, 1000 >, + <1, 10, 100, 2000 >, + <1, 10, 200, 1000 >, + <1, 10, 200, 2000 >, + <1, 20, 100, 1000 >, + <1, 20, 100, 2000 >, + <1, 20, 200, 1000 >, + <1, 20, 200, 2000 >, + <2, 10, 100, 1000 >, + <2, 10, 100, 2000 >, + <2, 10, 200, 1000 >, + <2, 10, 200, 2000 >, + <2, 20, 100, 1000 >, + <2, 20, 100, 2000 >}; + +// domain +test bool domain1() = domain({<1, 10 >, + <2, 20 >}) == {1, 2}; +test bool domain2() = domain({<1, 10, 100 >, + <2, 20, 200 >}) == {1, 2}; +test bool domain3() = domain({<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >}) == {1, 2}; +test bool domain4() + = domain({<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >}) == {1, 2}; + +// domainR +test bool domainR1() = domainR({<1, 10 >, + <2, 20 >}, {}) == {}; +test bool domainR2() = domainR({<1, 10 >, + <2, 20 >}, {2}) == {<2, 20 >}; +test bool domainR3() + = domainR({<1, 10, 100 >, + <2, 20, 200 >}, {2, 5}) == {<2, 20, 200 >}; +test bool domainR4() + = domainR({<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >}, {1, 3}) == {<1, 10, 100, 1000 >}; +test bool domainR5() + = domainR({<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >}, {2, 5}) == {<2, 20, 200, 2000, 20000 >}; + +// domainX +test bool domainX1() = domainX({<1, 10 >, + <2, 20 >}, {}) == {<1, 10 >, + <2, 20 >}; +test bool domainX2() = domainX({<1, 10 >, + <2, 20 >}, {2}) == {<1, 10 >}; +test bool domainX3() + = domainX({<1, 10, 100 >, + <2, 20, 200 >}, {2, 5}) == {<1, 10, 100 >}; +test bool domainX4() + = domainX({<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >}, {1, 3}) == {<2, 20, 200, 2000 >}; +test bool domainX5() + = domainX({<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >}, {2, 5}) == {<1, 10, 100, 1000, 10000 >}; + +// ident +test bool ident1() = ident({}) == {}; +test bool ident2() = ident({1}) == {<1, 1 >}; +test bool ident3() = ident({1, 2, 3}) == {<1, 1 >, + <2, 2 >, + <3, 3 >}; + +// invert +test bool invert1() = invert({<1, 10 >, + <2, 20 >}) == {<10, 1 >, + <20, 2 >}; +test bool invert2() + = invert({<1, 10, 100 >, + <2, 20, 200 >}) == {<100, 10, 1 >, + <200, 20, 2 >}; +test bool invert3() + = invert({<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >}) == {<1000, 100, 10, 1 >, + <2000, 200, 20, 2 >}; +test bool invert4() + = invert({<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >}) == {<10000, 1000, 100, 10, 1 >, + <20000, 2000, 200, 20, 2 >}; + +// range +test bool range1() = range({<1, 10 >, + <2, 20 >}) == {10, 20}; +test bool range2() = range({<1, 10, 100 >, + <2, 20, 200 >}) == {<10, 100 >, + <20, 200 >}; +test bool range3() + = range({<1, 10, 100, 1000 >, + <2, 20, 200, 2000 >}) == {<10, 100, 1000 >, + <20, 200, 2000 >}; +test bool range4() + = range({<1, 10, 100, 1000, 10000 >, + <2, 20, 200, 2000, 20000 >}) == {<10, 100, 1000, 10000 >, + <20, 200, 2000, 20000 >}; + +// rangeR +test bool rangeR1() = rangeR({<1, 10 >, + <2, 20 >}, {}) == {}; +test bool rangeR2() = rangeR({<1, 10 >, + <2, 20 >}, {20}) == {<2, 20 >}; + +// rangeX +test bool rangeX() = rangeX({<1, 10 >, + <2, 20 >}, {}) == {<1, 10 >, + <2, 20 >}; +test bool rangeX1() = rangeX({<1, 10 >, + <2, 20 >}, {20}) == {<1, 10 >}; + +// Tests related to the correctness of the dynamic types of relations produced by the library functions; +// incorrect dynamic types make pattern matching fail; +// testDynamicTypes +test bool testDynamicTypes1() { + rel[value, value] sr = {<"1", "1" >, + <2, 2 >, + <3, 3 >}; + return rel[int, int] _ := sr - <"1", "1">; +} +test bool testDynamicTypes2() { + rel[value a, value b] sr = {<"1", "1" >, + <2, 2 >, + <3, 3 >}; + return + rel[int, int] _ := sr - {<"1", "1" >} + && (sr - {<"1", "1" >}).a == {2, 3} + && (sr - {<"1", "1" >}).b == {2, 3}; +} +test bool testDynamicTypes3() { + return {<"1", "1" >, + *tuple[int, int] _ } := {<"1", "1" >, + <2, 2 >, + <3, 3 >}; +} + +test bool testDynamicTypes4() { + rel[value a, value b] sr1 = {<"1", "1" >, + <2, 2 >, + <3, 3 >}; + rel[value a, value b] sr2 = {<2, 2 >, + <3, 3 >}; + return rel[int, int] _ := sr1 & sr2 && (sr1 & sr2).a == {2, 3} && (sr2 & sr1).b == {2, 3}; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/Set.rsc| +module lang::rascal::tests::library::Set + +/******************************************************************************* + * Copyright (c) 2009-2015 CWI + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + + * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI + * * Paul Klint - Paul.Klint@cwi.nl - CWI + * * Bert Lisser - Bert.Lisser@cwi.nl - CWI + *******************************************************************************/import Exception; +import Set; + +// getFirstFrom +test bool getFirstFrom1() { + int N = Set::getFirstFrom({1}); + return N == 1; +} +test bool getFirstFrom2() { + int N = getFirstFrom({1}); + return N == 1; +} +test bool getFirstFrom3() { + int N = Set::getFirstFrom({1, 2}); + return (N == 1) || (N == 2); +} +test bool getFirstFrom4() { + int N = Set::getFirstFrom({1, 2, 3}); + return (N == 1) || (N == 2) || (N == 3); +} +test bool getFirstFrom5() { + real D = Set::getFirstFrom({1.0, 2.0}); + return (D == 1.0) || (D == 2.0); +} +test bool getFirstFrom6() { + str S = Set::getFirstFrom({"abc", "def"}); + return (S == "abc") || (S == "def"); +} +test bool getFirstFrom7(set[&T] S) + = isEmpty(S) || Set::getFirstFrom(S) == Set::getFirstFrom(S); +test bool getFirstFrom8(set[&T] S) = isEmpty(S) || Set::getFirstFrom(S) in S; + +@expected{EmptySet} +test bool getFirstFromError1() { + Set::getFirstFrom({}); + return false; +} + +@expected{EmptySet} +test bool getFirstFromError2() { + getFirstFrom({}); + return false; +} + +// getOneFrom +test bool getOneFrom1() { + int N = Set::getOneFrom({1}); + return N == 1; +} +test bool getOneFrom2() { + int N = Set::getOneFrom({1}); + return N == 1; +} +test bool getOneFrom3() { + int N = getOneFrom({1}); + return N == 1; +} +test bool getOneFrom4() { + int N = Set::getOneFrom({1, 2}); + return (N == 1) || (N == 2); +} +test bool getOneFrom5() { + int N = Set::getOneFrom({1, 2, 3}); + return (N == 1) || (N == 2) || (N == 3); +} +test bool getOneFrom6() { + real D = Set::getOneFrom({1.0, 2.0}); + return (D == 1.0) || (D == 2.0); +} +test bool getOneFrom7() { + str S = Set::getOneFrom({"abc", "def"}); + return (S == "abc") || (S == "def"); +} + +@expected{EmptySet} +test bool getOneFromError1() { + Set::getOneFrom({}); + return false; +} + +@expected{EmptySet} +test bool getOneFromError2() { + getOneFrom({}); + return false; +} + +// isEmpty +test bool isEmpty1() = isEmpty({}); +test bool isEmpty2() = isEmpty({1, 2}) == false; + +// mapper +test bool mapper1() { + int inc(int n) { + return n + 1; + } + return mapper({1, 2, 3}, inc) == {2, 3, 4}; +} + +// max +test bool max1() = Set::max({1, 2, 3, 2, 1}) == 3; +test bool max2() = max({1, 2, 3, 2, 1}) == 3; + +// min +test bool min1() = Set::min({1, 2, 3, 2, 1}) == 1; +test bool min2() = min({1, 2, 3, 2, 1}) == 1; + +// power +test bool power1() = Set::power({}) == {{}}; +test bool power2() = Set::power({1}) == {{}, {1}}; +test bool power3() = Set::power({1, 2}) == {{}, {1}, {2}, {1, 2}}; +test bool power4() + = Set::power({1, 2, 3}) == {{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}}; +test bool power5() + = Set::power({1, 2, 3, 4}) == {{}, + {1}, + {2}, + {3}, + {4}, + {1, 2}, + {1, 3}, + {1, 4}, + {2, 3}, + {2, 4}, + {3, 4}, + {1, 2, 3}, + {1, 2, 4}, + {1, 3, 4}, + {2, 3, 4}, + {1, 2, 3, 4}}; + +// reducer +test bool reducer1() { + int add(int x, int y) { + return x + y; + } + return reducer({1, 2, 3, 4}, add, 0) == 10; +} + +// size +test bool size1() = Set::size({}) == 0; +test bool size2() = size({}) == 0; +test bool size3() = Set::size({1}) == 1; +test bool size4() = Set::size({1, 2, 3}) == 3; + +// sum +test bool sum1() = sum({0}) == 0; +test bool sum2() = sum({1}) == 1; +test bool sum3() = sum({1, 2}) == 3; +test bool sum4() = sum({1, 2, 3}) == 6; + +// takeOneFrom +// TODO: rename E1 back to E +test bool takeOneFrom1() { + = Set::takeOneFrom({1}); + return (E1 == 1) && (SI == {}); +} +test bool takeOneFrom2() { + = Set::takeOneFrom({1, 2}); + return ((E1 == 1) && (SI == {2})) || ((E1 == 2) && (SI == {1})); +} + +@expected{EmptySet} +test bool takeOneFromError1() { + getOneFrom({}); + return false; +} + +// toList +test bool toList1() = Set::toList({}) == []; +test bool toList2() = toList({}) == []; +test bool toList3() = Set::toList({1}) == [1]; +test bool toList4() + = (Set::toList({1, 2, 1}) == [1, 2]) || (Set::toList({1, 2, 1}) == [2, 1]); + +// toMap +test bool toMap1() = Set::toMap({}) == (); +test bool toMap2() = toMap({}) == (); +test bool toMap3() = Set::toMap({<1, "a" >}) == (1 : {"a"}); +test bool toMap4() + = Set::toMap({<1, "a" >, + <2, "b" >, + <1, "c" >}) == (1 : {"a", "c"}, + 2 : {"b"}); + +// toMapUnique +test bool toMapUnique1() = Set::toMapUnique({}) == (); +test bool toMapUnique2() = toMapUnique({}) == (); +test bool toMapUnique3() = Set::toMapUnique({<1, "a" >}) == (1 : "a"); +test bool toMapUnique4() + = Set::toMapUnique({<1, "a" >, + <2, "b" >}) == (1 : "a", + 2 : "b"); + +@expected{MultipleKey} +test bool toMapUniqueError1() = toMapUnique({<1, 10 >, + <1, 20 >}) == (1 : 20); + +// testToString +test bool testToString1() = Set::toString({}) == "{}"; +test bool testToString2() = toString({}) == "{}"; +test bool testToString3() = Set::toString({1}) == "{1}"; +test bool testToString4() { + S = Set::toString({1, 2}); + return (S == "{1,2}") || (S == "{2,1}"); +} + +// setExpressions3 +test bool setExpressions1() { + value n = 1; + value s = "string"; + return set[int] _ := {n} && set[str] _ := {s, s, *{s, s}}; +} + +// Tests related to the correctness of the dynamic types of sets produced by the library functions; +// incorrect dynamic types make pattern matching fail; +// testDynamicTypes +test bool testDynamicTypes1() { + set[value] s = {"1", 2, 3}; + return set[int] _ := s - "1"; +} +test bool testDynamicTypes2() { + set[value] s = {"1", 2, 3}; + return set[int] _ := s - {"1"}; +} +test bool testDynamicTypes3() { + set[value] s = {"1", 2, 3}; + return set[int] _ := s & {2, 3}; +} +test bool testDynamicTypes4() = {"1", *int _} := {"1", 2, 3}; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/String.rsc| +module lang::rascal::tests::library::String + +/******************************************************************************* + * Copyright (c) 2009-2015 CWI + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + + * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI + * * Paul Klint - Paul.Klint@cwi.nl - CWI + * * Bert Lisser - Bert.Lisser@cwi.nl - CWI + *******************************************************************************/import String; + +// center +test bool center1() = center("a", 0) == "a"; +test bool center2() = center("a", 1) == "a"; +test bool center3() = center("a", 2) == "a "; +test bool center4() = center("a", 3) == " a "; + +test bool center5() = center("ab", 0, "-") == "ab"; +test bool center6() = center("ab", 1, "-") == "ab"; +test bool center7() = center("ab", 2, "-") == "ab"; +test bool center8() = center("ab", 3, "-") == "ab-"; +test bool center9() = center("ab", 4, "-") == "-ab-"; + +test bool center10() = center("ab", 3, "-+") == "ab-"; +test bool center11() = center("ab", 4, "-+") == "-ab-"; +test bool center12() = center("ab", 5, "-+") == "-ab-+"; +test bool center13() = center("ab", 6, "-+") == "-+ab-+"; + +// charAt +test bool charAt1() = String::charAt("abc", 0) == 97; +test bool charAt2() = String::charAt("abc", 1) == 98; +test bool charAt3() = String::charAt("abc", 2) == 99; +test bool charAt4() = charAt("abc", 0) == 97; + +@expected{IndexOutOfBounds} +test bool charAtError1() = String::charAt("abc", 3) == 99; + +// contains +test bool contains1() = contains("abc", "a"); +test bool contains2() = contains("abc", "c"); +test bool contains3() = !contains("abc", "x"); +test bool contains4() = !contains("abc", "xyzpqr"); +test bool contains5() = contains("abracadabra", "bra"); + +// deescape +test bool deescape1() = deescape("\\\"") == "\""; +test bool deescape2() = deescape("\\n") == "\n"; +test bool deescape3() = deescape("\\uAA11") == "\uAA11"; +test bool deescape4() = deescape("\\U012345") == "\U012345"; +test bool deescape5() = deescape("\\a0F") == "\a0f"; + +// endsWith +test bool endsWith1() = String::endsWith("abc", "abc"); +test bool endsWith2() = endsWith("abc", "abc"); +test bool endsWith3() = String::endsWith("abcdef", "def"); +test bool endsWith4() = !String::endsWith("abcdef", "abc"); + +// findAll +test bool findAll1() = findAll("abc", "a") == [0]; +test bool findAll2() = findAll("abc", "c") == [2]; +test bool findAll3() = findAll("abc", "x") == []; +test bool findAll4() = findAll("abc", "xyzpqr") == []; +test bool findAll5() = findAll("abracadabra", "bra") == [1, 8]; + +// findFirst +test bool findFirst1() = findFirst("abc", "a") == 0; +test bool findFirst2() = findFirst("abc", "c") == 2; +test bool findFirst3() = findFirst("abc", "x") == -1; +test bool findFirst4() = findFirst("abc", "xyzpqr") == -1; +test bool findFirst5() = findFirst("abracadabra", "bra") == 1; + +// findLast +test bool findLast1() = findLast("abc", "a") == 0; +test bool findLast2() = findLast("abc", "c") == 2; +test bool findLast3() = findLast("abc", "x") == -1; +test bool findLast4() = findLast("abc", "xyzpqr") == -1; +test bool findLast5() = findLast("abracadabra", "bra") == 8; + +// isEmpty +test bool isEmpty1() = isEmpty(""); +test bool isEmpty2() = isEmpty("abc") == false; + +// left +test bool left1() = left("a", 0) == "a"; +test bool left2() = left("a", 1) == "a"; +test bool left3() = left("a", 2) == "a "; + +test bool left4() = left("ab", 0, "-") == "ab"; +test bool left5() = left("ab", 1, "-") == "ab"; +test bool left6() = left("ab", 2, "-") == "ab"; +test bool left7() = left("ab", 3, "-") == "ab-"; +test bool left8() = left("ab", 4, "-") == "ab--"; + +test bool left9() = left("ab", 3, "-+") == "ab-"; +test bool left10() = left("ab", 4, "-+") == "ab-+"; +test bool left11() = left("ab", 5, "-+") == "ab-+-"; +test bool left12() = left("ab", 6, "-+") == "ab-+-+"; + +// replaceAll +test bool replaceAll0() = replaceAll("a", "", "A") == "a"; +test bool replaceAll1() = replaceAll("a", "a", "A") == "A"; +test bool replaceAll2() = replaceAll("a", "x", "X") == "a"; +test bool replaceAll3() = replaceAll("a", "aa", "A") == "a"; + +test bool replaceAll4() + = replaceAll("abracadabra", "a", "A") == "AbrAcAdAbrA"; +test bool replaceAll5() + = replaceAll("abracadabra", "a", "A") == "AbrAcAdAbrA"; +test bool replaceAll6() + = replaceAll("abracadabra", "a", "AA") == "AAbrAAcAAdAAbrAA"; +test bool replaceAll7() + = replaceAll("abracadabra", "ab", "AB") == "ABracadABra"; + +// replaceFirst +test bool replaceFirst0() = replaceFirst("a", "", "A") == "a"; +test bool replaceFirst1() = replaceFirst("a", "a", "A") == "A"; +test bool replaceFirst2() = replaceFirst("a", "x", "X") == "a"; +test bool replaceFirst3() = replaceFirst("a", "aa", "A") == "a"; +test bool replaceFirst4() + = replaceFirst("abracadabra", "a", "A") == "Abracadabra"; +test bool replaceFirst5() + = replaceFirst("abracadabra", "a", "AA") == "AAbracadabra"; +test bool replaceFirst6() + = replaceFirst("abracadabra", "ab", "AB") == "ABracadabra"; + +// replaceLast +test bool replaceLast0() = replaceLast("a", "", "A") == "a"; +test bool replaceLast1() = replaceLast("a", "a", "A") == "A"; +test bool replaceLast2() = replaceLast("a", "x", "X") == "a"; +test bool replaceLast3() = replaceLast("a", "aa", "A") == "a"; +test bool replaceLast4() + = replaceLast("abracadabra", "a", "A") == "abracadabrA"; +test bool replaceLast5() + = replaceLast("abracadabra", "a", "AA") == "abracadabrAA"; +test bool replaceLast6() + = replaceLast("abracadabra", "ab", "AB") == "abracadABra"; + +// reverse +test bool reverse1() = String::reverse("") == ""; +test bool reverse2() = reverse("") == ""; +test bool reverse3() = String::reverse("abc") == "cba"; + +// right +test bool right1() = right("a", 0) == "a"; +test bool right2() = right("a", 1) == "a"; +test bool right3() = right("a", 2) == " a"; + +test bool right4() = right("ab", 0, "-") == "ab"; +test bool right5() = right("ab", 1, "-") == "ab"; +test bool right6() = right("ab", 2, "-") == "ab"; +test bool right7() = right("ab", 3, "-") == "-ab"; +test bool right8() = right("ab", 4, "-") == "--ab"; + +test bool right9() = right("ab", 3, "-+") == "-ab"; +test bool right10() = right("ab", 4, "-+") == "-+ab"; +test bool right11() = right("ab", 5, "-+") == "-+-ab"; +test bool right12() = right("ab", 6, "-+") == "-+-+ab"; + +// size +test bool size1() = String::size("") == 0; +test bool size2() = size("") == 0; +test bool size3() = String::size("abc") == 3; + +// startsWith +test bool startsWith1() = String::startsWith("abc", "abc"); +test bool startsWith2() = startsWith("abc", "abc"); +test bool startsWith3() = String::startsWith("abcdef", "abc"); +test bool startsWith4() = !String::startsWith("abcdef", "def"); + +// substring +test bool substring1() = substring("abc", 0) == "abc"; +test bool substring2() = substring("abc", 1) == "bc"; +test bool substring3() = substring("abc", 2) == "c"; +test bool substring4() = substring("abc", 3) == ""; +test bool substring5() = substring("abc", 1, 2) == "b"; +test bool substring6() = substring("abc", 1, 3) == "bc"; + +@expected{IndexOutOfBounds} +test bool substringWrongIndex1() = substring("abc", 4) == "abc"; + +@expected{IndexOutOfBounds} +test bool substringWrongIndex2() = substring("abc", 1, 4) == "abc"; + +// toInt +test bool toInt1() = toInt("0") == 0; +test bool toInt2() = toInt("1") == 1; +test bool toInt3() = toInt("0001") == 1; +test bool toInt4() = toInt("-1") == -1; +test bool toInt5() = toInt("12345") == 12345; + +@expected{IllegalArgument} +test bool toIntError1() = toInt("abc") == 0; + +// toLowerCase +test bool toLowerCase1() = String::toLowerCase("") == ""; +test bool toLowerCase2() = toLowerCase("") == ""; +test bool toLowerCase3() = String::toLowerCase("ABC") == "abc"; +test bool toLowerCase4() = String::toLowerCase("ABC123") == "abc123"; + +// toReal +test bool toReal1() = toReal("0.0") == 0.0; +test bool toReal2() = toReal("1.0") == 1.0; +test bool toReal3() = toReal("0001.0") == 1.0; +test bool toReal4() = toReal("-1.0") == -1.0; +test bool toReal5() = toReal("1.2345") == 1.2345; + +@expected{IllegalArgument} +test bool toRealError1() = toReal("abc") == 0; + +// toUpperCase +test bool toUpperCase1() = String::toUpperCase("") == ""; +test bool toUpperCase2() = toUpperCase("") == ""; +test bool toUpperCase3() = String::toUpperCase("abc") == "ABC"; +test bool toUpperCase4() = String::toUpperCase("abc123") == "ABC123"; + +// Base64 +bool testBase64(str s) { + return + fromBase64(toBase64(s)) == s + && fromBase64(toBase64(s, includePadding = false)) == s; +} + +test bool toBase64Empty() = toBase64("") == ""; +test bool toBase64Single() = toBase64("a") == "YQ=="; +test bool toBase64SingleNoPadding() + = toBase64("a", includePadding = false) == "YQ"; + +test bool testBase64SomeChars() = testBase64("Hello World!"); +test bool testBase64AllChars1() = testBase64("`1234567890-=~!@#$%^&*"); +test bool testBase64AllChars2() + = testBase64("()_+qwertyuiop[]\\QWERTYUIOP"); +test bool testBase64AllChars3() + = testBase64("{}|asdfghjkl;\'ASDFGHJKL:\""); +test bool testBase64AllChars4() = testBase64("zxcvbnm,./ZXCVBNM\<\>? "); + +// Base32 +bool testBase32(str s) { + return + fromBase32(toBase32(s)) == s + && fromBase32(toBase32(s, includePadding = false)) == s; +} + +test bool toBase32Empty() = toBase32("") == ""; +test bool toBase32EmptyNoPadding() + = toBase32("", includePadding = false) == ""; +test bool toBase32Single() = toBase32("a") == "ME======"; +test bool toBase32SingleNoPadding() + = toBase32("a", includePadding = false) == "ME"; +test bool testBase32SomeChars() = testBase32("Hello World!"); +test bool testBase32AllChars1() = testBase32("`1234567890-=~!@#$%^&*"); +test bool testBase32AllChars2() + = testBase32("()_+qwertyuiop[]\\QWERTYUIOP"); +test bool testBase32AllChars3() + = testBase32("{}|asdfghjkl;\'ASDFGHJKL:\""); +test bool testBase32AllChars4() = testBase32("zxcvbnm,./ZXCVBNM\<\>? "); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/Type.rsc| +module lang::rascal::tests::library::Type + +import Type; + +data D + = a(bool b) + | a(str s1, str s2) + | a(int n, str color = "blue") + | a(str s, int sz = 10) + ; + +test bool tstMake1() = make ( + #D, + "a", + [true] + ) == a(true); + +test bool tstMake2() = make ( + #D, + "a", + ["x", "y"] + ) == a("x", "y"); + +test bool tstMake3() + = make(#D, "a", [3], ("color" : "red")) == a(3, color = "red"); + +test bool tstMake4() = make(#D, "a", ["x"], ("sz" : 20)) == a("x", sz = 20); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/ValueIO.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +@contributor{Bert Lisser - Bert.Lisser@cwi.nl - CWI} +module lang::rascal::tests::library::ValueIO + +import ValueIO; +import util::UUID; +import util::Reflective; + +data Bool (str def = "2") + = btrue() + | bfalse( bool falsity = true) + | band(Bool left, Bool right) + | bor(Bool left, Bool right) + ; + +data C = c(Maybe[int] i); +data Maybe[&T] + = none() + | some(&T t) + ; + +alias X[&T] = list[&T]; + +alias Y = int; + +loc value_io_test = |memory://test-tmp/value-io-< "" >.test| ; + +/*TODO: cleanup generated files as in Java version */private bool binaryWriteRead(type[&T] _, value exp) { + writeBinaryValueFile(value_io_test, exp); + if (&T N := readBinaryValueFile(value_io_test) && N == exp) + return true; + return false; +} + +test bool binBool() = binaryWriteRead(#bool, true); + +test bool binInt() = binaryWriteRead(#int, 1); + +test bool binReal() = binaryWriteRead(#real, 2.5); + +test bool binStr1() = binaryWriteRead(#str, "\"abc\""); + +test bool binStr2() = binaryWriteRead(#str, "\"ab\\nc\""); + +test bool binLoc() + = binaryWriteRead(#loc, |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 5>)); + +test bool binList() = binaryWriteRead ( + #list[int], + [1, 2, 3] + ); + +test bool binSet() = binaryWriteRead(#set[int], {1, 2, 3}); + +test bool binMap() = binaryWriteRead(#map[int, int], (1 : 10, + 2 : 20)); + +test bool binTuple() + = binaryWriteRead(#tuple[int, bool, str], <1, true, "abc">); + +test bool binAdt() + = binaryWriteRead( + #Bool, band(bor(btrue(), bfalse()), band(btrue(), btrue())) + ); + +test bool binParametrizedAdt1() = binaryWriteRead(#Maybe[value], none()); + +test bool binParametrizedAdt2() = binaryWriteRead(#Maybe[int], some(1)); + +test bool binParamAliasListInt() = binaryWriteRead ( + #X[int], + [1] + ); + +test bool binParamAliasInt() = binaryWriteRead(#Y, 1); + +loc value_io2_test = |memory://test-tmp/value-io2-< "" >.test| ; + +bool textWriteRead(type[&T] g, value exp) { + writeTextValueFile(value_io2_test, exp); + + if (&T N := readTextValueFile(g, value_io2_test) && N == exp) { + return true; + } + return false; +} + +test bool textParametrizedAdt1() = textWriteRead(#Maybe[int], some(1)); +test bool textParametrizedAdt2() = textWriteRead(#Maybe[value], none()); +test bool textParametrizedAdt3() = textWriteRead(#C, c(some(1))); + +test bool textBool() = textWriteRead(#bool, true); + +test bool textInt() = textWriteRead(#int, 1); + +test bool textReal() = textWriteRead(#real, 2.5); + +test bool textStr1() = textWriteRead(#str, "\"abc\""); + +test bool textStr2() = textWriteRead(#str, "\"ab\\nc\""); + +test bool textLoc() + = textWriteRead(#loc, |file:///home/paulk/pico.trm|(0, 1, <2, 3>, <4, 5>)); + +test bool textList() = textWriteRead ( + #list[int], + [1, 2, 3] + ); + +test bool textSet() = textWriteRead(#set[int], {1, 2, 3}); + +test bool textMap() = textWriteRead(#map[int, int], (1 : 10, + 2 : 20)); + +test bool textTuple() + = textWriteRead(#tuple[int, bool, str], <1, true, "abc">); + +test bool textAdt() + = textWriteRead( + #Bool, band(bor(btrue(), bfalse()), band(btrue(), btrue())) + ); + +test bool valueText(value v) = textWriteRead(#value, v); +test bool nodeText(node v) = textWriteRead(#node, v); +test bool strText(str v) = textWriteRead(#str, v); +test bool mapText(map[value, value] v) + = textWriteRead(#map[value, value], v); +test bool setText(set[value] v) = textWriteRead(#set[value], v); +test bool listText(list[value] v) = textWriteRead(#list[value], v); +test bool tupleText(tuple[value, value, value] v) + = textWriteRead(#tuple[value, value, value], v); +test bool numText(num v) = textWriteRead(#num, v); + +test bool valueBinary(value v) = binaryWriteRead(#value, v); +test bool nodeBinary(node v) = binaryWriteRead(#node, v); +test bool strBinary(str v) = binaryWriteRead(#str, v); +test bool mapBinary(map[value, value] v) + = binaryWriteRead(#map[value, value], v); +test bool setBinary(set[value] v) = binaryWriteRead(#set[value], v); +test bool listBinary(list[value] v) = binaryWriteRead(#list[value], v); +test bool tupleBinary(tuple[value, value, value] v) + = binaryWriteRead(#tuple[value, value, value], v); +test bool numBinary(num v) = binaryWriteRead(#num, v); + +loc compression_off = |memory://test-tmp/compression-off-< "" >.test| ; + +test bool disablingCompressionWorks(value v) { + writeBinaryValueFile(compression_off, v, compression = false); + return readBinaryValueFile(compression_off) == v; +} + +data NestedValue + = inAList(list[value] lvs) + | inASet(set[value] svs) + | inItself(NestedValue nv) + ; + +loc compression_shared + = |memory://test-tmp/compression-shared-< "" >.test| ; + +@maxDepth{20} +test bool disablingCompressionWorksWithSharedValues( + set[NestedValue] a, set[NestedValue] b, NestedValue c, value d +) { + lab = [a, b]; + joined = ; + writeBinaryValueFile(compression_shared, joined, compression = false); + return readBinaryValueFile(compression_shared) == joined; +} + +loc parsetree1 = |memory://test-tmp/parsetree1-< "" >.test| ; + +@Ignore{FOR NOW} +test bool writingParseTreeWorks() { + t + = parseModuleWithSpaces( + |project://rascal/src/org/rascalmpl/library/lang/rascal/syntax/Rascal.rsc| + ); + writeBinaryValueFile(parsetree1, t); + return readBinaryValueFile(parsetree1) == t; +} + +@Ignore{FOR NOW} +test bool writingParseTreeWorksWithoutCompression() { + t + = parseModuleWithSpaces( + |project://rascal/src/org/rascalmpl/library/lang/rascal/syntax/Rascal.rsc| + ); + writeBinaryValueFile(parsetree1, t, compression = false); + return readBinaryValueFile(parsetree1) == t; +} + +alias XX = loc; +data ExtraAliases + = al0(int x) + | al1(rel[XX, str, ExtraAliases] dt) + ; + +test bool aliasesNested(ExtraAliases e) + = binaryWriteRead(#ExtraAliases, e); + +// Reified types +private bool binaryWriteRead(type[&T] typ) { + writeBinaryValueFile(value_io_test, typ); + rtyp = readBinaryValueFile(value_io_test); + if (type[&T] N := rtyp && N == typ) + return true; + return false; +} + +test bool reifyBool() = binaryWriteRead(#bool); +test bool reifyStr() = binaryWriteRead(#str); +test bool reifyInt() = binaryWriteRead(#int); +test bool reifyReal() = binaryWriteRead(#real); +test bool reifyRat() = binaryWriteRead(#rat); +test bool reifyNum() = binaryWriteRead(#num); +test bool reifyNode() = binaryWriteRead(#node); +test bool reifyVoid() = binaryWriteRead(#void); +test bool reifyValue() = binaryWriteRead(#value); +test bool reifyList() = binaryWriteRead(#list[int]); +test bool reifySet() = binaryWriteRead(#set[int]); +test bool reifyLrel1() = binaryWriteRead(#lrel[int, str]); +test bool reifyLrel2() = binaryWriteRead(#lrel[int i, str s]); +test bool reifyRel1() = binaryWriteRead(#rel[int, str]); +test bool reifyRel2() = binaryWriteRead(#rel[int i, str s]); +test bool reifyMap1() = binaryWriteRead(#map[int, str]); +test bool reifyMap2() = binaryWriteRead(#map[int k, str v]); +test bool reifyFun1() = binaryWriteRead(#int(int)); +test bool reifyFun2() = binaryWriteRead(#int(int _n)); +test bool reifyPar1() = binaryWriteRead(#&T); +test bool reifyPar2() = binaryWriteRead(#&T <: num); + +alias A[&T] = list[&T]; +alias B[&T] = list[A[&T]]; + +test bool reifyAlias1() = binaryWriteRead(#A[int]); +test bool reifyAlias2() = binaryWriteRead(#B[int]); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/analysis/diff/edits/HiFiTreeDiffTests.rsc| +module lang::rascal::tests::library::analysis::diff::edits::HiFiTreeDiffTests + +extend analysis::diff::edits::ExecuteTextEdits; +extend analysis::diff::edits::HiFiLayoutDiff; +extend analysis::diff::edits::HiFiTreeDiff; +extend lang::pico::\syntax::Main; + +import IO; +import ParseTree; +import String; + +public str simpleExample + = "begin + ' declare + ' a : natural, + ' b : natural; + ' a := a + b; + ' b := a - b; + ' a := a - b + 'end + '" ; + +public str ifThenElseExample + = "begin + ' declare + ' a : natural; + ' if a then + ' a := 10 + ' else + ' if a then + ' a := 11 + ' else + ' a := 12 + ' fi + ' fi + 'end + '" ; + +@synopsis{Specification of what it means for `treeDiff` to be syntactically correct} +@description{ +TreeDiff is syntactically correct if: +* The tree after rewriting _matches_ the tree after applying the edits tot the source text and parsing that. +* Note that _matching_ ignores case-insensitive literals and layout, indentation and comments +} +bool editsAreSyntacticallyCorrect( + type[&T <: Tree] grammar, str example, Tree(Tree) transform, + list[TextEdit](Tree, Tree) diff +) { + orig = parse(grammar, example); + transformed = transform(orig); + edits = diff(orig, transformed); + edited = executeTextEdits(example, edits); + + try { + if (transformed := parse(grammar, edited)) { + return true; + } + else { + println("The edited result is not the same:"); + println(edited); + println("As the transformed:"); + println(transformed); + return false; + } + } + catch ParseError(loc l): { + println(" caused a parse error in:"); + println(edited); + return false; + } +} + +@synopsis{Extract the leading spaces of each line of code} +list[str] indentationLevels(str example) + = [i | /^[^\ ]*/ <- split("\n", example)]; + +@synopsis{In many cases, but not always, treeDiff maintains the indentation levels} +@description{ +Typically when a rewrite does not change the lines of code count, +and when the structure of the statements remains comparable, treeDiff +can guarantee that the indentation of a file remains unchanged, even if +significant changes to the code have been made. +} +@pitfalls{ +* This specification is not true for any transformation. Only apply it to +a test case if you can expect indentation-preservation for _the entire file_. +} +bool editsMaintainIndentationLevels( + type[&T <: Tree] grammar, str example, Tree(Tree) transform, + list[TextEdit](Tree, Tree) diff +) { + orig = parse(grammar, example); + transformed = transform(orig); + edits = diff(orig, transformed); + edited = executeTextEdits(example, edits); + + return indentationLevels(example) == indentationLevels(edited); +} + +Tree identity(Tree x) = x; + +start[Program] swapAB(start[Program] p) + = visit(p) { + case (Id) `a` => (Id) `b` + case (Id) `b` => (Id) `a` + }; + +start[Program] swapIfBranches(start[Program] p) + = visit(p) { + case (Statement) `if then <{Statement ";"}* thenBranch> else <{Statement ";"}* elseBranch> fi` + => (Statement) `if then  + ' <{Statement ";"}* elseBranch>  + 'else  + ' <{Statement ";"}* thenBranch> + 'fi` + }; + +start[Program] naturalToString(start[Program] p) + = visit(p) { + case (Type) `natural` => (Type) `string` + }; + +start[Program] addDeclarationToEnd(start[Program] p) + = visit(p) { + case (Program) `begin declare <{IdType ","}* decls>; <{Statement ";"}* body> end` + => (Program) `begin + ' declare + ' <{IdType ","}* decls>, + ' c : natural; + ' <{Statement ";"}* body> + 'end` + }; + +start[Program] addDeclarationToStart(start[Program] p) + = visit(p) { + case (Program) `begin declare <{IdType ","}* decls>; <{Statement ";"}* body> end` + => (Program) `begin + ' declare + ' c : natural, + ' <{IdType ","}* decls>; + ' <{Statement ";"}* body> + 'end` + }; + +start[Program] addDeclarationToMiddle(start[Program] p) + = visit(p) { + case (Program) `begin declare <{IdType ","}* pre>, , <{IdType ","}* post>; <{Statement ";"}* body> end` + => (Program) `begin + ' declare + ' <{IdType ","}* pre>, + ' , + ' middle : natural, + ' <{IdType ","}* post>; + ' <{Statement ";"}* body> + 'end` + }; + +start[Program](start[Program]) indent( + str indentation = " ", + bool indentFirstLine = true +) { + return + start[Program] + (start[Program] p){ + return + parse( + #start[Program], + indent(indentation, "

", indentFirstLine = indentFirstLine) + ); + }; +} + +start[Program] insertSpacesInDeclaration(start[Program] p) + = visit(p) { + case (IdType) ` : ` => (IdType) ` : ` + }; + +test bool nulTestWithId() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, identity, treeDiff + ); + +test bool simpleSwapper() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, swapAB, treeDiff + ) + && editsMaintainIndentationLevels( + #start[Program], simpleExample, swapAB, treeDiff + ); + +test bool addDeclarationToEndTest() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, addDeclarationToEnd, treeDiff + ); + +test bool addDeclarationToStartTest() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, addDeclarationToStart, treeDiff + ); + +test bool addDeclarationToMiddleTest() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, addDeclarationToMiddle, treeDiff + ); + +test bool addDeclarationToStartAndMiddleTest() + = editsAreSyntacticallyCorrect( + #start[Program], + simpleExample, + addDeclarationToStart o addDeclarationToMiddle, + treeDiff + ); + +test bool addDeclarationToMiddleAndEndTest() + = editsAreSyntacticallyCorrect( + #start[Program], + simpleExample, + addDeclarationToMiddle o addDeclarationToEnd, + treeDiff + ); + +test bool addDeclarationToStartAndEndTest() + = editsAreSyntacticallyCorrect( + #start[Program], + simpleExample, + addDeclarationToStart o addDeclarationToEnd, + treeDiff + ); + +test bool addDeclarationToStartMiddleAndEndTest() + = editsAreSyntacticallyCorrect( + #start[Program], + simpleExample, + addDeclarationToStart o addDeclarationToMiddle o addDeclarationToEnd, + treeDiff + ); + +test bool addDeclarationToEndAndSwapABTest() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, addDeclarationToEnd o swapAB, treeDiff + ); + +test bool addDeclarationToStartAndSwapABTest() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, addDeclarationToStart o swapAB, treeDiff + ); + +test bool addDeclarationToStartAndEndAndSwapABTest() + = editsAreSyntacticallyCorrect( + #start[Program], + simpleExample, + addDeclarationToStart o addDeclarationToEnd o swapAB, + treeDiff + ); + +test bool naturalToStringTest() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, naturalToString, treeDiff + ); + +test bool naturalToStringAndAtoBTest() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, naturalToString o swapAB, treeDiff + ); + +test bool swapBranchesTest() + = editsAreSyntacticallyCorrect( + #start[Program], ifThenElseExample, swapIfBranches, treeDiff + ); + +test bool nulTestWithIdLayout() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, identity, layoutDiff + ) + && editsMaintainIndentationLevels( + #start[Program], simpleExample, identity, layoutDiff + ); + +test bool indentAllLayout() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, indent(), layoutDiff + ) + && !editsMaintainIndentationLevels( + #start[Program], simpleExample, indent(), layoutDiff + ); + +test bool insertSpacesInDeclarationLayout() + = editsAreSyntacticallyCorrect( + #start[Program], simpleExample, insertSpacesInDeclaration, layoutDiff + ) + && editsMaintainIndentationLevels( + #start[Program], simpleExample, insertSpacesInDeclaration, layoutDiff + ); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/analysis/formalconcepts/FCATest.rsc| +@ignoreCompiler{Fix failing tests} +module lang::rascal::tests::library::analysis::formalconcepts::FCATest + +import util::Math; +import Set; +import ValueIO; +import analysis::formalconcepts::CXTIO; +import analysis::formalconcepts::FCA; + +data IntProperties + = composite() + | even() + | odd() + | prime() + | square() + ; + +test bool numbersAreCorrect() { + range = {*[1..11]}; + input + = {| i <- range} + + {| i <- range} + + {| i <- range, any(j <- range, j * j == i)}; + return checkNoMadeUpAttributes(input) && checkNoUnRelatedConcepts(input); +} + +rel[&TObject, &TAttribute] createRel( + set[&TObject] objects, set[&TAttribute] attributes, int combine +) { + result = objects * attributes; + if (size(objects) < 2 || size(attributes) < 2) { + return result; + } + combine + = toInt( + size(result) * (abs(combine) % size(attributes)) / (size(attributes) * 1.) + ); + // 0 to 1 chance of how connected it should be + int taken = 0; + actualResult = {}; + for (r <- result, taken < combine) { + actualResult += r; + taken += 1; + } + if (actualResult != {}) { + return actualResult; + } + return result; +} + +test bool testNoMadeUpAttributes( + set[&TObject] objects, set[&TAttribute] attributes, int combine +) + = checkNoMadeUpAttributes(createRel(objects, attributes, combine)); +bool checkNoMadeUpAttributes(rel[&TObject, &TAttribute] input) { + result = fca(input); + for (/<set[&TObject] objects, set[&TAttribute] attributes> := result) { + if (attributes > input[objects] && objects != {}) { + throw "for we would maximally expect: but got: "; + } + } + return true; +} + +@ignore{TODO: Fails for unknown reason} +test bool testNoUnRelatedConcepts( + set[&TObject] objects, set[&TAttribute] attributes, int combine +) + = checkNoUnRelatedConcepts(createRel(objects, attributes, combine)); + +bool checkNoUnRelatedConcepts(rel[&TObject, &TAttribute] input) { + ConceptLattice[&Object, &Attribute] result = fca(input); + for (/Concept[&TObject, &TAttribute] _: <{e, *rest}, _> := result, size(rest) > 0, size(rest) != (size(input<0>) - 1)) { + if (( input[e] | it & input[r] | r <- rest ) == {}) { + throw " have nothing in common, but they are a concept? "; + } + } + return true; +} + +test bool fcaHasExpectedOutput() { + result + = fca( + readCxt( + |std:///lang/rascal/tests/library/analysis/formalconcepts/FCxt1.cxt| + ) + ); + reference + = readBinaryValueFile( + #ConceptLattice[str, str], + |std:///lang/rascal/tests/library/analysis/formalconcepts/FCxt1.fca| + ); + return result == reference; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/analysis/graphs/GraphTests.rsc| +module lang::rascal::tests::library::analysis::graphs::GraphTests + +/******************************************************************************* + * Copyright (c) 2009-2015 CWI + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + + * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI + * * Paul Klint - Paul.Klint@cwi.nl - CWI + * * Bert Lisser - Bert.Lisser@cwi.nl - CWI + *******************************************************************************/import analysis::graphs::Graph; +import List; +import Set; +import Relation; + +/* + 1 -----> 3 + | | + v | + 2 | + | | + v | + 4 <------+ + +*/public Graph[int] G1 = {<3, 4 >, + <1, 2 >, + <2, 4 >, + <1, 3 >} ; + +/* + 1 + | + v + 2 <----> 3 + ^ ^ + | | + v v + 4 <----> 5 + +*/public Graph[int] G2 = {<1, 2 >, + <2, 3 >, + <3, 2 >, + <2, 4 >, + <4, 2 >, + <3, 5 >, + <5, 3 >, + <4, 5 >, + <5, 3 >} ; + +/* + 1------->6------>7<---->8 + | + v + 2 <----> 3 + ^ ^ + | | + v v + 4 <----> 5 + +*/public Graph[int] G3 + = {<1, 2 >, + <2, 3 >, + <3, 2 >, + <2, 4 >, + <4, 2 >, + <3, 5 >, + <5, 3 >, + <4, 5 >, + <5, 3 >, + <1, 6 >, + <6, 7 >, + <7, 8 >, + <8, 7 >} ; + +/* + 2 -----> 3 ------+ + ^ | | + | v v + 1 -----> 4 ----> 5 + | + V + 6 +*/public Graph[int] G4 = {<1, 2 >, + <1, 4 >, + <2, 3 >, + <3, 4 >, + <3, 5 >, + <4, 5 >, + <4, 6 >} ; + +/* + + 5 +---------- 7 +------- 3 + | / | | | + v v v | | + 11 ------+ 8 <-----+ | + | \ | | | + v \ | v v + 2 | +----> 9 10 + | ^ + +-----------------------------+ + */public Graph[int] G5 + = {<5, 11 >, + <11, 2 >, + <11, 9 >, + <11, 10 >, + <7, 11 >, + <7, 8 >, + <8, 9 >, + <3, 8 >, + <3, 10 >} ; + +/* + +-------+ +-----+ + | | | | + v | v | + 0 ----> 1 ----> 2 ----> 3 ----+ +*/public Graph[int] G6 = {<0, 1 >, + <1, 2 >, + <2, 1 >, + <2, 3 >, + <3, 3 >} ; + +/* + +---------------+ + | | + v | + 1 ----> 0 ----> 2 + | + v + 3 ----> 4 + +*/public Graph[int] G7 = {<1, 0 >, + <0, 2 >, + <2, 1 >, + <0, 3 >, + <3, 4 >} ; + +/* + 0 ----> 1 ----> 2 ----> 3 + +*/public Graph[int] G8 = {<0, 1 >, + <1, 2 >, + <2, 3 >} ; + +/* + +---------------+ + | | + v | + 0 ----> 1 ----> 2 + | + | + 3 <-----+ ----> 6 + | | + v v + 5 <---- 4 + +*/public Graph[int] G9 = {<0, 1 >, + <1, 2 >, + <2, 0 >, + <1, 3 >, + <1, 4 >, + <1, 6 >, + <3, 5 >, + <4, 5 >} ; + +/* + +---------------+ 9 <------+-----+ + | | ^ | | + | v | | | + | +------ 2 ----> 6 <-----+------> 7 | + | | ^ | | | + | v | v | | + 3 <---- 0 ----> 1 ----> 4 ----> 5 -----> 8 <---+ + +*/public Graph[int] G10 + = {<0, 1 >, + <0, 3 >, + <1, 2 >, + <1, 4 >, + <2, 0 >, + <2, 6 >, + <3, 2 >, + <4, 5 >, + <4, 6 >, + <5, 6 >, + <5, 7 >, + <5, 8 >, + <5, 9 >, + <6, 4 >, + <7, 9 >, + <8, 9 >, + <9, 8 >} ; + +/* + +-----------------------+ + | | + v | + 0 ----> 1 ----> 2 ----> 3 + ^ + | + v + 4 +*/public Graph[int] G11 = {<0, 1 >, + <1, 2 >, + <2, 3 >, + <2, 4 >, + <3, 0 >, + <4, 2 >} ; + +// bottom +test bool bottom1() = bottom({}) == {}; +test bool bottom2() = bottom({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}) == {4}; +test bool bottom3() = bottom(G1) == {4}; +test bool bottom4() = bottom(G2) == {}; +test bool bottom5() = bottom(G3) == {}; +test bool bottom6() = bottom(G4) == {5, 6}; +test bool bottom7() = bottom(G5) == {2, 9, 10}; +test bool bottom8() = bottom(G6) == {}; +test bool bottom9() = bottom(G7) == {4}; +test bool bottom10() = bottom(G8) == {3}; +test bool bottom11() = bottom(G9) == {5, 6}; +test bool bottom12() = bottom(G10) == {}; + +// TODO: connectedComponents +// stronglyConnectedComponents +test bool scc0() = stronglyConnectedComponents({}) == {}; +test bool scc1() = stronglyConnectedComponents(G1) == {{1}, {2}, {3}, {4}}; +test bool scc2() = stronglyConnectedComponents(G2) == {{1}, {2, 3, 4, 5}}; +test bool scc3() + = stronglyConnectedComponents(G3) == {{1}, {2, 3, 4, 5}, {6}, {7, 8}}; +test bool scc4() + = stronglyConnectedComponents(G4) == {{1}, {2}, {3}, {4}, {5}, {6}}; +test bool scc5() + = stronglyConnectedComponents(G5) == {{2}, {3}, {5}, {7}, {8}, {9}, {10}, {11}}; +test bool scc6() = stronglyConnectedComponents(G6) == {{0}, {1, 2}, {3}}; +test bool scc7() = stronglyConnectedComponents(G7) == {{4}, {3}, {1, 2, 0}}; +test bool scc8() = stronglyConnectedComponents(G8) == {{0}, {1}, {2}, {3}}; +test bool scc9() + = stronglyConnectedComponents(G9) == {{3}, {4}, {5}, {6}, {0, 1, 2}}; +test bool scc10() + = stronglyConnectedComponents(G10) == {{0, 1, 2, 3}, {4, 5, 6}, {7}, {8, 9}}; +test bool scc11() = stronglyConnectedComponents(G11) == {{0, 1, 2, 3, 4}}; + +test bool sccPreservesElements(Graph[int] G) { + components = stronglyConnectedComponents(G); + return {*comp| comp <- components} == carrier(G); +} + +test bool sccDisjointComponents(Graph[int] G) { + components = stronglyConnectedComponents(G); + return + isEmpty(G) + || size(components) <= 1 + || all(set[int] comp1 <- components, set[int] comp2 <- components, ((comp1 == comp2) ? true : isEmpty(comp1 & comp2))); +} + +// stronglyConnectedComponentsAndTopSort +test bool sccNoDuplicatesInOrder(Graph[int] G) { + = stronglyConnectedComponentsAndTopSort(G); + return size(ordered) == size(toSet(ordered)); +} + +test bool sccOrderEqualsComponents(Graph[int] G) { + = stronglyConnectedComponentsAndTopSort(G); + return {*comp| comp <- components} == toSet(ordered); +} + +// order +test bool order1() = order(G1) == [1, 2, 3, 4]; +test bool order2() = order(G2)[0] == 1; +test bool order3() = order(G3)[0] == 1 && order(G3)[1] == 6; +test bool order4() = order(G4) == [1, 2, 3, 4, 6, 5]; +test bool order5() = order(G5) == [5, 3, 7, 11, 2, 8, 9, 10]; +test bool order6() = order(G6) == [0, 1, 2, 3]; +test bool order7() = order(G7) == [1, 0, 2, 3, 4]; +test bool order8() = order(G8) == [0, 1, 2, 3]; +test bool order9() = order(G9) == [1, 2, 0, 6, 4, 3, 5]; +test bool order10() = order(G10) == [1, 2, 0, 3, 6, 4, 5, 7, 9, 8]; +test bool order11() = order(G11) == [1, 2, 3, 0, 4]; + +// predecessors +test bool predecessors1() + = predecessors({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, 4) == {2, 3}; +test bool predecessors2() = predecessors(G1, 2) == {1}; +test bool predecessors3() = predecessors(G1, 4) == {2, 3}; +test bool predecessors4() = predecessors(G2, 2) == {1, 3, 4}; +test bool predecessors5() = predecessors(G2, 5) == {3, 4}; +test bool predecessors6() = predecessors(G3, 8) == {7}; +test bool predecessors7() = predecessors(G3, 5) == {3, 4}; + +// reachR() +test bool reachR1() = reachR({}, {}, {}) == {}; +test bool reachR2() = reachR({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {1}, {}) == {}; +test bool reachR3() = reachR({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {1}, {1, 2}) == {2}; +test bool reachR4() = reachR({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {1}, {1, 2, 3}) == {2, 3}; +test bool reachR5() = reachR({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {1}, {1, 2, 4}) == {2, 4}; + +// reachX +test bool reachX1() = reachX({}, {}, {}) == {}; +test bool reachX2() = reachX({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {1}, {}) == {2, 3, 4}; +test bool reachX3() = reachX({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {1}, {2}) == {3, 4}; +test bool reachX4() = reachX({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {1}, {2, 3}) == {}; +test bool reachX5() = reachX({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {1}, {4}) == {2, 3}; + +// reach +test bool reach0() = reach({}, {}) == {}; +test bool reach1() = reach({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {1}) == {1, 2, 3, 4}; +test bool reach2() = reach({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {2}) == {2, 4}; +test bool reach3() = reach({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {3}) == {3, 4}; +test bool reach4() = reach({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {4}) == {4}; +test bool reach5() = reach({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {3, 4}) == {3, 4}; +test bool reach6() = reach({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, {2, 3}) == {2, 3, 4}; + +// TODO: shortestPathPair +// successors +test bool successors1() = successors({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}, 1) == {2, 3}; +test bool successors2() = successors(G1, 3) == {4}; +test bool successors3() = successors(G1, 1) == {2, 3}; +test bool successors4() = successors(G2, 1) == {2}; +test bool successors5() = successors(G3, 2) == {3, 4}; + +// top +test bool top1() = top({}) == {}; +test bool top2() = top({<1, 2 >, + <1, 3 >, + <2, 4 >, + <3, 4 >}) == {1}; +test bool top3() = top(G1) == {1}; +test bool top4() = top(G2) == {1}; +test bool top5() = top(G3) == {1}; +test bool top6() = top(G4) == {1}; +test bool top7() = top(G5) == {5, 7, 3}; +test bool top8() = top(G6) == {0}; +test bool top9() = top(G7) == {}; +test bool top10() = top(G8) == {0}; +test bool top11() = top(G9) == {}; +test bool top12() = top(G10) == {}; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/analysis/statistics/DescriptiveTests.rsc| +module lang::rascal::tests::library::analysis::statistics::DescriptiveTests + +import IO; +import List; +import util::Math; +import analysis::statistics::Descriptive; +import lang::rascal::tests::library::analysis::statistics::RangeUtils; + +bool eq(num a, num b) { + error = 1 / pow(10, min(scale(a), scale(b)) - 1); + return abs(a - b) <= error; +} +bool leq(num a, num b) = a < b ? true : eq(a, b); + +test bool geometricLessThanArithmeticMean(list[num] nums) { + if (nums == []) + return true; + nums = abs(nums); + nums = assureRange(nums, 0.1, 30); + return leq(geometricMean(nums), mean(nums)); +} +test bool meanTimesSizeEqualsSum(list[num] nums) { + if (nums == []) + return true; + return eq(mean(nums) * size(nums), sum(nums)); +} + +test bool percentileRelation(list[num] nums, int a, int b) { + if (nums == []) { + return true; + } + a = abs(a) % 100; + b = abs(b) % 100; + if (a > b) { + t = a; + a = b; + b = t; + } + return leq(percentile(nums, a), percentile(nums, b)); +} + +test bool varianceIsPositive(list[num] nums) { + if (nums == []) + return true; + nums = assureRange(nums, 0.0001, 400); + return variance(nums) >= 0; +} + +test bool kurtoiseNeverBelowZero(list[num] nums) { + if (nums == []) + return true; + nums = assureRange(nums, 0.0001, 400); + return variance(nums) > 0 ==> kurtosis(nums) >= 0; +} + +test bool standardDeviationIsPositive(list[num] nums) { + if (nums == []) + return true; + nums = assureRange(nums, 0.0001, 400); + return standardDeviation(nums) >= 0; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/analysis/statistics/RangeUtils.rsc| +@synopsis{Conversions and assertions on numerical ranges, for testing and sampling purposes.} +module lang::rascal::tests::library::analysis::statistics::RangeUtils + +import util::Math; +import IO; + +@synopsis{list of absolute numbers for every list element.} +list[&T <: num] abs(list[&T <: num] nums) = [abs(n) | n <- nums]; + +@synopsis{Returns `n` if low <= n <= high, or any other number that is guaranteed between the `low` and `high` bounds.} +@description{ +This function is used to _map_ randomly generated numbers _into_ a range. The goal +is to achieve a more-or-less **uniform distribution** inside of the range `[low, high]`, given a more +ore less uniformly distributed value for `n` over any other unknown range `[x,y]`. This works +best if `y - x >= high - low`, otherwise parts of the target range may be unreachable. +} +&T <: num assureRange(&T <: num n, &T <: num low, &T <: num high) { + assert low < high; + + target = n; + window = high - low; + + // jump above the lower bound into the range with steps sized `window` + while(target < low) { + target += ceil(low / window - target / window) * window; + } + + // or jump below the high bound into the range with steps sized `window` + while(high < target) { + target -= ceil(target / window - high / window) * window; + } + + assert low <= target && target <= high; + + return target; +} + +test bool assureRangeTest(num x) = 0 <= target && target <= 10 + when target := assureRange(x, 0, 10); + +@synopsis{Change a list of numbers into a list of numbers that all fit into a range.} +@description{ +The goal of this function is to make sure an otherwise randomly generated list of numbers +is limited to a given range, between `low` and `high` inclusive bounds. +The target numbers are the same if they already fit, and we try to keep a uniform distribution +within the range as much as possible. +} +list[&T <: num] assureRange(list[&T <: num] nums, &T <: num low, &T <: num high) + = [assureRange(n, low, high) | n <- nums]; + +test bool assureRangeListTest() + = assureRange([0..10], 100, 110) == [100..110]; +test bool assureRangeListTestNeg() + = assureRange([0..-10], 100, 110) == [100, *[109, 108, 107, 106, 105, 104, 103, 102, 101]]; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/lang/csv/CSVIOTests.rsc| +module lang::rascal::tests::library::lang::csv::CSVIOTests + +import IO; +import Type; +import lang::csv::IO; +import util::Math; +import ParseTree; +import DateTime; +import util::UUID; + +loc targetFile = |memory://test-tmp/csv-test-file--< "" >.csv| ; + +bool readWrite(set[&T] dt) = readWrite(type(typeOf(dt), ()), dt); +bool readWrite(type[&T] returnType, set[&T1] dt) { + if (dt == {}) + return true; + writeCSV(returnType, dt, targetFile); + + if (dt != readCSV(returnType, targetFile)) { + throw "Default read/write params"; + } + writeCSV(returnType, dt, targetFile, header = false); + if (dt != readCSV(returnType, targetFile, header = false)) { + throw "Header = false"; + } + writeCSV(returnType, dt, targetFile, separator = ";"); + if (dt != readCSV(returnType, targetFile, separator = ";")) { + throw "separator = ;"; + } + if (/\node() !:= typeOf(dt)) { + dt = fixAmbStrings(dt); + writeCSV(returnType, dt, targetFile); + if (dt != readCSV(targetFile)) { + println("expected: "); + println(dt); + println(readCSV(targetFile)); + throw "inferred types"; + } + } + return true; +} + +&T fixAmbStrings(&T v) { + return + visit(v) { + case str s => "a" + s when /^[ \t\n]*[0-9]+[ \t\n]*$/ := s + case str s => "a" + s when /^[ \t\n]*[0-9]+[ \t\n]*\.[0-9]*[ \t\n]*$/ := s + case str s + => "a" + s + when /^[ \t\n]*[0-9][ \t\n]*+r[ \t\n]*[0-9]*[ \t\n]*$/ := s + case str s => "a" + s when /^[ \t\n]*\<[ \t\<\>]*\>[ \t\n]*$/ := s + case str s => "a" + s when /^[ \t\n]*\<([ \t]|[^\>,])*\>[ \t\n]*$/ := s + case str s => "a" + s + "a" when /^[ \t\n]*[{\(\[][ \t\n]*[\)}\]][ \t\n]*$/ := s + } + ; +} + +test bool csvBooleanInfer() { + writeFile(targetFile, "col1,col2\nTRUE,True"); + return readCSV(targetFile) == {<true, true >}; +} + +test bool csvBoolean() { + writeFile(targetFile, "col1,col2\nTRUE,True"); + return readCSV(#rel[bool col1, bool col2], targetFile) == {<true, true >}; +} + +test bool csvDateTime() { + writeFile( + targetFile, + "col1,col2\n2012-06-24T00:59:56+02:00," + ); + r = readCSV(#lrel[datetime a, datetime b], targetFile)[0]; + println(r); + return r.a == r.b; +} + +test bool csvWithLoc(rel[loc first, int second] dt) = readWrite(dt); +test bool csvWithStr(rel[str first, int second] dt) = readWrite(dt); +test bool csvWithList(rel[list[&T] first, int second] dt) = readWrite(dt); +test bool csvWithSet(rel[set[&T] first, int second] dt) = readWrite(dt); +test bool csvWithMap(rel[map[&T, &Y] first, int second] dt) = readWrite(dt); +test bool csvWithNode(rel[node first, int second] dt) = readWrite(dt); + +// this can not work, when reading back we do not have enough information +// to specialize int and loc back to the original values. +//test bool csvRandom(rel[value x, value y] dt) = readWrite(dt); +test bool csvMoreTuples(rel[str a, str b, int c, bool d, real e] dt) + = readWrite(dt); + +// this can not work, when reading back we do not have enough information +// to specialize int and loc back to the original values. +// test bool csvMoreRandomTypes(rel[&T1 a, &T2 b, int c, str d, &T3 e] dt) = readWrite(dt); +test bool csvMoreRandomTypes(rel[loc a, loc b, int c, str d, loc e] dt) + = readWrite(dt); + +bool checkType(type[value] expected, str input) { + writeFile(targetFile, input); + + // Reified types in the interpreter and in compiled code differ slightly: + // - the symbol is identical + // - the definitions are "smaller" in the compiled version (they only contain + // symbols reachable from the symbol of the reified type). + // Therefore we only compare symbols here + return expected.symbol == getCSVType(targetFile).symbol; +} + +test bool csvTypeInference1() + = checkType(#rel[str, int], "col1,col2 + 'a,2 + '"); + +test bool csvTypeInference2() + = checkType(#rel[bool, real], "col1,col2 + 'true,42.0 + '"); + +test bool csvTypeInference3() + = checkType(#rel[num, int], "col1,col2 + '2.0,2 + '2,3 + '"); + +@memo +str createString(int j) = ( "a" | it + "" | i <- [0..j] ); + +test bool normalData() { + rel[str name, int arity, real tst] relDt + = {| i <- [1..200]}; + return readWrite(#rel[str name, int arity, real tst], relDt); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/lang/html/IOTests.rsc| +module lang::rascal::tests::library::lang::html::IOTests + +import lang::html::IO; +import IO; + +private loc exampleHTML = |std:///lang/html/examples/example.html| ; + +test bool simpleParseTest() = html(_) := readHTMLFile(exampleHTML); + +test bool partialDocumentsAreCompletedOnRead() + = html([head(_), body(_)]) := readHTMLString("\paragraaf\"); + +test bool partialDocumentsAreCompletedOnWrite() + = /html/ := writeHTMLString(p([text("paragraaf")])); + +test bool originTrackingElements() { + return + originTracking( + readHTMLFile(exampleHTML, trackOrigins = true), readFile(exampleHTML) + ); +} + +test bool originTrackingElementsWithEndTags() { + return + originTracking( + readHTMLFile(exampleHTML, trackOrigins = true, includeEndTags = true), + readFile(exampleHTML) + ); +} + +test bool canWriteWhatWeReadWithoutExceptions() { + aaa = readHTMLFile(exampleHTML, trackOrigins = true); + writeHTMLString(aaa); + return true; +} + +private bool originTracking(node example, str content) { + poss = [x.location | /HTMLElement x := example, !(x is text), !(x is \data)]; + + // every node has a .src field, otherwise this fails with an explicitTemplateSpecialization + for (loc p <- poss, p.offset?) { + // some (top) nodes do not have offsets + assert content[p.offset] == "\<"; + // all nodes start with a opening tag < + assert content[p.offset + p.length - 1] == "\>"; + // all nodes end with a closing tag > + } + + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/Issue2633.rsc| +module lang::rascal::tests::library::lang::json::Issue2633 + +import lang::json::IO; +import IO; + +data OuterData = outer(str name, str \type, list[Nested] nested_abc); +data Nested + = nested( + str \type, map[str, value] property__1, map[value, value] property__2 + ); + +test bool failsOnCertainJSON() { + str input + = "{ + ' \"name\": \"TESTING_ Adding one more character to this data causes the test to fail\", + ' \"type\": \"type_abcd\", + ' \"nested_abc\": [ + ' { + ' \"type\": \"line\", + ' \"property__1\": { \"y\": { \"value\": 2, \"absolute\": false } }, + ' \"property__2\": {} + ' }, + ' { + ' \"type\": \"line\", + ' \"property__1\": { \"y\": { \"value\": 2, \"absolute\": false } }, + ' \"property__2\": {} + ' }, + ' { + ' \"type\": \"line\", + ' \"property__1\": { \"y\": { \"value\": 2, \"absolute\": false } }, + ' \"property__2\": {} + ' }, + ' { + ' \"type\": \"line\", + ' \"property__1\": { \"y\": { \"value\": 2, \"absolute\": false } }, + ' \"property__2\": {} + ' }, + ' { + ' \"type\": \"line\", + ' \"property__1\": { \"y\": { \"value\": 2, \"absolute\": false } }, + ' \"property__2\": {} + ' }, + ' { + ' \"type\": \"line\", + ' \"property__1\": { \"y\": { \"value\": 2, \"absolute\": false } }, + ' \"property__2\": {} + ' }, + ' { + ' \"type\": \"line\", + ' \"property__1\": { \"y\": { \"value\": 2, \"absolute\": false } }, + ' \"property__2\": {} + ' } + ' ] + '} + '"; + + // Fails when the original data above is increased by even one character (e.g. the name, or changing a 2 to 20, or even adding a meaningless space anywhere) + parsedVFD = parseJSON(#OuterData, input, trackOrigins = true, lenient = true); + println("Parsed VisualFormData (After)"); + iprintln(parsedVFD); + + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc| +module lang::rascal::tests::library::lang::json::JSONIOTests + +import String; +import lang::json::IO; +import util::UUID; +import util::Maybe; +import IO; +import util::Math; +import Type; +import DateTime; +import List; +import Node; + +loc targetFile = |memory://test-tmp/test-< "" >.json| ; +public int maxLong = floor(pow(2, 63)) ; + +bool writeRead( + type[&T] returnType, + &T dt, + value(value x) normalizer = value (value x){ return x; }, + bool dateTimeAsInt = false, + bool rationalsAsString = false, + bool unpackedLocations = false, + bool explicitConstructorNames = false, + bool explicitDataTypes = false +) { + json + = asJSON( + dt, + dateTimeAsInt = dateTimeAsInt, + rationalsAsString = rationalsAsString, + unpackedLocations = unpackedLocations, + explicitConstructorNames = explicitConstructorNames, + explicitDataTypes = explicitDataTypes + ); + readBack + = normalizer( + parseJSON( + returnType, json, + explicitConstructorNames = explicitConstructorNames, + explicitDataTypes = explicitDataTypes + ) + ); + if (readBack !:= normalizer(dt)/* ignores additional src fields */ ) { + println("What is read back, a <type(typeOf(readBack), ())>:"); + iprintln(readBack); + println( + "Is different from the original, a <type(typeOf(normalizer(dt)), ())>:" + ); + iprintln(normalizer(dt)); + return false; + } + return true; +} + +// only single constructors supported for now +data DATA1 = data1(int n); +data DATA2 = data2(str n); +data DATA3 = data3(int n, str kw = "abc"); +data Enum + = x() + | y() + | z() + ; +data DATA4 = data4( Enum e = x()); + +@ignore{until #2133 is fixed} +test bool writeReadIsTheSameAsAsJSONparseJSON(value example) { + jsonFile = |memory://jsontests/example.json|; + writeJSON(jsonFile, example); + written = readFile(jsonFile); + + // asJON == writeJSON + assert asJSON(example) == written; + + // parseJSON == readJSON + assert toDefaultRec(parseJSON(#value, written)) == toDefaultRec(readJSON(#value, jsonFile)); + + return true; +} + +test bool jsonWithBool1(bool dt) = writeRead(#bool, dt); +test bool jsonWithInt1(int dt) = writeRead(#int, dt); +test bool jsonWithReal1(real dt) = writeRead(#real, dt); +test bool jsonWithRat1(rat dt) = writeRead(#rat, dt); +test bool jsonWithNum1(num dt) + = writeRead(#num, dt, normalizer = numNormalizer); + +test bool jsonWithLoc1(loc dt) = writeRead(#loc, dt); +test bool jsonWithLoc2(loc dt) + = writeRead(#loc, dt, unpackedLocations = true); +test bool jsonWithStr1(str dt) = writeRead(#str, dt); +test bool jsonWithDatetime1(datetime dt) = writeRead(#datetime, dt); +test bool jsonWithDatetime2(datetime dt) + = writeRead(#datetime, dt, dateTimeAsInt = true); +test bool jsonWithList1(list[int] dt) = writeRead(#list[int], dt); +test bool jsonWithSet1(set[int] dt) = writeRead(#set[int], dt); +test bool jsonWithMap1(map[int, int] dt) = writeRead(#map[int, int], dt); +@ignore{until #2133 is fixed} +test bool jsonWithNode1(node dt) + = writeRead(#node, dt, normalizer = toDefaultRec); +test bool jsonWithRational1(rat r) = writeRead(#rat, r); +test bool jsonWithRational2(rat r) + = writeRead(#rat, r, rationalsAsString = true); + +test bool jsonWithDATA11(DATA1 dt) = writeRead(#DATA1, dt); +test bool jsonWithDATA21(DATA2 dt) = writeRead(#DATA2, dt); +test bool jsonWithDATA12(DATA1 dt) + = writeRead(#DATA1, dt, explicitDataTypes = true); +test bool jsonWithDATA22(DATA2 dt) + = writeRead(#DATA2, dt, explicitDataTypes = true); +test bool jsonWithDATA13(DATA1 dt) + = writeRead(#DATA1, dt, explicitConstructorNames = true); +test bool jsonWithDATA23(DATA2 dt) + = writeRead(#DATA2, dt, explicitConstructorNames = true); + +@synopsis{all values can be written and read again} +@description{ +However sets are always read back in as lists if we don't have +a specific abstract data-type that can enforce sets. +} +@ignore{until #2133 is fixed} +test bool jsonRandom1(value dt) + = writeRead(#value, dt, normalizer = toDefaultRec); + +test bool json1() = writeRead(#DATA1, data1(123)); +test bool json2() = writeRead(#DATA2, data2("123")); +test bool json3() = writeRead(#DATA3, data3(123, kw = "123")); +test bool json4(Enum e) = writeRead(#DATA4, data4(e = e)); + +bool originTest(loc example) { + ex2 = readJSON(#node, example, trackOrigins = true); + content = readFile(example); + lines = split("\n", content); + poss = [ | /node x := ex2, x.line?]; + + // every node has a .src field, otherwise this fails with an exception + for (<loc p, int line> <- poss) { + assert content[p.offset] == "{"; + // all nodes start with a { + assert content[p.offset + p.length - 1] == "}"; + // all nodes end with a } + assert p.begin.line == line; + assert lines[p.begin.line - 1][p.begin.column] == "{"; + assert lines[p.end.line - 1][p.end.column - 1] == "}"; + } + + return true; +} + +test bool originTracking() { + files + = [l + | loc l <- |std:///lang/rascal/tests/library/lang/json|.ls, l.extension == "json"]; + + return ( true | it && originTest(example) | loc example <- files ); +} + +value numNormalizer(int i) = i % maxLong + when abs(i) > maxLong; +value numNormalizer(real r) = r - round(r) == 0 ? round(r) : fitDouble(r); +default value numNormalizer(value x) = x; + +@synopsis{Normalizer used to replace unrecoverable types with their default representatives} +value toDefaultRec(value readBack) { + value mapToSet(value _: map[value, value] m) = {{k, m[k]}| value k <- m}; + default value mapToSet(value x) = x; + + // first we remove maps cleanly to avoid key collisions caused by normalization + noMaps = visit(readBack) { + case value v => mapToSet(v) + } + ; + + // then we normalize the rest, which could cause key collisions if maps were still there + return visit(noMaps) { + case value x => toDefaultValue(x) + } + ; +} + +// The list order depends on the hashcodes of the children +// as the writer is top-down and this rewrite is bottom-up +// we end up with different lists sometimes (if the elements have been rewritten). +// therefore we normalize all lists to sets, as they can be tested for equality +// regardless of order and hashcode collisions +value toDefaultValue(set[value] x) = x; +value toDefaultValue(list[value] x) = {*x}; +value toDefaultValue(map[void, void] _) = {}; +value toDefaultValue(node x) + = {{k, m[k]}| m := getKeywordParameters(x), k <- m} + + {*[{"arg", c[i]} | c := getChildren(x), i <- index(c)]}; +value toDefaultValue(map[value, value] m) = {{k, m[k]}| k <- m}; + +// commented until issue #2133 can be fixed +// value toDefaultValue(<>) = {}; +value toDefaultValue(<value x>) = {x}; +value toDefaultValue(<value x, value y>) = toDefaultValue([x, y]); +value toDefaultValue(<value x, value y, value z>) = toDefaultValue([x, y, z]); +value toDefaultValue(<value x, value y, value z, value a>) + = toDefaultValue([x, y, z, a]); +value toDefaultValue(<value x, value y, value z, value a, value b>) + = toDefaultValue([x, y, z, a, b]); +value toDefaultValue(<value x, value y, value z, value a, value b, value c>) + = toDefaultValue([x, y, z, a, b, c]); +value toDefaultValue( + <value x, value y, value z, value a, value b, value c, value d> +) + = toDefaultValue([x, y, z, a, b, c, d]); +value toDefaultValue( + <value x, value y, value z, value a, value b, value c, value d, value e> +) + = toDefaultValue([x, y, z, a, b, c, d, e]); +value toDefaultValue( + <value x, value y, value z, value a, value b, value c, value d, value e, value f> +) + = toDefaultValue([x, y, z, a, b, c, d, e, f]); +value toDefaultValue(loc l) { + // this simulates the simplications the writer applies + if (!(l.offset?)) { + if (l.scheme == "file") { + return l.path; + } + else { + return ""[1..-1]; + } + } + else { + return ""; + } +} + +value toDefaultValue(int i) = i % maxLong + when abs(i) > maxLong; +value toDefaultValue(rat r) = {numerator(r), denominator(r)}; +value toDefaultValue(datetime t) + = printDateTime(t, "yyyy-MM-dd\'T\'HH:mm:ssZ"); +value toDefaultValue(real r) = r - round(r) == 0 ? round(r) : fitDouble(r); +default value toDefaultValue(value x) = x; + +test bool accurateParseErrors() { + ex = readFile(|std:///lang/rascal/tests/library/lang/json/glossary.json|); + broken = ex[..size(ex) / 2] + ex[size(ex) / 2 + 10..]; + + try { + ex2 = parseJSON(#node, broken, trackOrigins = true); + } + catch ParseError(loc l): + return l.begin.line == 14; + + try { + // accurate locations have to be provided also when trackOrigins=false + ex2 = parseJSON(#node, broken, trackOrigins = false); + } + catch ParseError(loc l): + return l.begin.line == 14; + + return true; +} + +@ignore{until #2133 is fixed} +test bool regression1() = jsonRandom1(("a" : 12, + [] : {})); + +data Cons = cons( str bla = "null"); + +test bool dealWithNull() { + // use the default nulls map + assert parseJSON(#map[str, value], "{\"bla\": null}") == ("bla" : "null"()); + + // using our own nulls map + assert parseJSON(#map[str, value], "{\"bla\": null}", nulls = (#value : -1)) == ("bla" : -1); + + // conflicting entries in the nulls maps: more specific goes first: + assert parseJSON( + #map[str, node], "{\"bla\": null}", nulls = (#value : -1, + #node : "null"()) + ) == ("bla" : "null"()); + + // the builtin Maybe interpreter with null + assert parseJSON(#map[str, Maybe[str]], "{\"bla\": null}") == ("bla" : nothing()); + + // the builtin Maybe interpreter with non-null + assert parseJSON(#map[str, Maybe[str]], "{\"bla\": \"foo\"}") == ("bla" : just("foo")); + + // test different specific nulls for different expected types: + for (t <- defaultJSONNULLValues<0>) { + assert parseJSON(t, "null") == (defaultJSONNULLValues[t] ? "default-not-found"); + } + + assert parseJSON(#list[int], "[1,null,2]", nulls = ()) == [1, 2]; + assert parseJSON(#list[int], "[1,null,2]") == [1, defaultJSONNULLValues[#int], 2]; + assert parseJSON(#set[int], "[1,null,2]", nulls = ()) == {1, 2}; + assert parseJSON(#set[int], "[1,null,2]") == {1, defaultJSONNULLValues[#int], 2}; + + try { + assert parseJSON(#tuple[int, int], "[null,null]", nulls = ()) == <0, 0>; + } + catch ParseError(_): + assert true; + + // test undefined top-level null + try { + parseJSON(#int, "null", nulls = ()); + assert false; + } + catch ParseError(_): + assert true; + + // keyword parameters and null + assert cons( + bla = "foo" + ) := parseJSON(#Cons, "{\"bla\": \"foo\"}"); + assert cons() := parseJSON(#Cons, "{\"bla\": null}"); + + return true; +} + +data Example = example( Prop ex = F()); + +data Prop + = T() + | F() + | and(Prop lhs, Prop rhs) + | or(Prop lhs, Prop rhs) + ; + +str format(T()) = "true"; +str format(F()) = "false"; +str format(and(Prop p1, Prop p2)) = " && "; +str format(or(Prop p1, Prop p2)) = " || "; + +Prop parse(type[Prop] _, "true") = T(); +Prop parse(type[Prop] _, "false") = F(); + +test bool formattingToStringsTest() { + ex1 = and(and(\T(), \F()), or(\T(), \F())); + + writeJSON( + |memory://test-json/formatted.json|, example(ex = ex1), formatter = format + ); + source = readFile(|memory://test-json/formatted.json|); + + assert source == "{\"ex\":\"true && false && true || false\"}"; + + writeFile(|memory://test-json/printed.json|, "{\"ex\":\"true\"}"); + + Example result + = readJSON(#Example, |memory://test-json/printed.json|, parser = parse); + + assert result.ex == T(); + + return true; +} + +test bool explicitConstructorNames() { + tmp = data4(e = z()); + json = asJSON(tmp, explicitConstructorNames = true); + + assert json == "{\"_constructor\":\"data4\",\"e\":{\"_constructor\":\"z\"}}"; + + assert tmp2 := parseJSON(#DATA4, json, explicitConstructorNames = true) && tmp2 := tmp; + + // here we can't be sure to get z() back, but we will get some Enum + assert data4( + e = Enum _ + ) := parseJSON(#DATA4, json, explicitConstructorNames = false); + + return true; +} + +test bool explicitDataTypes() { + tmp = data4(e = z()); + json = asJSON(tmp, explicitDataTypes = true); + + assert json == "{\"_constructor\":\"data4\",\"_type\":\"DATA4\",\"e\":{\"_constructor\":\"z\",\"_type\":\"Enum\"}}"; + + // _constructor and _type must be the first fields + assert tmp2 := parseJSON(#DATA4, json, explicitDataTypes = true) && tmp := tmp2; + + // _type and _constructor may appear in a different order + flippedJson + = "{\"_type\":\"DATA4\",\"_constructor\":\"data4\",\"e\":{\"_constructor\":\"z\",\"_type\":\"Enum\"}}"; + assert tmp2 := parseJSON(#DATA4, flippedJson, explicitDataTypes = true) && tmp := tmp2; + + // here we can't be sure to get z() back, but we will get some Enum + assert data4( + e = Enum _ + ) := parseJSON(#DATA4, json, explicitDataTypes = false); + + return true; +} + +data X (loc src = |unkown:///|) = v1( int x = 0, str s = ""); + +test bool jsonVerifyOriginCorrect() { + ref = v1(x = 123456789); + refExpected = asJSON(ref); + t1 = [v1(s = "hoi"), ref]; + writeJSON(|memory:///test.json|, t1); + v = readJSON(#list[X], |memory:///test.json|, trackOrigins = true); + return refExpected == readFile(v[1].src); +} + +test bool triggerIssue2633() { + return jsonVerifyOriginCorrectAcrossBufferBoundaries(1023); +} + +test bool jsonVerifyOriginCorrectAcrossBufferBoundaries() { + /* twice just before and after the 1024 buffer size of JsonReader */ for (int sSize <- [1000..1025] + [2000..2050]) { + jsonVerifyOriginCorrectAcrossBufferBoundaries(sSize); + } + return true; +} + +bool jsonVerifyOriginCorrectAcrossBufferBoundaries(int sSize) { + ref = v1(x = 123456789); + refExpected = asJSON(ref); + + t1 = [v1(s = "<for (_ <- [0..sSize]) {>a<}>"), ref]; + writeJSON(|memory:///test.json|, t1); + + //s this throws exceptions and asserts if there are bugs with the + // origin tracker. In particular it triggers #2633 + v = readJSON(#list[X], |memory:///test.json|, trackOrigins = true); + + // checking the last element + if (refExpected != readFile(v[1].src)) { + println("Failed for : != "); + return false; + } + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/lang/xml/XMLIOTests.rsc| +module lang::rascal::tests::library::lang::xml::XMLIOTests + +import IO; +import String; +import lang::xml::IO; +import util::Maybe; + +bool checkXMLResult(str input, node expected, bool fullyQualify = false) { + result = readXML(input, fullyQualify = fullyQualify); + if (result == expected) { + return true; + } + println("readXML failed"); + println("Expected: "); + iprintln(expected); + println("Got: "); + iprintln(result); + return false; +} + +str buildXML(str name, map[str, str] attrs = (), list[str] children = []) { + result = "\<"; + for (a <- attrs) { + result += " = \"\""; + } + result += "\>"; + for (c <- children) { + result += c; + } + return result + "\\>"; +} + +str fixColon(str attr) = visit(attr) { + case /-/ => ":" + }; + +test bool simpleXMLTest1() = checkXMLResult(buildXML("xml"), "xml"()); + +test bool simpleXMLTest2() + = checkXMLResult(buildXML("xml", children = [buildXML("a")]), "xml"("a"())); + +test bool simpleXMLTest3() + = checkXMLResult( + buildXML("xml", attrs = ("href" : "#42")), "xml"(href = "#42") + ); + +test bool namespaceIgnored1() + = checkXMLResult( + buildXML("table", attrs = ("xmlns" : "http://www.w3.org/TR/html4/")), + "table"() + ); + +test bool namespaceIgnored2() + = checkXMLResult( + buildXML( + "table", + attrs = ("xmlns-ht" : "http://www.w3.org/TR/html4/"), + children = [buildXML("ht:tr")] + ), + "table"("tr"()) + ); + +test bool namespaceIncluded() + = checkXMLResult( + buildXML( + "table", + attrs = ("xmlns-ht" : "http://www.w3.org/TR/html4/"), + children = [buildXML("ht-tr")] + ), "table"("ht-tr"(), xmlns = ("ht" : "http://www.w3.org/TR/html4/")), + fullyQualify = true + ); + +test bool namespacesMultiple() + = checkXMLResult( + buildXML( + "table", + attrs = ("xmlns-ht" : "http://www.w3.org/TR/html4/"), + children = + [ + buildXML("ht-tr"), + buildXML("tr", attrs = ("xmlns-ht2" : "http://www.w3.org/TR/html5/")) + ] + ), "table"( + "ht-tr"(), "tr"(xmlns = ("ht2" : "http://www.w3.org/TR/html5/")), + xmlns = ("ht" : "http://www.w3.org/TR/html4/") + ), + fullyQualify = true + ); + +test bool originTrackingElements() { + loc l = |std:///lang/rascal/tests/library/lang/xml/glossary.xml|; + return originTracking(readXML(l, trackOrigins = true), readFile(l)); +} + +test bool originTrackingElementsWithEndTags() { + loc l = |std:///lang/rascal/tests/library/lang/xml/glossary.xml|; + return + originTracking( + readXML(l, trackOrigins = true, includeEndTags = true), readFile(l) + ); +} + +private bool originTracking(node example, str content) { + poss = [ | /node x := example, x.line?]; + + // every node has a .src field, otherwise this fails with an exception + for (<loc p, str line> <- poss, p.offset?) { + // some (top) nodes do not have offsets + assert + content[p.offset] == "\<" : "

does not start with an opening \<"; + + // all nodes start with a opening tag < + assert + trim(content[p.offset..p.offset + p.length])[-1] == "\>" + : "

does not end with a closing \> []]"; + + // all nodes end with a closing tag > + assert + "" == line + : "

begin line is unequal to actual begin line "; + } + + return true; +} + +test bool streamingAPI() { + loc l = |std:///lang/rascal/tests/library/lang/xml/glossary.xml|; + next + = streamXML( + |std:///lang/rascal/tests/library/lang/xml/glossary.xml|, "GlossEntry" + ); + + return just(node _) := next() && nothing() := next(); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/util/MaybeTests.rsc| +module lang::rascal::tests::library::util::MaybeTests + +import util::Maybe; + +test bool nothingStrEqual() { + Maybe[str] x = nothing(); + Maybe[str] y = nothing(); + return x == nothing() && y == nothing() && x == y; +} + +test bool just2nothing() { + x = just(3); + x = nothing(); + return x == nothing(); +} + +test bool inc1() { + Maybe[int] x = just(3); + x.val += 1; + return x.val == 4; +} + +test bool inc2() { + x = just(3); + x.val += 1; + return x.val == 4; +} + +test bool inc3() { + x = just((1 : "a")); + x.val[1] ? "aaa" += "xxx"; + return x.val == (1 : "axxx"); +} + +test bool inc4() { + x = just((1 : "a")); + x.val[2] ? "aaa" += "xxx"; + return x.val == (1 : "a", + 2 : "aaaxxx"); +} + +data X + = contain(Maybe[bool] p, Maybe[int] kw1 = just(2), Maybe[str] kw2 = nothing()); + +test bool contain1() { + c = contain(nothing()); + return c.p == nothing() && c.kw1.val == 2 && c.kw2 == nothing(); +} + +test bool contain2() { + c = contain(nothing(), kw1 = nothing(), kw2 = just("hi")); + return c.p == nothing() && c.kw1 == nothing() && c.kw2 == just("hi"); +} + +test bool contain3() { + c = contain(nothing()); + c.kw1 = nothing(); + c.kw2 = just("hi"); + return c.p == nothing() && c.kw1 == nothing() && c.kw2 == just("hi"); +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/util/PriorityQueueTests.rsc| +module lang::rascal::tests::library::util::PriorityQueueTests + +import List; +import util::PriorityQueue; + +test bool prioTest() { + Q = mkPriorityQueue(); + + elms = [10, 8, 50, 30, 1]; + for (int i <- elms) + Q = insertElement(Q, i, i); + + list[int] sorted = []; + while(size(sorted) < size(elms)) { + = extractMinimum(Q); + sorted = sorted + [minimum]; + } + + return true; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/util/ReflectiveTests.rsc| +module lang::rascal::tests::library::util::ReflectiveTests + +import IO; +import List; +import String; +import util::FileSystem; +import util::Reflective; +import util::UUID; + +private loc testLibraryLoc = |memory://myTestLibrary-< uuid().authority >/| ; + +test bool commonSuffixCommutative(list[str] a, list[str] b) + = commonSuffix(a, b) == commonSuffix(b, a); +test bool cs1() = commonSuffix ( + [], + ["c"] + ) == 0; + +test bool cs2() = commonSuffix ( + ["c"], + ["c"] + ) == 1; + +test bool cs3() = commonSuffix ( + ["a", "b", "c"], + ["c"] + ) == 1; + +test bool cs4() = commonSuffix ( + ["a", "b", "c"], + ["b", "c"] + ) == 2; +test bool cs5() = commonSuffix ( + ["a", "b", "c"], + ["a", "b", "c"] + ) == 3; +test bool cs6() = commonSuffix ( + ["a", "b", "c"], + ["z", "a", "b", "c"] + ) == 3; +test bool cs7() = commonSuffix ( + ["a", "b", "c"], + ["a", "b", "d"] + ) == 0; + +test bool moduleExceptionWithSrc() { + pcfg = pathConfig(srcs = [|project://rascal/src/org/rascalmpl/library/|]); + return + getModuleName( + |project://rascal/src/org/rascalmpl/library/Exception.rsc|, pcfg + ) == "Exception"; +} + +test bool moduleReflectiveWithSrc() { + pcfg = pathConfig(srcs = [|project://rascal/src/org/rascalmpl/library/|]); + return + getModuleName( + |project://rascal/src/org/rascalmpl/library/util/Reflective.rsc|, pcfg + ) == "util::Reflective"; +} + +test bool moduleExceptionOnlyTpl() { + writeFile( + testLibraryLoc + "/resources/rascal/$Exception.tpl", + "$Exception.tpl (only file matters, content irrelevant) + " + ); + pcfg = pathConfig(libs = [testLibraryLoc + "/resources/"]); + return + getModuleName( + |project://rascal/src/org/rascalmpl/library/Exception.rsc|, pcfg + ) == "Exception"; +} + +test bool moduleReflectiveOnlyTpl() { + writeFile( + testLibraryLoc + "/resources/rascal/util/Reflective.tpl", + "util::$Reflective.tpl (only file matters, content irrelevant) + " + ); + pcfg = pathConfig(srcs = [], libs = [testLibraryLoc + "/resources/"]); + return + getModuleName( + |project://rascal/src/org/rascalmpl/library/util/Reflective.rsc|, pcfg + ) == "util::Reflective"; +} + +test bool longestModuleReflectiveOnlyTpl() { + writeFile( + testLibraryLoc + "/1/resources/rascal/$Reflective.tpl", + "$Reflective.tpl at top level (only file matters, content irrelevant) + " + ); + writeFile( + testLibraryLoc + "/2/resources/rascal/util/Reflective.tpl", + "util::$Reflective.tpl in subdir util (only file matters, content irrelevant) + " + ); + pcfg + = pathConfig( + srcs = [], + libs = [testLibraryLoc + "1/resources/", testLibraryLoc + "/2/resources/"] + ); + return + getModuleName( + |project://rascal/src/org/rascalmpl/library/util/Reflective.rsc|, pcfg + ) == "util::Reflective"; +} + +test bool moduleOnlyInSecondSrc() { + testLibrarySrc = testLibraryLoc + "src/org/rascalmpl/library/"; + ESrc = testLibrarySrc + "E.rsc"; + println("moduleOnlyInSecondSrc: "); + writeFile(ESrc, "module E"); + + pcfg + = pathConfig( + srcs = [|project://rascal/src/org/rascalmpl/library/|, testLibrarySrc] + ); + return getModuleName(ESrc, pcfg) == "E"; +} +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/util/SemVerTests.rsc| +module lang::rascal::tests::library::util::SemVerTests + +import util::SemVer; +import Exception; + +test bool lessVersion1() = lessVersion("1.0.0", "2.0.0"); +test bool lessVersion2() = lessVersion("2.0.0", "2.1.0"); +test bool lessVersion3() = lessVersion("2.1.0", "2.1.1"); +test bool lessVersion4() = lessVersion("1.0.0-alpha", "1.0.0"); +test bool lessVersion5() = lessVersion("1.0.0-alpha", "1.0.0-alpha.1"); +test bool lessVersion6() + = lessVersion("1.0.0-alpha.1", "1.0.0-alpha.beta"); +test bool lessVersion7() = lessVersion("1.0.0-alpha.beta", "1.0.0-beta"); +test bool lessVersion8() = lessVersion("1.0.0-beta", "1.0.0-beta.2"); +test bool lessVersion9() = lessVersion("1.0.0-beta.2", "1.0.0-beta.11"); +test bool lessVersion10() = lessVersion("1.0.0-beta.11", "1.0.0-rc.1"); +test bool lessVersion11() = lessVersion("1.0.0-rc.1", "1.0.0"); + +test bool ge1() = satisfiesVersion("1.2.7", "\>=1.2.7"); +test bool ge2() = satisfiesVersion("1.2.8", "\>=1.2.7"); +test bool ge3() = satisfiesVersion("2.5.3", "\>=1.2.7"); +test bool ge4() = satisfiesVersion("1.3.9", "\>=1.2.7"); +test bool ge5() = !satisfiesVersion("1.2.6", "\>=1.2.7"); +test bool ge6() = !satisfiesVersion("1.1.0", "\>=1.2.7"); + +test bool and1() = satisfiesVersion("1.2.7", "\>=1.2.7 \<1.3.0"); +test bool and2() = satisfiesVersion("1.2.8", "\>=1.2.7 \<1.3.0"); +test bool and3() = satisfiesVersion("1.2.99", "\>=1.2.7 \<1.3.0"); +test bool and4() = !satisfiesVersion("1.2.6", "\>=1.2.7 \<1.3.0"); +test bool and5() = !satisfiesVersion("1.3.0", "\>=1.2.7 \<1.3.0"); +test bool and6() = !satisfiesVersion("1.1.0", "\>=1.2.7 \<1.3.0"); + +test bool or1() = satisfiesVersion("1.2.7", "1.2.7 || \>=1.2.9 \<2.0.0"); +test bool or2() = satisfiesVersion("1.2.9", "1.2.7 || \>=1.2.9 \<2.0.0"); +test bool or3() = satisfiesVersion("1.4.6", "1.2.7 || \>=1.2.9 \<2.0.0"); +test bool or4() = !satisfiesVersion("1.2.8", "1.2.7 || \>=1.2.9 \<2.0.0"); +test bool or5() = !satisfiesVersion("2.0.0", "1.2.7 || \>=1.2.9 \<2.0.0"); + +test bool prerelease1() + = satisfiesVersion("1.2.3-alpha.7", "\>1.2.3-alpha.3"); +test bool prerelease2() + = satisfiesVersion("1.2.3-alpha.9", "\>1.2.3-alpha.3"); +test bool prerelease3() = satisfiesVersion("3.4.5", "\>1.2.3-alpha.3"); + +test bool hyphen1() = satisfiesVersion("1.2.3", "1.2.3 - 2.3.4"); +test bool hyphen2() = satisfiesVersion("2.2.3", "1.2.3 - 2.3.4"); +test bool hyphen3() = !satisfiesVersion("2.3.5", "1.2.3 - 2.3.4"); +test bool hyphen4() = satisfiesVersion("1.2.3", "1.2 - 2.3.4"); +test bool hyphen5() = satisfiesVersion("1.2.3", "1.2 - 2.3"); +test bool hyphen6() = satisfiesVersion("2.2.99", "1.2 - 2.3"); +test bool hyphen7() = satisfiesVersion("2.3.0", "1.2 - 2.3"); +test bool hyphen8() = !satisfiesVersion("2.3.1", "1.2 - 2.3"); +test bool hyphen9() = satisfiesVersion("1.2.3", "1.2 - 2"); +test bool hyphen10() = !satisfiesVersion("2.3.1", "1.2 - 2"); +test bool hyphen11() = !satisfiesVersion("3", "1.2 - 2"); + +test bool x1() = satisfiesVersion("1.2.3", "*"); +test bool x2() = satisfiesVersion("1.2.3", "x"); +test bool x3() = satisfiesVersion("1.2.3", "1.*"); +test bool x4() = !satisfiesVersion("2.2.3", "1.*"); +test bool x5() = satisfiesVersion("1.2.3", "1.2.*"); +test bool x6() = !satisfiesVersion("1.3.3", "1.2.*"); + +test bool tilde1() = satisfiesVersion("1.2.3", "~1.2.3"); +test bool tilde2() = satisfiesVersion("1.2.9", "~1.2.3"); +test bool tilde3() = !satisfiesVersion("1.3.0", "~1.2.3"); +test bool tilde4() = satisfiesVersion("1.2.0", "~1.2"); +test bool tilde5() = satisfiesVersion("1.2.9", "~1.2"); +test bool tilde6() = !satisfiesVersion("1.3.0", "~1.2"); +test bool tilde7() = satisfiesVersion("1.0.0", "~1"); +test bool tilde8() = satisfiesVersion("1.2.3", "~1"); +test bool tilde9() = !satisfiesVersion("2.0.0", "~1"); +test bool tilde10() = satisfiesVersion("0.2.3", "~0.2.3"); +test bool tilde11() = satisfiesVersion("0.2.9", "~0.2.3"); +test bool tilde12() = !satisfiesVersion("0.3.0", "~0.2.3"); +test bool tilde13() = satisfiesVersion("0.2.0", "~0.2"); +test bool tilde14() = satisfiesVersion("0.2.9", "~0.2"); +test bool tilde15() = !satisfiesVersion("0.3.0", "~0.2"); +test bool tilde16() = satisfiesVersion("0.0.0", "~0"); +test bool tilde17() = satisfiesVersion("0.1.2", "~0"); +test bool tilde18() = !satisfiesVersion("1.0.0", "~0"); +test bool tilde19() = satisfiesVersion("1.2.3-beta.2", "~1.2.3-beta.2"); +test bool tilde20() = satisfiesVersion("1.2.4", "~1.2.3-beta.2"); +test bool tilde21() = !satisfiesVersion("1.3.0", "~1.2.3-beta.2"); +test bool tilde22() = satisfiesVersion("1.2.3-beta.3", "~1.2.3-beta.2"); +test bool tilde23() = satisfiesVersion("1.2.4-beta.2", "~1.2.3-beta.2"); + +test bool caret1() = satisfiesVersion("1.2.3", "^1.2.3"); +test bool caret2() = satisfiesVersion("1.4.9", "^1.2.3"); +test bool caret3() = !satisfiesVersion("2.0.0", "^1.2.3"); +test bool caret4() = satisfiesVersion("0.2.3", "^0.2.3"); +test bool caret5() = satisfiesVersion("0.2.9", "^0.2.3"); +test bool caret6() = !satisfiesVersion("0.3.0", "^0.2.3"); +test bool caret7() = satisfiesVersion("0.0.3", "^0.0.3"); +test bool caret8() = !satisfiesVersion("0.0.4", "^0.0.3"); +test bool caret9() = satisfiesVersion("1.2.3-beta.2", "^1.2.3-beta.2"); +test bool caret10() = !satisfiesVersion("2.0.0", "^1.2.3-beta.2"); +test bool caret11() = satisfiesVersion("1.2.3-beta.4", "^1.2.3-beta.2"); +test bool caret12() = satisfiesVersion("1.2.4-beta.2", "^1.2.3-beta.2"); +test bool caret13() = satisfiesVersion("0.0.3-beta", "^0.0.3-beta"); +test bool caret14() = !satisfiesVersion("0.0.4", "^0.0.3-beta"); +test bool caret15() = satisfiesVersion("0.0.3-pr.2", "^0.0.3-beta"); +test bool caret16() = satisfiesVersion("1.2.0", "^1.2.x"); +test bool caret17() = satisfiesVersion("1.2.5", "^1.2.x"); +test bool caret18() = !satisfiesVersion("2.0.0", "^1.2.x"); +test bool caret19() = satisfiesVersion("0.0.0", "^0.0.x"); +test bool caret20() = satisfiesVersion("0.0.5", "^0.0.x"); +test bool caret21() = !satisfiesVersion("0.1.0", "^0.0.x"); +test bool caret22() = satisfiesVersion("0.0.0", "^0.0"); +test bool caret23() = satisfiesVersion("0.0.5", "^0.0"); +test bool caret24() = !satisfiesVersion("0.1.0", "^0.0"); +test bool caret25() = satisfiesVersion("1.0.0", "^1.x"); +test bool caret26() = satisfiesVersion("1.5.3", "^1.x"); +test bool caret27() = !satisfiesVersion("2.0.0", "^1.x"); +test bool caret28() = satisfiesVersion("0.0.0", "^0.x"); +test bool caret29() = satisfiesVersion("0.5.3", "^0.x"); +test bool caret30() = !satisfiesVersion("1.0.0", "^0.x"); + +@expected{IllegalArgument} +test bool invalid1() = lessVersion("a.1.2", "b.1.2"); + +@expected{IllegalArgument} +test bool invalid2() = lessVersion("1;1.2", "1;1.2"); + +@expected{IllegalArgument} +test bool invalid3() = satisfiesVersion("0.5.3", "^0.y"); + +@expected{IllegalArgument} +test bool invalid4() = satisfiesVersion("0.5.3", "@0.y"); + +@expected{IllegalArgument} +test bool invalid5() = satisfiesVersion("0.5.3", "0.1 - 0.z"); + +@expected{IllegalArgument} +test bool invalid6() = satisfiesVersion("0.1", "0.1 z 0.2"); + +@expected{IllegalArgument} +test bool invalid7() = satisfiesVersion("0.5.3", "\> =0.1"); + +@expected{IllegalArgument} +test bool invalid8() = satisfiesVersion("0.5.3", "\>= 0.1"); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/tests/library/util/UUIDTests.rsc| +module lang::rascal::tests::library::util::UUIDTests + +import util::UUID; + +test bool uniqueness() = uuid() != uuid(); +### |project://rascal/src/org/rascalmpl/library/lang/rascal/upgrade/UpdateNestedListAndSetPatterns.rsc| +@bootstrapParser +module lang::rascal::upgrade::UpdateNestedListAndSetPatterns + +import lang::rascal::\syntax::Rascal; +import util::FileSystem; +import ParseTree; +import IO; +import Message; + +list[Message] report(loc root) + = [*report(parse(#start[Module], m)) | m <- find(root, "rsc")]; + +void update(loc root) { + modules = [f | /file(f) := crawl(root), f.extension == "rsc"]; + for (m <- modules) { + writeFile(m, ""); + } +} + +list[Message] report(Tree m) + = [info("found postfix multivar", name@\loc) + | /(Pattern) `*` := m]; + +Tree updateTree(Tree m) + = visit(m) { + case (Pattern) `*` + => (Pattern) `*` + }; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/upgrade/UpgradePostfixStarAndPlusToPrefix.rsc| +@bootstrapParser +module lang::rascal::upgrade::UpgradePostfixStarAndPlusToPrefix + +import util::FileSystem; +import lang::rascal::\syntax::Rascal; +import ParseTree; +import IO; +import Message; + +list[Message] report(loc root) + = [*report(parse(#start[Module], m)) | m <- find(root, "rsc")]; + +void update(loc root) { + for (m <- find(root, "rsc")) { + writeFile(m, ""); + } +} + +list[Message] report(Tree m) { + result = []; + visit(m) { + case (Pattern) `[<{Pattern ","}* _>,list[] ,<{Pattern ","}* _>]`: + result += [info("found list pattern to upgrade", elem@\loc)]; + case (Pattern) `{<{Pattern ","}* _>,set[] ,<{Pattern ","}* _>}`: + result += [info("found list pattern to upgrade", elem@\loc)]; + } + + return result; +} + +public +Tree updateTree(Tree m) + = innermost visit(m) { + case (Pattern) `[<{Pattern ","}* before>,list[] ,<{Pattern ","}* after>]` + => (Pattern) `[<{Pattern ","}* before>, * , <{Pattern ","}* after>]` + case (Pattern) `{<{Pattern ","}* before>,set[] ,<{Pattern ","}* after>}` + => (Pattern) `{<{Pattern ","}* before>, * , <{Pattern ","}* after>}` + case Pattern _: + fail; + }; +### |project://rascal/src/org/rascalmpl/library/lang/rascal/vis/ImportGraph.rsc| +@synopsis{Visualize the import and extend structure of a Rascal project using a hierarchical graph layout} +@description{ +The so-called "import graph" for Rascal modules stems from the times of the ASF+SDF Meta-Environment. +There it was the core UI for interacting with a loaded modular language specification. We recreated +it here for Rascal, but in an on-demand fashion. You can look at the import graph but it is not kept up-to-date +with the state of the files in the IDE. It is more of a snapshot of the current situation. +} +@benefits{ +* A visual representation can help avoid overly complex dependencies, including unnecessary cycles. +* Visual analysis may help get an overview of a complex Rascal application. +* Modular and extensible language implementations often provide nice pictures where each layer +is clearly visible. +} +@pitfalls{ +* The visualization is a static snapshot and does not change automatically when files are saved. +} +@bootstrapParser +module lang::rascal::vis::ImportGraph + +import util::Reflective; +import vis::Graphs; +import lang::rascal::grammar::definition::Modules; +import lang::rascal::\syntax::Rascal; +import Exception; +import util::FileSystem; +import util::IDEServices; +import IO; +import analysis::graphs::Graph; +import Set; + +@synopsis{If `projectName` is an open project in the current IDE, the visualize its import/extend graph.} +void importGraph(str projectName, bool hideExternals = true) { + importGraph(|project://< projectName >|, hideExternals = hideExternals); +} + +@synopsis{Given an arbitrary root folder of a Rascal project, visualize its import/extend graph.} +void importGraph(loc projectRoot, bool hideExternals = true) { + // we use compiler() mode here to avoid diving into the transitively depended projects. + pcfg = getProjectPathConfig(projectRoot, mode = compiler()); + importGraph(pcfg, hideExternals = hideExternals); +} + +@synopsis{Visualizes an import/extend graph for all the modules in the srcs roots of the current PathConfig} +void importGraph(PathConfig pcfg, bool hideExternals = true) { + m = getProjectModel(pcfg.srcs); + + // let's start with a simple graph and elaborate on details in later versions + g + = { + | <- sort(m.imports), hideExternals ==> to notin m.external + } + + { + | <- sort(m.extends), hideExternals ==> to notin m.external + } + + {<"_", to>| to <- top(m.imports + m.extends)}// pull up the top modules + ; + + list[str] nodeClass(str n) + = [*["external" | n in m.external], *["project" | n notin m.external]]; + + gClosed = g+; + + list[str] edgeClass(str from, str to) + = [ + *["extend" | in m.extends], + *["import" | in m.imports], + *["transitive" + | in g o gClosed, notin gClosed, notin gClosed], + *["cyclic" | in gClosed] + ]; + + styles + = [ + cytoStyleOf( + selector = \edge(equal("source", "_")), + style = defaultEdgeStyle()[visibility = "hidden"] + ), + cytoStyleOf( + selector = \node(id("_")), style = defaultNodeStyle()[visibility = "hidden"] + ), + cytoStyleOf( + selector = \edge(className("extend")), + style = defaultEdgeStyle()[\line-style = "dashed"] + ), + cytoStyleOf( + selector = \edge(className("transitive")), + style = defaultEdgeStyle()[opacity = ".25"][\line-opacity = "0.25"] + ), + cytoStyleOf( + selector = \edge(className("cyclic")), + style = defaultEdgeStyle()[opacity = "1"][\line-opacity = "1"][\width = 10] + ) + ]; + + loc modLinker(str name) { + if (loc x <- m.files[name]) + return x; + else + return |nothing:///|; + } + + default loc modLinker(value _) = |nothing:///|; + + cfg + = cytoGraphConfig( + \layout = defaultDagreLayout()[ranker = \network-simplex()], + styles = styles, + title = "Rascal Import/Extend Graph", + nodeClassifier = nodeClass, + edgeClassifier = edgeClass, + nodeLinker = modLinker, + edgeStyle = defaultEdgeStyle()[\curve-style = taxi()] + ); + + showInteractiveContent(graph(g, cfg = cfg), title = cfg.title); +} + +@synopsis{Container for everything we need to know about the modules in a project to visualize it.} +data ProjectModel + = projectModel( + set[str] modules = {}, + set[str] external = {}, + rel[str, str] imports = {}, + rel[str, str] extends = {}, + rel[str, loc] files = {}); + +@synopsis{Collects name, imports and extends for all modules reachable from the `srcs` root folders.} +ProjectModel getProjectModel(list[loc] srcs) { + allFiles = {*find(src, "rsc")| src <- srcs}; + + models = {getProjectModel(f)| f <- allFiles}; + + wholeWorld + = projectModel( + modules = {*m.modules| m <- models}, + imports = {*m.imports| m <- models}, + extends = {*m.extends| m <- models}, + files = {*m.files| m <- models} + ); + + wholeWorld.external + = wholeWorld.imports<1> + wholeWorld.extends<1> - wholeWorld.modules; + + return wholeWorld; +} + +@synopsis{Collects name, imports and extends for a single Rascal module} +ProjectModel getProjectModel(loc file) { + try { + Module m = parseModule(file); + + = getModuleMetaInf(m); + + return + projectModel( + modules = {name}, + imports = {name} * imps, + extends = {name} * exts, + files = {} + ); + } + catch ParseError(_): + return projectModel(); +} +### |project://rascal/src/org/rascalmpl/library/lang/rsf/IO.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +module lang::rsf::IO + +@synopsis{Read an RSF file. + +Read relations from an RSF file. An RSF file contains tuples of binary relations +in the following format: + RelationName Arg1 Arg2 +where each field is separated by a tabulation character (\t). One file may contain tuples for more than one relation. readRSF takes an RSF file nameRSFFile and generates a map[str,rel[str,str]] that maps each relation name to the actual relation.} +@javaClass{org.rascalmpl.library.lang.rsf.RSFIO} +public java map[str, rel[str, str]] readRSF(loc nameRSFFile); + +@javaClass{org.rascalmpl.library.lang.rsf.RSFIO} +public java map[str, type[value]] getRSFTypes(loc location); + +@javaClass{org.rascalmpl.library.lang.rsf.RSFIO} +public java &T readRSFRelation(type[&T] result, str name, loc location); + +@resource{ +rsf +} +@synopsis{The RSF schema should be given as: + rsf+rascal-file-uri + where rascal-file-uri is a standard Rascal URI, for instance: + rsf+file:///tmp/myRSFFile.rsf + or + rsf+project://MyProject/src/data/myRSFFile.rsf} +public str generate(str moduleName, loc uri) { + // Retrieve the relation names and their types + map[str, type[value]] rsfRels = getRSFTypes(uri); + + return + "module + 'import lang::rsf::IO; + '<for (rname <- rsfRels) {> + 'public () { + ' return readRSFRelation(#, \"\", ); + '} + '<}> + '"; +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/filters/CountPreferAvoid.rsc| +module lang::sdf2::filters::CountPreferAvoid + +import ParseTree; +import Set; +import List; + +@synopsis{Import his module if you want prefer/avoid filtering with counting enabled for your grammar. Use @prefer and @avoid to +label alternatives.} +&T <: Tree countPreferAvoidFilter(amb(set[&T <: Tree] alternatives)) { + alts = [*alternatives]; + counts + = [( 0 | it + 1 | /appl(prod(_, _, {\tag("prefer"()), *_}), _) := alt ) + | Tree alt <- alts]; + + new = [alts[i] | int i <- index(alts), counts[i] == max(counts)]; + + counts + = [( 0 | it + 1 | /appl(prod(_, _, {\tag("avoid"()), *_}), _) := alt ) | Tree alt <- new]; + + result = {new[i]| int i <- index(new), counts[i] == min(counts)}; + + if (result == alternatives) { + fail countPreferAvoidFilter; + } + else { + return amb(result); + } +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/filters/DetectCycles.rsc| +module lang::sdf2::filters::DetectCycles + +import ParseTree; + +&T <: Tree cycleDetectionFilter(amb(set[&T <: Tree] alts)) { + if (/t: cycle(_, _) <- alts) { + throw "Cycle detected at "; + } + else { + fail cycleDetectionFilter; + } +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/filters/DirectThenCountPreferAvoid.rsc| +module lang::sdf2::filters::DirectThenCountPreferAvoid + +import ParseTree; +import Set; +import List; + +@synopsis{Import his module if you want prefer/avoid filtering with counting enabled for your grammar. Use @prefer and @avoid to +label alternatives.} +&T <: Tree directThenCountPreferAvoidFilter( + amb(set[&T <: Tree] alternatives) +) { + if (size(alternatives) == 1) { + fail directThenCountPreferAvoidFilter; + } + // first check for direct prefers / avoids + direct = {t| t: appl(prod(_, _, {\tag("prefer"()), *_}), _) <- alternatives}; + if ({oneTree} := direct) { + return oneTree; + } + if (size(direct) != 0) { + // some were filtered + alternatives = direct; + } + avoids = {t| t: appl(prod(_, _, {\tag("avoid"()), *_}), _) <- alternatives}; + + if (size(alternatives) == (size(avoids) + 1)) { + return ParseTree::amb(alternatives - avoids); + } + alternatives -= avoids; + + // now filter the nested prefers + alts = [*alternatives]; + indexes = index(alts); + tags = getTags(alts); + counts = [( 0 | it + 1 | "prefer"() <- tags[i] ) | i <- indexes]; + + indexes = [i | i <- indexes, counts[i] == max(counts)]; + + counts = [( 0 | it + 1 | "avoid"() <- tags[i] ) | i <- indexes]; + + result = {alts[indexes[i]]| i <- index(indexes), counts[i] == min(counts)}; + + if (result == alternatives) { + fail directThenCountPreferAvoidFilter; + } + else { + return amb(result); + } +} + +list[list[value]] getTags(list[Tree] ts) = [getTags(t) | t <- ts]; + +list[value] getTags(Tree t) { + list[value] result = []; + todo = [t]; + while(todo != []) { + todoCopy = todo; + todo = []; + for (appl(prod(_, _, tags), args) <- todoCopy) { + result += [tg | \tag(tg) <- tags]; + todo += args; + } + } + return result; +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/filters/FilterCycles.rsc| +module lang::sdf2::filters::FilterCycles + +import ParseTree; + +&T <: Tree cycleFilter(amb(set[&T <: Tree] alts)) { + new = {a| a <- alts, /cycle(_, _) !:= a}; + + if (new == alts) { + fail cycleFilter; + } + else { + return amb(new); + } +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/filters/GeneralInjectionCount.rsc| +module lang::sdf2::filters::GeneralInjectionCount + +import ParseTree; +import List; + +private default bool injection(Tree _) = false; +private +bool injection(appl(prod(Symbol _, [Symbol _], set[Attr] _), [Tree _])) + = true; + +&T <: Tree generalInjectionCountFilter(amb(set[&T <: Tree] alts)) { + as = [*alts]; + counts = [( 0 | it + 1 | /Tree t := a, injection(t) ) | a <- as]; + minimum = min(counts); + new = {as[i]| i <- index(as), counts[i] == minimum}; + + if (new == alts) { + fail generalInjectionCountFilter; + } + else { + return amb(new); + } +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/filters/IndirectPreferAvoid.rsc| +module lang::sdf2::filters::IndirectPreferAvoid + +import ParseTree; +import Set; + +private default bool isPreferred(Tree _) = false; +private bool isPreferred(appl(prod(_, _, {\tag("prefer"()), *_}), _)) = true; +private +bool isPreferred(appl(prod(Symbol _, [Symbol _], set[Attr] _), [Tree arg])) + = isPreferred(arg); + +private default bool isAvoided(Tree _) = false; +private bool isAvoided(appl(prod(_, _, {\tag("avoid"()), *_}), _)) = true; +private +bool isAvoided(appl(prod(Symbol _, [Symbol _], set[Attr] _), [Tree arg])) + = isAvoided(arg); + +@synopsis{Import his module if you want prefer/avoid filtering enabled for your grammar. Use @prefer and @avoid to +label alternatives.} +&T <: Tree indirectPreferAvoidFilter(amb(set[&T <: Tree] alternatives)) { + prefers = {t| t <- alternatives, isPreferred(t)}; + + if (prefers != {}, size(alternatives) != size(prefers)) { + return amb(prefers); + } + avoids = {t| t <- alternatives, isAvoided(t)}; + + if (avoids != {}, size(alternatives) != size(avoids)) { + return amb(alternatives - avoids); + } + fail; +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/filters/InjectionCount.rsc| +module lang::sdf2::filters::InjectionCount + +import ParseTree; + +private default int count(Tree _) = 0; +private +int count(appl(prod(Symbol _, [Symbol _], set[Attr] _), [Tree arg])) + = 1 + count(arg); + +&T <: Tree injectionCountFilter(amb(set[&T <: Tree] alts)) { + as = [*alts]; + counts = [count(a) | a <- as]; + new = {as[i]| i <- index(as), counts[i] == min(counts)}; + + if (new == alts) { + fail injectionCountFilter; + } + else { + return amb(new); + } +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/filters/PreferAvoid.rsc| +module lang::sdf2::filters::PreferAvoid + +import ParseTree; +import Set; + +@synopsis{Import his module if you want prefer/avoid filtering enabled for your grammar. Use @prefer and @avoid to +label alternatives.} +&T <: Tree preferAvoidFilter(amb(set[&T <: Tree] alternatives)) { + prefers = {t| t: appl(prod(_, _, {\tag("prefer"()), *_}), _) <- alternatives}; + + if (prefers != {}, size(alternatives) != size(prefers)) { + return amb(prefers); + } + avoids = {t| t: appl(prod(_, _, {\tag("avoid"()), *_}), _) <- alternatives}; + + if (avoids != {}, size(alternatives) != size(avoids)) { + return amb(alternatives - avoids); + } + fail; +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/filters/Reject.rsc| +module lang::sdf2::filters::Reject + +import ParseTree; + +@synopsis{Import his module if you want SDF2 style reject filtering enabled for your grammar. Use @reject to +label one alternative and the whole non-terminal will be filtered if it matches.} +&T <: Tree rejectFilter(amb(set[&T <: Tree] alts)) { + if (appl(prod(_, _, {*_, \tag("reject"())}), _) <- alts) + filter; + fail; +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/syntax/Sdf2.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl - CWI} +module lang::sdf2::\syntax::Sdf2 + +lexical Sort + = OneChar: [A-Z] !>> [A-Za-z0-9] + | MoreChars: ([A-Z] [A-Za-z0-9\-]* [A-Za-z0-9] !>> [A-Za-z0-9]) \ "LAYOUT" + ; + +syntax Syms = Sym*; + +lexical NatCon = Digits: [0-9]+ !>> [0-9]; + +lexical NumChar = Digits: [\\] [0-9]+ !>> [0-9]; + +start syntax SDF = Def: "definition" Definition def; + +syntax Character + = Numeric: NumChar + | short: ShortChar + | top: "\\TOP" + | eof: "\\EOF" + | bot: "\\BOT" + | label_start: "\\LABEL_START" + ; + +lexical ShortChar + = Regular: [a-zA-Z0-9] + | Escaped: [\\] ![A-Za-mo-qsu-z0-9] + // -\0-\31 + ; + +syntax Renaming + = Sym: Sym "=\>" Sym + | production: Prod "=\>" Prod + ; + +syntax Renamings = Renamings: "[" Renaming* "]"; + +lexical IdCon = Default: [A-Za-z] [A-Za-z\-0-9]* !>> [A-Za-z\-0-9]; + +syntax Class + = SimpleCharClass: "[" Range* "]" + | \bracket: "(" Class ")" + | Comp: "~" Class + > Diff: Class "/" Class + > left ISect: Class "/\\" Class + > left Union: Class "\\/" Class + ; + +syntax Range + = Character + | Range: Character "-" Character + ; + +syntax Attribute + = Id: "id" "(" ModuleName ")" + | Term: ATermAttribute + | Reject: "reject" + | Prefer: "prefer" + | Avoid: "avoid" + | Bracket: "bracket" + | Assoc: Assoc + ; + +syntax ATermAttribute = Default: ATerm a \ "reject" \ "prefer" \ "avoid" \ "bracket"; + +syntax Attrs + = Attrs: "{" {Attribute ","}* "}" + | NoAttrs: + ; + +syntax Prod = Prod: Syms "-\>" Sym Attrs; + +syntax Prods = Prod*; + +syntax Grammar + = Aliases: "aliases" Aliases + | Sorts: "sorts" Syms + | ImpSection: ImpSection + | Syntax: "syntax" Prods + | KernalStartSyms: "start-symbols" Syms + | Variables: "variables" Prods + | Priorities: "priorities" Priorities + | Restrictions: "restrictions" Restrictions + | LexicalSyntax: "lexical" "syntax" Prods + | LexicalStartSyms: "lexical" "start-symbols" Syms + | LexicalVariables: "lexical" "variables" Prods + | LexicalPriorities: "lexical" "priorities" Priorities + | LexicalRestrictions: "lexical" "restrictions" Restrictions + | ContextFreeSyntax: "context-free" "syntax" Prods + | ContextFreeStartSyms: "context-free" "start-symbols" Syms + | ContextFreePriorities: "context-free" "priorities" Priorities + | ContextFreeRestrictions: "context-free" "restrictions" Restrictions + ; + +syntax Label + = Quoted: StrCon + | IdCon: IdCon \ "left" \ "right" \ "assoc" \ "non-assoc" + ; + +syntax Sym + = Sort: Sort + | Lit: StrCon + | CILit: SingleQuotedStrCon + | Class: Class + | Layout: "LAYOUT" + | Empty: "(" ")" + | Bracket: "(" Sym ")" + | Seq: "(" Sym Sym+ ")" + | Opt: Sym "?" + | Iter: Sym "+" + | IterStar: Sym "*" + | IterSep: "{" Sym Sym "}" "+" + | IterStarSep: "{" Sym Sym "}" "*" + | CF: "\<" Sym "-CF" "\>" + | Lex: "\<" Sym "-LEX" "\>" + | Varsym: "\<" Sym "-VAR" "\>" + | ParameterizedSort: Sort "[[" {Sym ","}+ "]]" + > right Alt: Sym "|" Sym + > Label: Label ":" Sym + ; + +layout LAYOUTLIST = LAYOUT* !>> [\ \t\n\r%]; + +lexical LAYOUT + = Whitespace: [\ \t\n\r] + | @category="comment" Line: "%%" ![\n]* [\n] + | @category="comment" Nested: "%" ![%\n] "%" + ; + +syntax Alias = Alias: Sym "-\>" Sym; + +syntax Aliases = Alias*; + +lexical StrChar + = NewLine: [\\] [n] + | Tab: [\\] [t] + | Quote: [\\] [\"] + | Backslash: [\\] [\\] + | Decimal: [\\] [0-9] [0-9] [0-9] + | Normal: ![\n\t\"\\] + ; + +lexical StrCon = Default: [\"] StrChar* [\"]; + +syntax FunctionName + = UnquotedFun: IdCon + | QuotedFun: StrCon + ; + +lexical SingleQuotedStrCon = Default: [\'] SingleQuotedStrChar* [\']; + +lexical SingleQuotedStrChar + = NewLine: [\\] [n] + | Tab: [\\] [t] + | Quote: [\\] [\'] + | Backslash: [\\] [\\] + | Decimal: [\\] [0-9] [0-9] [0-9] + | Normal: ![\n\t\'\\] + ; + +syntax RealCon = RealCon: IntCon "." NatCon OptExp; + +syntax OptExp + = Present: "e" IntCon + | Absent: + ; + +start syntax Module = Module: "module" ModuleName ImpSection* Sections; + +syntax ModuleName + = Unparameterized: ModuleId id \ "aliases" \ "lexical" \ "priorities" \ "context-free" \ "definition" \ "syntax" \ "variables" \ "module" \ "imports" \ "exports" \ "hiddens" \ "left" \ "right" \ "assoc" \ "non-assoc" \ "bracket" \ "sorts" \ "restrictions" !>> [A-Za-z0-9_\-] + | Parameterized: ModuleId id "[" Syms actuals "]" + ; + +lexical ModuleWord = Word: [A-Za-z0-9_\-]+ !>> [A-Za-z0-9_\-]; + +lexical ModuleId + = Leaf: ModuleWord !>> [/] + | Root: "/" ModuleId + | Path: ModuleWord "/" ModuleId + ; + +syntax Import + = Module: ModuleName + | RenamedModule: ModuleName Renamings + | Bracket: "(" Import ")" + ; + +syntax Imports = Import*; + +syntax Section + = Exports: "exports" Grammar* + | Hiddens: "hiddens" Grammar* + ; + +syntax Sections = Section*; + +syntax ImpSection = Imports: "imports" Imports; + +syntax Definition = Module* modules; + +syntax Lookahead + = Class: Class ! bracket class + | Seq: Class class "." Lookaheads ! alt las + ; + +syntax Lookaheads + = Single: Lookahead + | right alt: Lookaheads "|" Lookaheads + | Bracket: "(" Lookaheads ")" + | List: "[[" {Lookahead ","}* "]]" + ; + +syntax Restriction = Follow: Syms "-/-" Lookaheads; + +syntax Assoc + = Left: "left" + | Right: "right" + | NonAssoc: "non-assoc" + | Assoc: "assoc" + ; + +syntax Restrictions = Default: Restriction*; + +syntax ArgumentIndicator = Default: "\<" {NatCon ","}+ "\>"; + +syntax Group + = non-assoc WithArguments: Group ArgumentIndicator + | non-assoc NonTransitive: Group "." + | ProdsGroup: "{" Prods "}" + | AssocGroup: "{" Assoc ":" Prods "}" + | SimpleGroup: Prod + ; + +syntax Priority + = Chain: {Group "\>"}+ + | Assoc: Group Assoc Group + ; + +syntax Priorities = {Priority ","}*; + +syntax AFun + = Quoted: StrCon + | Unquoted: IdCon \ "left" \ "right" \ "assoc" \ "non-assoc" + ; + +syntax ATerm + = Int: IntCon + | Real: RealCon + | Fun: AFun + | Appl: AFun "(" {ATerm ","}+ ")" + | Placeholder: "\<" ATerm "\>" + | List: "[" {ATerm ","}* "]" + | Annotated: ATerm Annotation + ; + +syntax Annotation = Default: "{" {ATerm ","}+ "}"; + +syntax IntCon + = Natural: NatCon + | Positive: "+" NatCon + | Negative: "-" NatCon + ; +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/util/Importer.rsc| +module lang::sdf2::util::Importer + +import lang::sdf2::util::Load; +import lang::sdf2::util::SDF2Grammar; +import lang::rascal::format::Grammar; +import lang::rascal::grammar::definition::Modules; +import util::Reflective; + +@resource{ +sdf +} +@synopsis{Converts an SDF2 module to a Rascal module} +@description{ +The `sdf` uri scheme works like this: +`sdf:///` + +The default Rascal search path is used to resolve the +module name to a file with the `.sdf2` extension. + +The module name is expected to be in SDF2 syntax. + +If modules are "imported" by the given top module, +then these names are resolved recursively and a +"definition" composed of all relative modules is +collected before the translation process starts. +All of the syntax rules in all of the SDF2 modules +end up in one Rascal module. +} +public str generate(str name, loc at, PathConfig pcfg) { + def = loadSDF2Module(at.path, pcfg); + gr = injectStarts(fuse(dup(sdf2grammar(at.path[1..], def)))); + return + "module + ' + ' + ' + 'extend lang::sdf2::filters::PreferAvoid; + 'extend lang::sdf2::filters::IndirectPreferAvoid; + 'extend lang::sdf2::filters::Reject; + "; +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/util/Load.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl} +module lang::sdf2::util::Load + +import lang::sdf2::\syntax::Sdf2; +import ParseTree; +import Set; +import util::Reflective; + +public SDF loadSDF2Module(str name, PathConfig pcfg) { + set[Module] modules = {}; + set[str] newnames = {name}; + set[str] done = {}; + + while(newnames != {}) { + = takeOneFrom(newnames); + + if (n notin done) { + file = getSearchPathLoc(n + ".sdf", pcfg); + \mod = parse(#start[Module], file).top; + modules += \mod; + newnames += getImports(\mod); + done += {n}; + } + } + + def = "definition + ' + '<for (Module m <- modules) {> + '<}>"; + + return parse(#SDF, def); +} + +private set[str] getImports(Module \mod) { + return {""| /Import i := \mod, /ModuleName name := i}; +} +### |project://rascal/src/org/rascalmpl/library/lang/sdf2/util/SDF2Grammar.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI} +@synopsis{Convert SDF2 grammars to an (unnormalized) Rascal internal grammar representation (Grammar).} +module lang::sdf2::util::SDF2Grammar + +// Todo List: +// - Some tests are marked with @ignore (and commented out) since they trigger a Rascal bug: +// . The expression: `(Group) `A -\> B <1>`; triggers a bug in AST construction +// . The test (Class) `[]` == \char-class([]); // gives unsupported operation +import IO; +import String; +import Set; +import List; +import Map; +import ParseTree; +import Grammar; +import lang::rascal::grammar::definition::Characters; +import lang::sdf2::\syntax::Sdf2; + +public +Symbol label(str s, conditional(Symbol t, set[Condition] cs)) + = conditional(label(s, t), cs); +public +Symbol conditional( + conditional(Symbol s, set[Condition] c1), set[Condition] c2 +) + = conditional(s, c1 + c2); + +public GrammarDefinition sdf2grammar(loc input) { + return sdf2grammar("Main", input); +} + +public GrammarDefinition sdf2grammar(str main, loc input) { + return sdf2grammar(main, parse(#SDF, input)); +} + +public GrammarDefinition sdf2grammar(SDF def) { + return sdf2grammar("Main", def); +} + +public +Grammar::Grammar injectStarts(Grammar::Grammar g) + = visit(g) { + case Production p => p[def = \start(p.def)] when p.def in g.starts + }; + +public GrammarDefinition sdf2grammar(str main, SDF def) { + if ((SDF) `definition ` := def) { + ms = (); + for (Module m <- mods) { + gm = getModule(m); + ms[gm.name] = gm; + } + + main = moduleName(main); + + if (main notin ms) + throw "Main module

not found"; + res = definition(main, ms); + res = split(res); + res = addLexicalChaining(res); + + //res = resolve(res); + res + = applyConditions(res, (s: c| c: conditional(s, _) <- getConditions(def))); + res = removeDirectProductionCycle(res); + + return res; + } + throw "Unknown format for SDF2"; +} + +private +GrammarDefinition split(GrammarDefinition def) + = visit(def) { + case Grammar::Grammar g => split(g) + }; + +private +GrammarDefinition removeDirectProductionCycle(GrammarDefinition def) { + def = visit(def) { + case Production p + => p[alternatives = {a| a <- p.alternatives, !isProductionCycle(a, p)}] + when p has def && p has alternatives + case Production p + => p[choices = [c | c <- p.choices, !isProductionCycle(c, p)]] + when p has def && p has choices + } + ; + return visit(def) { + case Grammar::Grammar g => removeEmptyProductions(g) + } + ; +} + +private bool isProductionCycle(\prod(_, [sing], _), Production b) { + if (s := strip(sing) && s has name && bs := strip(b.def) && bs has name) + return s.name == bs.name; + else + return false; +} +private default bool isProductionCycle(Production a, Production b) = false; + +private GrammarDefinition addLexicalChaining(GrammarDefinition def) { + set[Symbol] sSorts + = {s + | /Grammar::Grammar g := def, s <- g.rules, (s is \sort || s is \parameterized-sort) + }; + set[Symbol] lSorts + = {s + | /Grammar::Grammar g := def, s <- g.rules, !(s is \sort || s is \parameterized-sort) + }; + overlap = {s.name| s <- sSorts} & {s.name| s <- lSorts}; + if (overlap != {}) { + // first replace the lexicals l with LEX_l + def = visit(def) { + case Grammar::Grammar g: { + for (s <- g.rules, !(s is \sort || s is \parameterized-sort), s.name in overlap, p := g.rules[s]) { + newSymbol = \lex("LEX_"); + g.rules[newSymbol] = visit(p) { + case \priority(_, l) => \priority(newSymbol, l) + case \associativity(_, a, l) => \associativity(newSymbol, a, l) + case \cons(_, ss, a) => \cons(newSymbol, ss, a) + case \func(_, ss, kws) => \func(newSymbol, ss, kws) + case \choice(_, ps) => \choice(newSymbol, ps) + } + ; + g.rules = delete(g.rules, s); + } + insert g; + } + } + ; + + // now add the chain rules to one of the grammars + chains + = grammar( + {}, (\sort(n): \prod(\sort(n), [\lex("LEX_")], {})| n <- overlap) + ); + def = top-down-break visit(def) { + case Grammar::Grammar g => compose(g, chains) + } + ; + } + return def; +} + +Symbol striprec(Symbol s) = visit(s) { + case Symbol t => strip(t) + }; +Symbol strip(label(str _, Symbol s)) = strip(s); +Symbol strip(conditional(Symbol s, set[Condition] _)) = strip(s); +default Symbol strip(Symbol s) = s; + +private Grammar::Grammar split(Grammar::Grammar g) { + for (nt <- g.rules, cur := g.rules[nt], sorts := {strip(s)| /prod(s, _, _) := cur}, size(sorts) > 1) { + for (Symbol s <- sorts) { + newp = keep(cur, s); + if (g.rules[s]? && s != strip(cur.def)) + g.rules[s].alternatives += newp.alternatives; + else + g.rules[s] = newp; + } + } + return removeEmptyProductions(g); +} + +data Production = temp(); + +private bool isNotEmpty(Production p) { + if (p has alternatives) { + return p.alternatives != {}; + } + if (p has choices) { + return p.choices != []; + } + return true; +} +private Grammar::Grammar removeEmptyProductions(Grammar::Grammar g) { + g = visit(g) { + case list[Production] l => [p | p <- l, isNotEmpty(p)] + case set[Production] l => {p| p <- l, isNotEmpty(p)} + } + ; + return g[rules = (s: p| s <- g.rules, p := g.rules[s], isNotEmpty(p))]; +} + +private +Production keep(Production source, Symbol s) + = visit(source) { + case \priority(_, l) => \priority(s, l) + case \associativity(_, a, l) => \associativity(s, a, l) + case \cons(_, ss, a) => \cons(s, ss, a) + case \func(_, ss, kws) => \func(s, ss, kws) + case \choice(_, ps) => \choice(s, ps) + case list[Production] ps => [p | p <- ps, strip(p.def) == s] when size(ps) > 0 + // bug #208 + case set[Production] ps => {p| p <- ps, strip(p.def) == s} when size(ps) > 0 + }; + +private GrammarModule getModule(Module m) { + if (/(Module) `module ` := m) { + name = moduleName(""); + println("processing "); + prods = getProductions(m); + imps = getImports(m); + + // note that imports in SDF2 have the semantics of extends in Rascal + return + \module( + name, + {}, + imps, + illegalPriorities(dup(grammar(getStartSymbols(m), prods))) + ); + //return \module(name, {}, imps, grammar({}, prods)); + } + throw "can not find module name in "; +} + +public +str moduleName(/\//) + = moduleName("
::");
+public
+str moduleName(/languages::/)
+    = moduleName("lang::");
+public default str moduleName(str i) = i;
+
+private set[str] getImports(Module m) {
+    return {moduleName("")| /Import i := m, /ModuleName name := i};
+}
+
+public
+GrammarDefinition applyConditions(
+    GrammarDefinition d, map[Symbol from, Symbol to] conds
+) {
+    Symbol app(Symbol s) {
+        if (s is label)
+            return label(s.name, app(s.symbol));
+        else if (s in conds)
+            return conds[s];
+        else
+            return s;
+    }
+    
+    return
+        visit(d) {
+            case prod(Symbol e, list[Symbol] ss, set[Attr] as)
+                => prod(e, [app(s) | s <- ss], as)
+        }
+}
+
+public Grammar::Grammar illegalPriorities(Grammar::Grammar g) {
+    extracted = {};
+    g = innermost visit(g) {
+            case \priority(Symbol def, list[Production] ps):
+                if ([*pre, p: prod(Symbol other, _, _), *post] := ps, !sameType(def, other)) {
+                    println(
+                        "WARNING: extracting production from non-recursive priority chain"
+                    );
+                    extracted
+                        += p[attributes = p.attributes + \tag("NotSupported"("priority with 
 "))];
+                    insert priority(def, pre + post);
+                }
+                else
+                    fail;
+            case \associativity(Symbol def, Associativity a, set[Production] q):
+                if ({*_, p: prod(Symbol other, _, _)} := q, !sameType(def, other)) {
+                    println(
+                        "WARNING: extracting production from non-recursive associativity group"
+                    );
+                    extracted
+                        += p[attributes = p.attributes + \tag("NotSupported"(" associativity with "))];
+                    insert associativity(def, a, q);
+                }
+                else
+                    fail;
+        }
+    
+    return compose(g, grammar({}, extracted));
+}
+
+public &T dup(&T g) {
+    prods = {p| /Production p: prod(_, _, _) := g};
+    
+    // first we fuse the attributes (SDF2 semantics) and the cons names
+    solve(prods) {
+        if ({prod(l, r, a1), prod(l, r, a2), *rest} := prods) {
+            prods = {prod(l, r, a1 + a2), *rest};
+        }
+        if ({prod(label(n, l), r, a1), prod(l, r, a2), *rest} := prods) {
+            prods = {prod(label(n, l), r, a1 + a2), *rest};
+        }
+        if ({prod(label(n, l), r, a1), prod(label(_, l), r, a2), *rest} := prods) {
+            prods = {prod(label(n, l), r, a1 + a2), *rest};
+        }
+    }
+    
+    // now we replace all uses of prods by their fused counterparts
+    g = visit(g) {
+            case prod(l, r, _):
+                if ({p: prod(l, r, _), *_} := prods)
+                    insert p;
+                else if ({p: prod(label(_, l), r, _), *_} := prods)
+                    insert p;
+                else
+                    fail;
+            case prod(label(_, l), r, _):
+                if ({p: prod(label(_, l), r, _), *_} := prods)
+                    insert p;
+                else
+                    fail;
+        }
+    
+    return g;
+}
+
+test bool test1()
+    = sdf2grammar(
+          (SDF) `definition  module X exports context-free syntax    "abc" -\> ABC`
+      ).modules["X"].grammar.rules[sort("ABC")] == choice(sort("ABC"), {prod(sort("ABC"), [lit("abc")], {})});
+
+// \char-class([range(97,122),range(48,57)])
+test bool test2()
+    = rs :=
+      sdf2grammar(
+          (SDF) `definition
+              'module PICOID
+          'exports
+          'lexical syntax
+          '   [a-z] [a-z0-9]* -\> PICO-ID  
+          'lexical restrictions
+          '  PICO-ID -/- [a-z0-9]`
+      ).modules["PICOID"].grammar.rules
+    && prod(
+           lex("PICO-ID"),
+           [
+               \char-class([range(97, 122)]),
+               \conditional(
+                   \iter-star(\char-class([range(97, 122), range(48, 57)])),
+                   {\not-follow(\char-class([range(97, 122), range(48, 57)]))}
+               )
+           ],
+           {}
+       ) == rs[lex("PICO-ID")];
+
+test bool test3()
+    = rs :=
+      sdf2grammar(
+          (SDF) `definition
+              'module StrChar
+          'exports
+          ' lexical syntax
+          '   ~[\\0-\\31\\n\\t\\"\\\\]          -\> StrChar {cons("normal")}`
+      ).modules["StrChar"].grammar.rules
+    && prod(
+           label("normal", sort("StrChar")),
+           [\char-class([range(26, 33), range(35, 91), range(93, 65535)])],
+           {}
+       ) == rs[sort("StrChar")];
+
+public set[Production] getProductions(Module \mod) {
+    res = {};
+    visit(\mod) {
+        case (Grammar) `syntax `:
+            res += getProductions(prods, true);
+        case (Grammar) `lexical syntax `:
+            res += getProductions(prods, true);
+        case (Grammar) `context-free syntax `:
+            res += getProductions(prods, false);
+        case (Grammar) `priorities <{Priority ","}* prios>`:
+            res += getPriorities(prios, true);
+        case (Grammar) `lexical priorities <{Priority ","}* prios>`:
+            res += getPriorities(prios, true);
+        case (Grammar) `context-free priorities <{Priority ","}* prios>`:
+            res += getPriorities(prios, false);
+    }
+    ;
+    
+    return res;
+}
+
+set[Production] getProductions(SDF sd)
+    = {*getProductions(m)| m <- sd.def.modules};
+
+test bool test4()
+    = getProductions((SDF) `definition module A exports syntax A -\> B`) == {prod(sort("B"), [sort("A")], {})};
+
+test bool test5()
+    = getProductions(
+          (SDF) `definition module A exports lexical syntax A -\> B`
+      ) == {prod(lex("B"), [sort("A")], {})};
+
+test bool test6()
+    = getProductions(
+          (SDF) `definition module A exports lexical syntax A -\> B B -\> C`
+      ) == {prod(lex("C"), [sort("B")], {}), prod(lex("B"), [sort("A")], {})};
+
+test bool test7()
+    = getProductions(
+          (SDF) `definition module A exports context-free syntax A -\> B`
+      ) == {prod(sort("B"), [sort("A")], {})};
+
+test bool test9()
+    = getProductions(
+          (SDF) `definition module A exports priorities A -\> B \> C -\> D`
+      ) == {prod(sort("B"), [sort("A")], {}), prod(sort("D"), [sort("C")], {})};
+
+test bool test9_2()
+    = getProductions(
+          (SDF) `definition module A exports priorities B "*" B -\> B \> B "+" B -\> B`
+      ) == {priority (
+                sort("B"),
+                [
+                    prod(sort("B"), [sort("B"), lit("*"), sort("B")], {}),
+                    prod(sort("B"), [sort("B"), lit("+"), sort("B")], {})
+                ]
+            )};
+
+public set[Production] getProductions(Prod* prods, bool isLex) {
+    return {*fixParameters(getProduction(prod, isLex))| Prod prod <- prods};
+}
+
+set[Production] fixParameters(set[Production] input) {
+    return
+        innermost visit(input) {
+            case prod(\parameterized-sort(str name, [*pre, sort(str x), *post]), lhs, as)
+                => prod(
+                       \parameterized-sort (
+                           name,
+                           [*pre, \parameter(x, adt (
+                                                    "Tree",
+                                                    []
+                                                )), *post]
+                       ),
+                       visit(lhs) {
+                           case sort(x) => \parameter(x, adt (
+                                                             "Tree",
+                                                             []
+                                                         ))
+                       },
+                       as
+                   )
+        }
+}
+
+private str labelName("") = "";
+private
+default str labelName(str s)
+    = toLowerCase(s[0]) + (size(s) > 1 ? s[1..] : "");
+
+public set[Production] getProduction(Prod P, bool isLex) {
+    switch(P) {
+        case (Prod) ` -\> LAYOUT `:
+            return
+                {prod(layouts("LAYOUTLIST"), [\iter-star(\lex("LAYOUT"))], {}),
+                 prod(\lex("LAYOUT"), getSymbols(syms, isLex), getAttributes(ats))};
+        case (Prod) ` -\>  {<{Attribute ","}* _>, reject, <{Attribute ","}* _> }`:
+            return
+                {prod(
+                     keywords(getSymbol(sym, isLex).name + "Keywords"),
+                     getSymbols(syms, isLex),
+                     {}
+                 )};
+        case (Prod) ` -\>  {<{Attribute ","}* x>, cons(), <{Attribute ","}* y> }`:
+            return
+                {prod(
+                     label(labelName(unescape(n)), getSymbol(sym, isLex)),
+                     getSymbols(syms, isLex),
+                     getAttributes(
+                         (Attrs) `{<{Attribute ","}* x>, <{Attribute ","}* y> }`
+                     )
+                 )};
+        case (Prod) ` -\>  `:
+            return
+                {prod(getSymbol(sym, isLex), getSymbols(syms, isLex), getAttributes(ats))};
+        default: {
+            println("WARNING: not importing 

"); + return {prod(sort("IGNORED"), [], {\tag("NotSupported"("

"))})}; + } + } +} + +test bool test10() + = getProduction((Prod) `PICO-ID ":" TYPE -\> ID-TYPE`, false) == {prod(sort("ID-TYPE"), [sort("PICO-ID"), lit(":"), sort("TYPE")], {})}; + +test bool test11() + = getProduction((Prod) `PICO-ID ":" TYPE -\> ID-TYPE`, true) == {prod(lex("ID-TYPE"), [sort("PICO-ID"), lit(":"), sort("TYPE")], {})}; + +test bool test12() + = getProduction( + (Prod) `PICO-ID ":" TYPE -\> ID-TYPE {cons("decl"), left}`, false + ) == {prod( + sort("ID-TYPE"), + [sort("PICO-ID"), lit(":"), sort("TYPE")], + {\assoc(left())} + )}; + +test bool test13() + = getProduction( + (Prod) `[\\ \\t\\n\\r] -\> LAYOUT {cons("whitespace")}`, true + ) == {prod( + \lex("LAYOUT"), + [\char-class([range(32, 32), range(9, 9), range(10, 10), range(13, 13)])], + {} + )}; + +test bool test14() + = getProduction((Prod) `{~[\\n]* [\\n]}* -\> Rest`, true) == {prod( + sort("Rest"), + [ + \iter-star-seps ( + \iter-star(\char-class([range(0, 9), range(11, 65535)])), + [\char-class([range(10, 10)])] + ) + ], + {} + )}; + +public set[Symbol] getConditions(SDF m) { + res = {}; + visit(m) { + case (Grammar) `restrictions `: + res += getRestrictions(rests, true); + case (Grammar) `lexical restrictions `: + res += getRestrictions(rests, true); + case (Grammar) `context-free restrictions `: + res += getRestrictions(rests, false); + case (Prod) ` -\> {<{Attribute ","}* _>, reject, <{Attribute ","}* _> }`: + res + += {conditional( + getSymbol(sym, false), + {\delete(keywords(getSymbol(sym, false).name + "Keywords"))} + ), + conditional( + getSymbol(sym, true), + {\delete(keywords(getSymbol(sym, true).name + "Keywords"))} + )}; + } + + while({conditional(s, cs1), conditional(s, cs2), *rest} := res) + res = rest + {conditional(s, cs1 + cs2)}; + + //iprintln(res); + return res; +} + +public set[Symbol] getRestrictions(Restriction* restrictions, bool isLex) { + //println("looping over < restrictions>"); + res = {*getRestriction(r, isLex)| Restriction r <- restrictions}; + + //println("collected: "); + return res; +} + +public set[Symbol] getRestriction(Restriction restriction, bool isLex) { + println("getting rest: "); + switch(restriction) { + case (Restriction) `-/- `: + return {}; + + case (Restriction) `LAYOUT? -/- `: + return + {conditional( + \iter-star(\lex("LAYOUT")), {\not-follow(l)| l <- getLookaheads(ls)} + )}; + + case (Restriction) ` -/- `: + return + {conditional( + getSymbol(s1, isLex), {\not-follow(l)| l <- getLookaheads(ls)} + )}; + + case (Restriction) ` -/- `: + return + getRestriction((Restriction) ` -/- `, isLex) + + {*getRestriction((Restriction) ` -/- `, isLex) + | Sym s <- rest + }; + + default: { + println("WARNING: ignored "); + return {}; + } + } +} + +test bool test18() = getRestriction((Restriction) `-/- [a-z]`, true) == {}; + +test bool test19() + = getRestriction((Restriction) `ID -/- [a-z]`, true) == {conditional(sort("ID"), {\not-follow(\char-class([range(97, 122)]))})}; + +// ----- getLookaheads, getLookahead ----- +public set[Symbol] getLookaheads(Lookaheads ls) { + switch(ls) { + case (Lookaheads) ``: + return {getCharClass(c)}; + + case (Lookaheads) ` . `: { + rs = getLookaheads(r); + if (size(rs) == 1) + return {\seq([getCharClass(l), *rs])}; + else + return {\seq([getCharClass(l), \alt(rs)])}; + } + + case (Lookaheads) ` | `: + return getLookaheads(l) + getLookaheads(r); + + case (Lookaheads) `()`: + return getLookaheads(l); + + default: { + println("Warning: ignored "); + return {}; + } + } +} + +test bool test21() + = getLookaheads((Lookaheads) `[a-z]`) == {\char-class([range(97, 122)])}; + +test bool test22() = getLookaheads((Lookaheads) `[a-z] . [0-9]`) == {}; + +test bool test23() + = getLookaheads((Lookaheads) `[a-z] | [\\"]`) == {\char-class([range(97, 122)]), \char-class([range(34, 34)])}; + +// ----- getPriorities, getPriority ----- +public +set[Production] getPriorities({Priority ","}* priorities, bool isLex) { + return {getPriority(p, isLex)| Priority p <- priorities}; +} + +public Production getPriority(Group group, bool isLex) { + switch(group) { + case (Group) ``: + return choice(definedSymbol(p, isLex), getProduction(p, isLex)); + + case (Group) ` .`: + return getPriority(g, isLex); + + // we ignore non-transitivity here! + case (Group) ` `: + return getPriority(g, isLex); + + // we ignore argument indicators here! + case (Group) `{}`: + return + choice(definedSymbol(ps, isLex), {*getProduction(p, isLex)| Prod p <- ps}); + + case (Group) `{ : }`: + return + \associativity( + definedSymbol(ps, isLex), + getAssociativity(a), + {*getProduction(p, isLex)| Prod p <- ps} + ); + + default: + throw "missing case "; + } +} + +test bool test24() + = getPriority((Group) `A -\> B`, false) == prod(sort("B"), [sort("A")], {}); + +test bool test25() + = getPriority((Group) `A -\> B .`, false) == prod(sort("B"), [sort("A")], {}); + +test bool test26() + = getPriority((Group) `A -\> B \<1\>`, false) == prod(sort("B"), [sort("A")], {}); + +test bool test27() + = getPriority((Group) `{A -\> B C -\> D}`, false) == choice( + sort("B"), + {prod(sort("D"), [sort("C")], {}), prod(sort("B"), [sort("A")], {})} + ); + +test bool test28() + = getPriority((Group) `{left: A -\> B}`, false) == \associativity(sort("B"), \left(), {prod(sort("B"), [sort("A")], {})}); + +test bool test29() + = getPriority((Group) `{left: A -\> B B -\> C}`, false) == \associativity( + sort("B"), + \left(), + {prod(sort("C"), [sort("B")], {}), prod(sort("B"), [sort("A")], {})} + ); + +public Production getPriority(Priority p, bool isLex) { + switch(p) { + case (Priority) ``: + return getPriority(g, isLex); + + case (Priority) ` `: + return + \associativity( + definedSymbol(g1, isLex), + getAssociativity(a), + {getPriority((Priority) ``, isLex), + getPriority((Priority) ``, isLex)} + ); + + case (Priority) `<{Group "\>"}+ groups>`: + return + priority( + definedSymbol(groups, isLex), + [getPriority(group, isLex) | Group group <- groups] + ); + } + throw "could not get priority of

"; +} + +test bool test30() + = getPriority((Priority) `A -\> B`, false) == priority ( + sort("B"), + [prod(sort("B"), [sort("A")], {})] + ); + +test bool test31() + = getPriority((Priority) `A -\> B .`, false) == priority ( + sort("B"), + [prod(sort("B"), [sort("A")], {})] + ); + +test bool test32() + = getPriority((Priority) `A -\> B \<1\>`, false) == prod(sort("B"), [sort("A")], {}); + +test bool test33() + = getPriority((Priority) `{A -\> B C -\> D}`, false) == priority ( + sort("B"), + [ + choice( + sort("B"), + {prod(sort("D"), [sort("C")], {}), prod(sort("B"), [sort("A")], {})} + ) + ] + ); + +test bool test34() + = getPriority((Priority) `A -\> B \> C -\> D`, false) == priority ( + sort("B"), + [prod(sort("B"), [sort("A")], {}), prod(sort("D"), [sort("C")], {})] + ); + +test bool test35() + = getPriority((Priority) `A -\> B \> C -\> D \> E -\> F`, false) == priority ( + sort("B"), + [ + prod(sort("B"), [sort("A")], {}), + prod(sort("D"), [sort("C")], {}), + prod(sort("F"), [sort("E")], {}) + ] + ); + +// ----- definedSymbol ----- +public Symbol definedSymbol((&T <: Tree) v, bool isLex) { + // Note that this might not work if there are different right-hand sides in the group... + // I don't know what to do about this yet. + if (/(Prod) ` -\> ` := v) { + return getSymbol(s, isLex); + } + else + if (/(Prod) ` -\> LAYOUT ` := v) { + return \lex("LAYOUT"); + } + throw "could not find a defined symbol in "; +} + +// ----- getStartSymbols ----- +public set[Symbol] getStartSymbols(Module \mod) { + result = {}; + visit(\mod) { + case (Grammar) `context-free start-symbols `: + result += {getSymbol(sym, false)| sym <- syms}; + case (Grammar) `lexical start-symbols `: + result += {getSymbol(sym, true)| sym <- syms}; + case (Grammar) `start-symbols `: + result += {getSymbol(sym, true)| sym <- syms}; + } + return result; +} + +test bool test36() + = getStartSymbols( + (Module) `module M exports context-free start-symbols A B C` + ) == {sort("A"), sort("B"), sort("C")}; + +test bool test37() + = getStartSymbols( + (Module) `module M exports lexical start-symbols A B C` + ) == {sort("A"), sort("B"), sort("C")}; + +test bool test38() + = getStartSymbols((Module) `module M exports start-symbols A B C`) == {sort("A"), sort("B"), sort("C")}; + +public +list[Symbol] getSymbols((Syms) ``, bool isLex) + = [getSymbol(sym, isLex) | sym <- ss]; + +test bool test39() + = getSymbols((Syms) `A B "ab"`, true) == [sort("A"), sort("B"), lit("ab")]; + +public Symbol getSymbol(Sym sym, bool isLex) { + switch(sym) { + case (Sym) `LAYOUT ?`: + return \layouts("LAYOUTLIST"); + case (Sym) ` : `: + return label(labelName(unescape(l)), getSymbol(s, isLex)); + + case (Sym) ` : `: + return label(labelName(""), getSymbol(s, isLex)); + + case (Sym) `LAYOUT`: + return \lex("LAYOUT"); + + case (Sym) ``: + return lit(unescape(l)); + + case (Sym) ``: + return cilit(unescape(l)); + + case (Sym) `[[<{Sym ","}+ syms>]]`: + return \parameterized-sort("", separgs2symbols(syms, isLex)); + + case (Sym) ` ?`: + return opt(getSymbol(s, isLex)); + + case (Sym) ``: + return getCharClass(cc); + + case (Sym) `\< -LEX \>`: + return getSymbol(s, true); + + case (Sym) `\< -CF \>`: + return getSymbol(s, false); + + case (Sym) `\< -VAR \>`: + return getSymbol(s, isLex); + + case (Sym) ` | `: + return alt({getSymbol(lhs, isLex), getSymbol(rhs, isLex)}); + } + + if (isLex) + switch(sym) { + case (Sym) ``: + return lex(""); + + case (Sym) ` *`: + return \iter-star(getSymbol(s, isLex)); + + case (Sym) ` +`: + return \iter(getSymbol(s, isLex)); + + case (Sym) ` *?`: + return \iter-star(getSymbol(s, isLex)); + + case (Sym) ` +?`: + return \iter(getSymbol(s, isLex)); + + case (Sym) `{ } *`: + return \iter-star-seps ( + getSymbol(s, isLex), + [getSymbol(sep, isLex)] + ); + + case (Sym) `{ } +`: + return \iter-seps ( + getSymbol(s, isLex), + [getSymbol(sep, isLex)] + ); + + case (Sym) `?`: + return \opt(getSymbol(s, isLex)); + + case (Sym) `( )`: + return seq([getSymbol(first, isLex)] + [getSymbol(e, isLex) | e <- rest]); + + case (Sym) `()`: + return getSymbol(s, isLex); + + default: + throw "missed a case "; + } + else + switch(sym) { + case (Sym) ``: + return sort(""); + + case (Sym) ` *`: + return \iter-star-seps ( + getSymbol(s, isLex), + [\layouts("LAYOUTLIST")] + ); + + case (Sym) ` +`: + return \iter-seps ( + getSymbol(s, isLex), + [\layouts("LAYOUTLIST")] + ); + + case (Sym) ` *?`: + return \iter-star-seps ( + getSymbol(s, isLex), + [\layouts("LAYOUTLIST")] + ); + + case (Sym) ` +?`: + return \iter-seps ( + getSymbol(s, isLex), + [\layouts("LAYOUTLIST")] + ); + + case (Sym) `{ } *`: + return + \iter-star-seps ( + getSymbol(s, isLex), + [\layouts("LAYOUTLIST"), getSymbol(sep, isLex), \layouts("LAYOUTLIST")] + ); + + case (Sym) `{ } +`: + return + \iter-seps ( + getSymbol(s, isLex), + [\layouts("LAYOUTLIST"), getSymbol(sep, isLex), \layouts("LAYOUTLIST")] + ); + + case (Sym) `( )`: + return + seq( + [getSymbol(first, isLex)] + + [\layouts("LAYOUTLIST"), getSymbol(e, isLex) | e <- rest] + ); + + case (Sym) `( | )`: + return alt({getSymbol(first, isLex), getSymbol(second, isLex)}); + case (Sym) `()`: + return getSymbol(single, isLex); + default: + throw "missed a case "; + } +} + +public Symbol alt({alt(set[Symbol] ss), *Symbol rest}) = alt(ss + rest); + +test bool test40() = getSymbol((Sym) `"abc"`, false) == lit("abc"); +test bool test41() = getSymbol((Sym) `"a\\\\c"`, false) == lit("a\\c"); +test bool test42() = getSymbol((Sym) `"a\>c"`, false) == lit("a\>c"); +test bool test43() = getSymbol((Sym) `ABC`, false) == sort("ABC"); +test bool test44() = getSymbol((Sym) `'abc'`, false) == cilit("abc"); +test bool test45() + = getSymbol((Sym) `abc : ABC`, false) == label("abc", sort("ABC")); +test bool test46() + = getSymbol((Sym) `"abc" : ABC`, false) == label("abc", sort("ABC")); +test bool test47() + = getSymbol((Sym) `A[[B]]`, false) == \parameterized-sort ( + "A", + [sort("B")] + ); +test bool test48() = getSymbol((Sym) `A?`, false) == opt(sort("A")); +test bool test49() + = getSymbol((Sym) `[a]`, false) == \char-class([range(97, 97)]); +test bool test50() + = getSymbol((Sym) `A*`, false) == \iter-star-seps ( + sort("A"), + [\layouts("LAYOUTLIST")] + ); +test bool test51() + = getSymbol((Sym) `A+`, false) == \iter-seps ( + sort("A"), + [\layouts("LAYOUTLIST")] + ); +test bool test52() + = getSymbol((Sym) `A*?`, false) == opt(\iter-star-seps ( + sort("A"), + [\layouts("LAYOUTLIST")] + )); +test bool test53() + = getSymbol((Sym) `A+?`, false) == opt(\iter-seps ( + sort("A"), + [\layouts("LAYOUTLIST")] + )); +test bool test54() + = getSymbol((Sym) `{A "x"}*`, false) == \iter-star-seps ( + sort("A"), + [\layouts("LAYOUTLIST"), lit("x"), \layouts("LAYOUTLIST")] + ); +test bool test55() + = getSymbol((Sym) `{A "x"}+`, false) == \iter-seps ( + sort("A"), + [\layouts("LAYOUTLIST"), lit("x"), \layouts("LAYOUTLIST")] + ); +test bool test56() + = getSymbol((Sym) `{A "x"}*?`, false) == opt( + \iter-star-seps ( + sort("A"), + [\layouts("LAYOUTLIST"), lit("x"), \layouts("LAYOUTLIST")] + ) + ); +test bool test57() + = getSymbol((Sym) `{A "x"}+?`, false) == opt( + \iter-seps ( + sort("A"), + [\layouts("LAYOUTLIST"), lit("x"), \layouts("LAYOUTLIST")] + ) + ); +test bool test58() = getSymbol((Sym) `A*`, true) == \iter-star(sort("A")); +test bool test59() = getSymbol((Sym) `A+`, true) == \iter(sort("A")); +test bool test60() + = getSymbol((Sym) `A*?`, true) == opt(\iter-star(sort("A"))); +test bool test61() = getSymbol((Sym) `A+?`, true) == opt(\iter(sort("A"))); +test bool test62() + = getSymbol((Sym) `{A "x"}*`, true) == \iter-star-seps ( + sort("A"), + [lit("x")] + ); +test bool test63() + = getSymbol((Sym) `{A "x"}+`, true) == \iter-seps ( + sort("A"), + [lit("x")] + ); +test bool test64() + = getSymbol((Sym) `{A "x"}*?`, true) == opt(\iter-star-seps ( + sort("A"), + [lit("x")] + )); +test bool test65() + = getSymbol((Sym) `{A "x"}+?`, true) == opt(\iter-seps ( + sort("A"), + [lit("x")] + )); + +//test getSymbol((Sym) ``, true) == +//test getSymbol((Sym) ``, true) == +// ----- unescape ----- +// Take a string constant and replace all escaped characters by the character itself. +// Unescape on Symbols. Note that the function below can currently coexist with the two unescape functions +// since StrCon and SingleQuotedStrCons are *not* a subtype of Symbol (which they should be). +// Do a deep match (/) to skip the chain function that syntactically includes both subtypes in Symbol. +private str unescape(Sym s) { + if (/SingleQuotedStrCon scon := s) { + return unescape(scon); + } + if (/StrCon scon := s) { + return unescape(scon); + } + throw "unexpected string format: "; +} + +public str unescape(StrCon s) { + if ([StrCon] /^\"\"$/ := s) + return unescapeStr(chars); + throw "unexpected string format: "; +} + +private str unescape(SingleQuotedStrCon s) { + if ([SingleQuotedStrCon] /^\'\'$/ := s) + return unescapeStr(chars); + throw "unexpected string format: "; +} + +test bool testUn1() = unescape((StrCon) `"abc"`) == "abc"; +test bool testUn2() = unescape((StrCon) `"a\\nc"`) == "a\nc"; +test bool testUn3() = unescape((StrCon) `"a\\"c"`) == "a\"c"; +test bool testUn4() = unescape((StrCon) `"a\\\\c"`) == "a\\c"; +test bool testUn5() = unescape((StrCon) `"a\\\\\\"c"`) == "a\\\"c"; + +test bool testUn6() = unescape((SingleQuotedStrCon) `'abc'`) == "abc"; +test bool testUn7() = unescape((SingleQuotedStrCon) `'a\\nc'`) == "a\nc"; +test bool testUn8() = unescape((SingleQuotedStrCon) `'a\\'c'`) == "a\'c"; +test bool testUn9() = unescape((SingleQuotedStrCon) `'a\\\\c'`) == "a\\c"; + +// unescapeStr: do the actual unescaping on a string +// Also takes care of escaping of < and > characters as required for Rascal strings (TO DO/CHECK) +public str unescapeStr(str chars) { + return + visit(chars) { + case /^\\b/ => "\b" + case /^\\t/ => "\t" + case /^\\n/ => "\n" + case /^\\f/ => "\f" + case /^\\r/ => "\r" + case /^\\\'/ => "\'" + case /^\\"/ => "\"" + case /^\\\\/ => "\\" + case /^\\TOP/ => "\u00FF" + case /^\\EOF/ => "\u00A0" + case /^\\BOT/ => "\u0000" + case /^\\LABEL_START/ => "\u00A1" + case /^\ "\<" + case /^\>/ => "\>" + } + ; +} + +test bool un20() = unescapeStr("abc") == "abc"; +test bool un21() = unescapeStr("a\nbc") == "a\nbc"; +test bool un22() = unescapeStr("a\\\nbc") == "a\\\nbc"; +test bool un23() = unescapeStr("a\"bc") == "a\"bc"; +test bool un24() = unescapeStr("a\\\"bc") == "a\"bc"; +test bool un25() = unescapeStr("a\\bc") == "a\bc"; +test bool un26() = unescapeStr("a\\\\tc") == "a\\tc"; +test bool un27() = unescapeStr("a\>b") == "a\>b"; +test bool un28() = unescapeStr("a\]`: + return \new-char-class([getCharRange(r) | /Range r := ranges]); + + case (Class) `()`: + return getCharClass(c); + + case (Class) `~ `: + return complement(getCharClass(c)); + + case (Class) ` /\\ `: + return intersection(getCharClass(l), getCharClass(r)); + + case (Class) ` \\/ `: + return union(getCharClass(l), getCharClass(r)); + + case (Class) ` / `: + return difference(getCharClass(l), getCharClass(r)); + + default: + throw "missed a case "; + } +} + +test bool testCC1() = getCharClass((Class) `[]`) == \char-class([]); +test bool testCC2() + = getCharClass((Class) `[a]`) == \char-class([range(97, 97)]); +test bool testCC3() + = getCharClass((Class) `[a-z]`) == \char-class([range(97, 122)]); +test bool testCC4() + = getCharClass((Class) `[a-z0-9]`) == \char-class([range(97, 122), range(48, 57)]); +test bool testCC5() + = getCharClass((Class) `([a])`) == \char-class([range(97, 97)]); +test bool testCC6() + = getCharClass((Class) `~[a]`) == complement(\char-class([range(97, 97)])); +test bool testCC7() + = getCharClass((Class) `[a] /\\ [b]`) == intersection(\char-class([range(97, 97)]), \char-class([range(98, 98)])); +test bool testCC8() + = getCharClass((Class) `[a] \\/ [b]`) == union(\char-class([range(97, 97)]), \char-class([range(98, 98)])); +test bool testCC9() + = getCharClass((Class) `[a] / [b]`) == difference(\char-class([range(97, 97)]), \char-class([range(98, 98)])); +test bool testCC10() + = getCharClass((Class) `[\\n]`) == \char-class([range(10, 10)]); +test bool testCC11() + = getCharClass((Class) `[\\t\\n]`) == \char-class([range(9, 9), range(10, 10)]); +test bool testCC12() + = getCharClass((Class) `~[\\0-\\31\\n\\t\\"\\\\]`) == complement( + \char-class([ + range(0, 25), range(10, 10), range(9, 9), range(34, 34), range(92, 92) + ]) + ); +test bool testCC13() + = getCharClass((Class) `[\\"]`) == \char-class([range(34, 34)]); + +// ----- getCharRange ----- +public CharRange getCharRange(Range r) { + switch(r) { + case (Range) ``: + return range(getCharacter(c), getCharacter(c)); + case (Range) ` - `: + return range(getCharacter(l), getCharacter(m)); + default: + throw "missed a case "; + } +} + +test bool testCR1() = getCharRange((Range) `a`) == range(97, 97); +test bool testCR2() = getCharRange((Range) `a-z`) == range(97, 122); +test bool testCR3() = getCharRange((Range) `\\n`) == range(10, 10); +test bool testCR4() = getCharRange((Range) `\\1-\\31`) == range(1, 25); + +public int getCharacter(Character c) { + switch(c) { + case [Character] /\\/: + return toInt(""); + case [Character] /\\/: + return toInt(""); + case [Character] /\\/: + return toInt(""); + case [Character] /\\t/: + return 9; + case [Character] /\\n/: + return 10; + case [Character] /\\r/: + return 13; + case [Character] /\\ /: + return 32; + case [Character] /\\/: + return charAt(esc, 0); + case [Character] //: + return charAt(ch, 0); + + default: + throw "missed a case "; + } +} + +//test bool testCCX1() = ((Character) `a`) == charAt("a", 0); +//test bool testCCX2() = ((Character) `\\\\`) == charAt("\\", 0); +//test bool testCCX3() = ((Character) `\\'`) == charAt("\'", 0); +//test bool testCCX4() = ((Character) `\\1`) == 1; +//test bool testCCX5() = ((Character) `\\12`) == 12; +//test bool testCCX6() = ((Character) `\\123`) == 123; +//test bool testCCX7() = ((Character) `\\n`) == 10; +// ----- getAttributes, getAttribute, getAssociativity ----- +public set[Attr] getAttributes(Attrs as) { + if ((Attrs) `{ <{Attribute ","}* mods> }` := as) { + return {*getAttribute(m)| Attribute m <- mods}; + } + return {}; +} + +test bool testAs() + = getAttributes((Attrs) `{left, cons("decl")}`) == {\assoc(\left()), \tag("cons"("decl"))}; + +public set[Attr] getAttribute(Attribute m) { + switch(m) { + case (Attribute) ``: + return {\assoc(getAssociativity(as))}; + + case (Attribute) `bracket`: + return {\bracket()}; + + case (Attribute) `cons()`: + return {}; + + case (Attribute) `memo`: + return {\tag("NotSupported"("memo"))}; + + case (Attribute) `prefer`: + return {\tag("prefer"())}; + + case (Attribute) `avoid`: + return {\tag("avoid"())}; + + case (Attribute) `reject`: + return {\tag("reject"())}; + + case (Attribute) `category()`: + return {\tag("category"(unescape(a)))}; + + case (Attribute) `()`: + return {\tag("NotSupported"(""(unescape(a))))}; + + case (Attribute) ``: + return {\tag("NotSupported"(""))}; + + default: + return {}; + } +} + +test bool testAs2() + = getAttribute((Attribute) `left`) == {\assoc(\left())}; + +private Associativity getAssociativity(Assoc as) { + switch(as) { + case (Assoc) `left`: + return \left(); + case (Assoc) `right`: + return \right(); + case (Assoc) `non-assoc`: + return \non-assoc(); + case (Assoc) `assoc`: + return \assoc(); + default: + throw "missed a case "; + } +} + +test bool testAssoc() = getAssociativity((Assoc) `left`) == \left(); + +private list[Symbol] separgs2symbols({Sym ","}+ args, bool isLex) { + return [getSymbol(s, isLex) | Sym s <- args]; +} +### |project://rascal/src/org/rascalmpl/library/lang/sexp/SExp.rsc| +@synopsis{AST model for S-Expressions.} +@contributor{Tijs van der Storm - storm@cwi.nl (CWI)} +module lang::sexp::SExp + +import lang::sexp::\syntax::SExp; +import ParseTree; + +data SExp + = string(String \str) + | \list(list[SExp] elts) + ; + +data String + = simple(SimpleString simpleStr) + | display(str display, SimpleString simpleStr) + ; + +data SimpleString + = raw(Raw raw) + | token(str \value) + | base64(list[str] chars) + | hex(list[str] digits) + | quoted(str \value) + ; + +data Raw = raw(int size, str bytes); + +public +SExp parseSExp(str src, loc l) + = implode(#SExp, parse(#lang::sexp::\syntax::SExp::SExp, src, l)); +### |project://rascal/src/org/rascalmpl/library/lang/sexp/syntax/SExp.rsc| +@synopsis{Syntax definition for S-Expressions, based on http://people.csail.mit.edu/rivest/Sexp.txt} +@contributor{Tijs van der Storm - storm@cwi.nl (CWI)} +module lang::sexp::\syntax::SExp + +import String; +import IO; + +start syntax SExp + = string: String + | \list: "(" SExp* ")" + ; + +syntax String + = simple: SimpleString + | display: Display SimpleString + ; + +syntax SimpleString + = raw: Raw + | token: Token + | base64: Base64 + | hex: HexaDecimal + | quoted: QuotedString + ; + +syntax Display = bracket "[" SimpleString "]"; + +syntax Raw = raw: Decimal >> [:] ":" !>> [\ \t\n\r] Bytes; + +lexical Decimal + = [1-9] [0-9]* !>> [0-9] + | [0] + ; + +lexical Bytes = ![]*; + +lexical Token = TokenChar+ !>> [a-zA-Z0-9\-./_:*+=]; + +syntax Base64 = bracket "|" Base64Char* "|"// nb: whitespace allowed + ; + +syntax HexaDecimal = bracket "#" HexDigit* "#"; + +// nb: whitespace allowed +lexical QuotedString = [\"] QSChar* [\"]; + +lexical QSChar + = ![\"\'\\\n\r] + | [\\] [btvnfr\"\'\\] + | [\\] [0-7] [0-7] [0-7] + | [\\] [x] HexDigit HexDigit + | [\\] [\n\r] + | [\\] [\r] [\n] + | [\\] [\n] [\r] + ; + +layout Whitespace = WS* !>> [\ \t\n\r]; + +lexical WS = [\ \t\n\r]; + +lexical TokenChar + = Alpha + | DecimalDigit + | SimplePunc + ; + +lexical Alpha = [a-zA-Z]; + +lexical DecimalDigit = [0-9]; + +lexical HexDigit = [0-9A-Fa-f]; + +lexical SimplePunc = [\-./_:*+=]; + +lexical Base64Char + = Alpha + | DecimalDigit + | [+/=] + ; + +public Raw raw(Decimal d, Bytes bs) { + int l = toInt(unparse(d)); + str s = unparse(bs); + println("L = "); + println("s = \"\""); + if (l != size(s)) { + filter; + } + else { + fail; + } +} + +str unparse(Bytes _) { + throw "unparse Bytes not implemented"; +} + +str unparse(Decimal _) { + throw "unparse Decimal not implemented"; +} +### |project://rascal/src/org/rascalmpl/library/lang/smtlib2/Compiler.rsc| +@license{ + Copyright (c) 2009-2015 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@synopsis{Synopsis: Translate the SMTLIBv2 AST to string so that it can be interpreted by a SMTLIBv2 compliant solver} +@contributor{Jouke Stoel - stoel@cwi.nl (CWI)} +module lang::smtlib2::Compiler + +import lang::smtlib2::command::Ast; +import lang::smtlib2::theory::core::Ast; +import lang::smtlib2::theory::ints::Ast; + +list[str] toString(Script script) + = [toString(command) | command <- script.commands]; + +// Commands +str toString(setLogic(Logic logic)) = "(not yet implemented)"; +str toString(setOption(Option option)) + = "(set-option )"; +str toString(setInfo(Info info)) = "(not yet implemented)"; +str toString(declareSort(str name, int arity)) = "(not yet implemented)"; +str toString(defineSort(str name, list[str] sorts, list[Sort] types)) + = "(not yet implemented)"; +str toString(declareFunction(str name, list[Sort] params, Sort returnType)) + = "(declare-fun () )"; +str toString( + defineFunction(str name, list[SortedVar] params, Sort returnType, Expr body) +) + = "(define-fun () )"; +str toString(\assert(Expr expr)) = "(assert )"; +str toString(checkSatisfiable()) = "(check-sat)"; +str toString(getValue(exprs)) + = "(get-value (<( "" | "<it> " | expr <- exprs )>))"; +str toString(getUnsatCore()) = "(get-unsat-core)"; +str toString(push(nr)) = "(push )"; +str toString(pop(nr)) = "(pop )"; +str toString(exit()) = "(exit)"; +default str toString(Command command) = "(unknown command)"; + +// Options +str toString(interactiveMode(val)) = ":interactive-mode "; +str toString(printSuccess(bool val)) = ":print-success "; +str toString(regularOutputChannel(channel)) + = ":regular-output-channel "; +str toString(diagnosticOutputChannel(str channel)) + = ":diagnostic-output-channel "; +str toString(expandDefinitions(bool val)) = ":expand-definitions "; +str toString(produceProofs(bool val)) = ":produce-proofs "; +str toString(produceUnsatCores(bool val)) = ":produce-unsat-cores "; +str toString(produceModels(bool val)) = ":produce-models "; +str toString(produceAssignments(bool val)) + = ":produce-assignments "; +str toString(randomSeed(int seed)) = ":random-seed "; +str toString(verbosity(int level)) = ":verbosity "; + +// Sorts +str toString(list[SortedVar] params) + = ( "" | "<it> ( )" | param <- params ); +str toString(list[Sort] sorts) + = ( "" | "<it> " | sort <- sorts ); +str toString(\int()) = "Int"; +str toString(\bool()) = "Bool"; + +// Literals +str toString(boolVal(b)) = b ? "true" : "false"; +str toString(intVal(i)) = ""; + +// Expr +str toString(var(str name)) = ""; +str toString(lit(Literal lit)) = toString(lit); +str toString(named(labeledExpr, label)) + = "(! :named <}>", htmlEscapes); + + str rec(Tree t: appl(prod(lit(str l), _, _), _)) = span("Keyword", l) + when isKeyword(l); + + str rec(Tree t: appl(prod(cilit(str l), _, _), list[Tree] as)) + = span("Keyword", yield(as)) + when isKeyword(l); + + str rec( + Tree t: appl(prod(_, _, {*_, \tag("category"(str cat))}), list[Tree] as) + ) + = span(cat, yield(as)); + + str rec(amb({Tree k, *_})) = rec(k); + + default str rec(appl(Production p, list[Tree] as)) + = "<for (Tree a <- as) {><}>"; + + default str rec(Tree t: char(_)) = escape("", htmlEscapes); + + str span(str class, str src) = "\\"\>\"; + + if (withStyle) { + return + "\ + ' + '\ + '\

\
+            '
+            '\\";
+    }
+    else {
+        return
+            "\
\
+            '
+            '\\";
+    }
+}
+
+@synopsis{Yields the characters of a parse tree as the original input sentence but using macros to wrap to-be-highlighted areas.}
+public str toLaTeX(Tree t) {
+    texEscapes
+        = (
+          "\\" : "\\textbackslash{}",
+          "\<" : "\\textless{}",
+          "\>" : "\\textgreater{}",
+          "%"  : "\\%{}",
+          "&"  : "\\&{}",
+          "_"  : "\\_{}",
+          "^"  : "\\^{}",
+          "{"  : "\\{{}",
+          "}"  : "\\}{}",
+          "$"  : "\\${}",
+          "["  : "{}[",
+          "\t" : "    "
+          );
+    
+    str rec(appl(prod(lit(str l), _, _), _)) = cat("Keyword", l)
+        when isKeyword(l);
+    
+    str rec(appl(prod(cilit(str l), _, _), _)) = cat("Keyword", l)
+        when isKeyword(l);
+    
+    str rec(
+        appl(prod(_, _, {*_, \tag("category"(str category))}), list[Tree] as)
+    )
+        = cat(category, "<for (a <- as) {><}>");
+    
+    default str rec(appl(_, list[Tree] as)) = "<for (a <- as) {><}>";
+    
+    str rec(amb({k, *_})) = rec(k);
+    
+    default str rec(Tree t: char(_)) = escape("", texEscapes);
+    
+    str cat(str class, str src) = "\\CAT{}{}";
+    
+    return rec(t);
+}
+
+@synopsis{Unparse a parse tree to unicode characters, wrapping certain substrings with ANSI codes for highlighting.}
+public str toANSI(Tree t, bool underlineAmbiguity = false, int tabSize = 4) {
+    str rec(Tree x: appl(prod(lit(str l), _, _), _))
+        = isKeyword(l) ? bold("") : "";
+    str rec(Tree x: appl(prod(cilit(str l), _, _), _))
+        = isKeyword(l) ? bold("") : "";
+    
+    str rec(
+        Tree x: appl(prod(_, _, {*_, \tag("category"(str cat))}), list[Tree] as)
+    )
+        = \map(cat, "");
+    
+    default str rec(x: appl(_, list[Tree] as)) = "<for (a <- as) {><}>";
+    
+    str rec(amb({k, *_})) = underlineAmbiguity ? underline(rec(k)) : rec(k);
+    
+    str rec(char(9)) = right("", tabSize);
+    default str rec(Tree t: char(_)) = "";
+    
+    str ESC = "\a1b[";
+    str Bold = "1m";
+    str Underline = "4m";
+    str Normal = "0m";
+    str Comment = "3m2m";
+    str bold(str s) = "";
+    str underline(str s) = "";
+    str comment(str s) = "";
+    
+    str \map(/[Cc]omment/, text) = comment(text);
+    str \map(/[Kk]eyword/, text) = bold(text);
+    default str \map(str _, str text) = text;
+    
+    return rec(t);
+}
+
+@synopsis{Encodes when to highlight a literal as a keyword category}
+private bool isKeyword(str s) = /^[a-zA-Z0-9_\-]*$/ := s;
+### |project://rascal/src/org/rascalmpl/library/util/IDEServices.rsc|
+module util::IDEServices
+
+extend analysis::diff::edits::TextEdits;
+import analysis::diff::edits::ExecuteTextEdits;
+extend Content;
+extend Message;
+
+@synopsis{Open a browser for a given location.}
+@description{
+Starts an _interactive_ browser for a given URI, typically in a tab embedded in the IDE.
+However, this depends on the current IDE context. Some editors do not support this feature.
+A browser window for the OS default browser will be started instead.
+}
+@javaClass{org.rascalmpl.library.util.IDEServicesLibrary}
+java void browse(
+    loc uri,
+    str title = "",
+    ViewColumn viewColumn = normalViewColumn(1)
+);
+
+@synopsis{Open an editor for file at a given location.}
+@description{
+Based on the current IDE context an editor will be "opened". This means
+for most IDEs that the language services associated with the file extension
+will be activated. However, this depends entirely on the IDE and the currently
+registered languages. 
+}
+@javaClass{org.rascalmpl.library.util.IDEServicesLibrary}
+java void edit(loc uri, ViewColumn viewColumn = activeViewColumn());
+
+@synopsis{Let the IDE apply a list of document edits.}
+@description{
+Asks the IDE to apply document edits as defined in the standard library module
+analysis::diff::edits::TextEdits, according to the semantics defined in
+analysis::diff::edits::ExecuteTextEdits. However, the IDE can take care of these
+changes in order to provide important UI experience features such as "preview"
+and "undo". 
+
+Typically a call to this IDE service method is included in the implementation
+of refactoring and quick-fix features of the language service protocol.
+}
+@javaClass{org.rascalmpl.library.util.IDEServicesLibrary}
+java void applyDocumentsEdits(list[FileSystemChange] edits);
+
+void applyFileSystemEdits(list[FileSystemChange] edits) {
+    applyDocumentsEdits(edits);
+}
+
+@synopsis{Asks the IDE to show a "browser window" with the given interactive Content.}
+@description{
+Just like ((browse)), with the important distinction that this starts both
+a web _client_ and a web _server_.
+}
+@benefits{
+* quickly spin-up and manage interactive visuals without worrying about garbage collection and memory leaks, or port numbers
+* shows visuals _inside_ the current IDE. Combines very well with ((edit)) to show visuals side-by-side with code.
+}
+@pitfalls{
+* the web servers will remain active until 30 minutes after
+the last interaction. After that a `404` (not found) http error will be produced and 
+((showInteractiveContent)) has to be called again to re-activate the visual.
+}
+@javaClass{org.rascalmpl.library.util.IDEServicesLibrary}
+java void showInteractiveContent(
+    Content content,
+    str title = content.title,
+    ViewColumn viewColumn = content.viewColumn
+);
+
+@javaClass{org.rascalmpl.library.util.IDEServicesLibrary}
+java void showMessage(Message message);
+
+@javaClass{org.rascalmpl.library.util.IDEServicesLibrary}
+java void logMessage(Message message);
+
+@javaClass{org.rascalmpl.library.util.IDEServicesLibrary}
+java void registerDiagnostics(list[Message] messages);
+
+@javaClass{org.rascalmpl.library.util.IDEServicesLibrary}
+java void unregisterDiagnostics(list[loc] resources);
+
+@synopsis{Fixes are an extension to error messages that allow for interactive code fixes in the IDE.}
+@description{
+This definition adds lists of ((CodeAction))s as optional fields to any message. In collaboration
+with a language server, these messages then lead to interactive quick fixes in IDEs.
+}
+data Message(list[CodeAction] fixes = []);
+
+@synopsis{Code actions bundle synchronous text edits and command execution with a title for the menu option.}
+@description{
+For any action instance, the IDE will:
+* show a menu option with the given title.
+* if the title is selected, then the (optional) edits will be executed first
+* and then the (optional) command is executed via the `execution` service of the language service protocol.
+}
+data CodeAction (list[FileSystemChange] edits = [],
+                 Command command = noop(),
+                 str title = command.title) = action();
+
+@synopsis{Commands are an open data-type for describing interactive functions that may be attached to CodeActions.}
+@description{
+Commands are simply immutable constructors with parameters. To use a command you can attach it to a ((module:Message))
+via a ((CodeAction)), and then have it executed by the respective language server.
+}
+data Command (str title = "") = noop();
+
+@synopsis{Utility function for testing code actions.}
+@benefits{
+* test code actions outside of the IDE context, for example while running unit tests.
+* this function is synchronous and blocks until the IO is finished. After running it you
+can test for changed file contents without waiting, in most cases (see pitfalls).
+}
+@description{
+* `action` is the action to execute
+* `evaluator` is used to evaluate action.command if it is present.
+* the return value is the return value of the evaluated command, or `true` if no command is present.
+}
+@pitfalls{
+* ((CodeAction))s may use the other features of ((util::IDEServices)), and thus start editors or browsers as side-effects.
+* ((CodeAction))s code actions with ((FileSystemChanges-FileSystemChange))s will write to disk and change the original files.
+* ((util::IDEServices::Command))s can only be executed by a parametrized command `evaluator``; if you do not provide it then 
+this test function will throw ((CallFailed)) exceptions for every unsupported (((util::IDEServices::Command)).
+* (((util::IDEServices::Command))s can start asynchronous effects by calling non-blocking functions that schedule other effects.
+An example is the starting and running of web ((Library:module:Content)) via ((showInteractiveContent)). Testing properties of the
+rendered content will require the use of asynchronous testing frameworks, like Selenium. 
+* Never call ((testCodeAction)) to execute actions in an interactive context. That must be done by the IDE client
+to synchronize the contents of editors and parse trees, etc. This function is only for unit testing code actions.
+}
+value testCodeAction(
+    CodeAction action,
+    value(Command _) evaluator = value (noop()){ return true; }
+) {
+    if (action.edits?) {
+        executeFileSystemChanges(action.edits);
+    }
+    if (action.command?) {
+        return evaluator(action.command);
+    }
+    return true;
+}
+### |project://rascal/src/org/rascalmpl/library/util/Math.rsc|
+@license{
+  Copyright (c) 2009-2015 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI}
+@contributor{Bert Lisser - Bert.Lisser@cwi.nl (CWI)}
+@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI}
+@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl}
+@synopsis{Mathematical functions.}
+@description{
+The `Math` library provides the following functions:
+
+(((TOC)))
+}
+module util::Math
+
+import List;
+import Exception;
+
+@synopsis{Absolute value of a number.}
+@description{
+Absolute value of the number `n`. The result type is equal to the type of the argument `n`.
+}
+@examples{
+```rascal-shell
+import util::Math;
+abs(13)
+abs(-13)
+abs(3.14)
+abs(-3.14)
+```
+}
+public &T <: num abs(&T <: num N) {
+    return N >= 0 ? N : -N;
+}
+
+@synopsis{Generate a random integer value.}
+@description{
+Return an arbitrary integer value. When the argument `limit` is given, the generated value is in the interval [0, `limit`),
+i.e., the limit is exclusive.
+}
+@examples{
+```rascal-shell
+import util::Math;
+arbInt();
+arbInt();
+arbInt();
+arbInt(10);
+arbInt(10);
+arbInt(10);
+```
+}
+@benefits{
+`arbInt` is a convenient generator for pseudo-random integers.
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java int arbInt();
+
+@javaClass{org.rascalmpl.library.util.Math}
+public java int arbInt(int limit);
+
+@synopsis{Generate a random real value in the interval [0.0,1.0).}
+@description{
+Generates an arbitrary real value in the interval [0.0, 1.0].
+}
+@examples{
+```rascal-shell
+import util::Math;
+arbReal();
+arbReal();
+arbReal();
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real arbReal();
+
+@synopsis{Define the seed for the generation of arbitrary values.}
+@description{
+Define the seed for the generation of arbitrary values such as ((arbBool)), ((arbInt)), ((arbReal)),
+((arbRat)), ((List-getOneFrom)),((Set-getOneFrom)), ((List-takeOneFrom)) and ((Set-takeOneFrom)). ((arbSeed)) resets the random number generator that
+is used to choose arbitrary values. This can be used to generate a reproducible series of choices.
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java void arbSeed(int seed);
+
+@synopsis{Generate an arbitrary rational value.}
+@examples{
+```rascal-shell
+import util::Math;
+arbRat();
+arbRat();
+arbRat();
+arbRat(10,100);
+arbRat(10,100);
+arbRat(10,100);
+```
+}
+public rat arbRat() {
+    n = arbInt();
+    d = arbInt();
+    if (d == 0)
+        d = 1;
+    return toRat(n, d);
+}
+
+public rat arbRat(int limit1, int limit2) {
+    n = arbInt(limit1);
+    d = arbInt(limit2);
+    if (d == 0)
+        d = 1;
+    return toRat(n, d);
+}
+
+@synopsis{Compute the smallest integer that is larger than a given number.}
+@description{
+Computes the _ceiling_ of a given number.
+Also see ((util::Math::floor)).
+}
+@examples{
+```rascal-shell
+import util::Math;
+ceil(3.2);
+ceil(-3.2);
+```
+}
+public int ceil(num x) {
+    int i = toInt(x);
+    if (i == x || x < 0) {
+        return i;
+    }
+    else {
+        return i + 1;
+    }
+}
+
+@synopsis{Calculate the cosine of a numeric value.}
+@description{
+The cosine of the number `x`.
+}
+@examples{
+```rascal-shell
+import util::Math;
+cos(1.0)
+cos(60 * PI() / 180)
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real cos(num x);
+
+@synopsis{Return the denominator of a rational value.}
+@javaClass{org.rascalmpl.library.util.Math}
+public java int denominator(rat n);
+
+@synopsis{The constant E.}
+@examples{
+```rascal-shell
+import util::Math;
+E();
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real E();
+
+@synopsis{Compute exp(x).}
+@description{
+Calculate `e``x`.
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real exp(num x);
+
+@synopsis{Compute the largest integer that is smaller than a given number.}
+@description{
+Computes the _floor_ of a given number.
+Also see ((util::Math::ceil)).
+}
+@examples{
+```rascal-shell
+import util::Math;
+floor(3.2);
+floor(-3.2);
+```
+}
+public int floor(num x) {
+    i = toInt(x);
+    if (i == x || x >= 0) {
+        return i;
+    }
+    else {
+        return i - 1;
+    }
+}
+
+@synopsis{Calculate the natural log of a numeric value.}
+@description{
+Calculate natural log of `x`.
+}
+@examples{
+```rascal-shell
+import util::Math;
+ln(20.0)
+ln(42.0)
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real ln(num x);
+
+@synopsis{Calculate the logbase of a numeric value.}
+@description{
+Calculate logbase of `x`.
+}
+@examples{
+```rascal-shell
+import util::Math;
+log(9.99999999, 10)
+log(10, 10)
+log(256.0, 2)
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real log(num x, num base);
+
+@synopsis{Compute the 10 based log(x).}
+public real log10(num x) = log(x, 10.0);
+
+@synopsis{Compute the 2 based log(x).}
+public real log2(num x) = log(x, 2.0);
+
+@synopsis{Determine the largest of two numeric values.}
+@description{
+The largest of two numbers. The type of the result is the same as the type of the largest argument.
+}
+@examples{
+```rascal-shell
+import util::Math;
+max(12, 13);
+max(12, 13.5);
+max(12, 11.5);
+```
+}
+public &T <: num max(&T <: num N, &T <: num M) {
+    return N > M ? N : M;
+}
+
+@synopsis{Determine the smallest of two numeric values.}
+@description{
+The smallest of two numbers. The type of the result is the same as the type of the smallest argument.
+}
+@examples{
+```rascal-shell
+import util::Math;
+min(12, 13);
+min(12, -13);
+min(3.14, 4);
+```
+}
+public &T <: num min(&T <: num N, &T <: num M) {
+    return N < M ? N : M;
+}
+
+@synopsis{Return the numerator of a rational value.}
+@javaClass{org.rascalmpl.library.util.Math}
+public java int numerator(rat n);
+
+@synopsis{Calculate the nth root of a numeric value.}
+@description{
+Calculate n√`x` where `n` can only be a integer.
+}
+@examples{
+```rascal-shell
+import util::Math;
+nroot(42 * 42, 2);
+nroot(42 * 42 * 42, 3);
+nroot(123456789012345678901234567890123456789.0, 100)
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real nroot(num x, int n);
+
+@synopsis{The constant pi.}
+@examples{
+```rascal-shell
+import util::Math;
+PI();
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real PI();
+
+@synopsis{Calculate an arbitrary power of a numeric value.}
+@description{
+The calculate `x``y` where `y` can only be a integer.
+}
+@examples{
+```rascal-shell
+import util::Math;
+pow(sqrt(42), 2)
+pow(12345678901234567890.0, 1000)
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real pow(num x, int y);
+
+@synopsis{Calculate an arbitrary power of a numeric value.}
+@description{
+The calculate `x``y` where `y` can be any real value.
+}
+@examples{
+```rascal-shell
+import util::Math;
+pow(sqrt(42), 2.3)
+pow(12345678901234567890.0, 100.2)
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real pow(num x, real y);
+
+@synopsis{Return the precision of a real number.}
+@javaClass{org.rascalmpl.library.util.Math}
+public java int precision(num x);
+
+@synopsis{Return a real number with given precision}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real precision(num x, int p);
+
+@synopsis{Define the precision for numeric calculations; returns the previous precision.}
+@javaClass{org.rascalmpl.library.util.Math}
+public java int setPrecision(int p);
+
+@synopsis{Return the scale of a real number.}
+@javaClass{org.rascalmpl.library.util.Math}
+public java int scale(num x);
+
+@synopsis{Return the unscaled integer of a real.}
+@javaClass{org.rascalmpl.library.util.Math}
+public java int unscaled(real x);
+
+@synopsis{Return the remainder of dividing the numerator by the denominator.}
+@javaClass{org.rascalmpl.library.util.Math}
+public java int remainder(rat n);
+
+@synopsis{Round a number to the nearest multiple of a given number (default 1).}
+@examples{
+```rascal-shell
+import util::Math;
+round(3.4);
+round(3.5);
+round(3.6);
+round(-3.4);
+round(-3.5);
+round(-3.6);
+round(13, 5);
+round(1.5,0.2);
+round(3r2,1r4);
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java int round(num d);
+public
+(&T <: num) round(&T <: num r, &T <: num nearest)
+    = round(r / (nearest * 1.0)) * nearest;
+
+@synopsis{p
+Push real value into a float using coercion and return the value represented by that float as a real}
+@description{
+The function fitFloat converts the unlimited precision real into a JVM float value.
+}
+@benefits{
+* This function comes in handy in combination with random real test values which have to 
+go through coercion in a Java library, like so:
+ `bool test myTest(real r, real j) = fitFloat(r) + fitFloat(j) == fitFloat(r) + fitFloat(j);`
+}
+@pitfalls{
+* If the real is smaller than the minimum float value or larger than the maximum float
+value, this function will throw an ArithmeticException.
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real fitFloat(real r) throws ArithmeticException;
+
+@synopsis{Push real value into a JVM double using coercion and return the value represented by that float as a real}
+@description{
+The function fitDouble converts the unlimited precision real into a JVM double value.
+}
+@benefits{
+* This function comes in handy in combination with random real test values which have to 
+go through coercion in a Java library, like so:
+ `bool test myTest(real r, real j) = fitDouble(r) + fitDouble(j) == fitDouble(r) + fitDouble(j);`
+}
+@pitfalls{
+* If the real is smaller than the minimum double value or larger than the maximum double
+value, this function will throw an ArithmeticException.
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real fitDouble(real r) throws ArithmeticException;
+
+@synopsis{Compute the ratio between two numbers as a percentage.}
+@examples{
+```rascal-shell
+import util::Math;
+percent(1r4, 1);
+percent(13,250);
+percent(80.0,160.0);
+```
+}
+public int percent(num part, num whole) = round((part / (whole * 1.0)) * 100);
+
+@synopsis{Calculate the sine of a numeric value.}
+@description{
+The sine of the number `x`.
+}
+@examples{
+```rascal-shell
+import util::Math;
+sin(0)
+sin(PI() / 2)
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real sin(num x);
+
+@synopsis{Calculate the square root of a numeric value.}
+@description{
+Calculate √`x`.
+}
+@examples{
+```rascal-shell
+import util::Math;
+sqrt(42 * 42);
+sqrt(12345678901234567890.5 * 12345678901234567890.5);
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real sqrt(num x);
+
+@synopsis{Calculate the tangent of a numeric value.}
+@description{
+The tangent of the number `x`.
+}
+@examples{
+```rascal-shell
+import util::Math;
+tan(45 * PI() / 180)
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real tan(num x);
+
+@synopsis{Convert a numeric value to an integer.}
+@description{
+Convert a number to an integer. If `n` is an integer, this is the identity. If `n` is a real value (implemented as BigDecimal) to an integer (implemented as BigInteger). This conversion is analogous to a narrowing primitive conversion from double to long as defined in the Java Language Specification: any fractional part of this BigDecimal will be discarded. Note that this conversion can loose information about the precision of the BigDecimal value.
+}
+@examples{
+```rascal-shell
+import util::Math;
+toInt(13)
+toInt(13.5)
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java int toInt(num N);
+
+@synopsis{Convert two numbers to a rational value (not supported on reals).}
+@javaClass{org.rascalmpl.library.util.Math}
+public java rat toRat(int numerator, int denominator);
+
+@synopsis{Convert a numeric value to a real.}
+@examples{
+```rascal-shell
+import util::Math;
+toReal(12)
+toReal(3.14)
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java real toReal(num N);
+
+@synopsis{Convert a numeric value to a string.}
+@examples{
+```rascal-shell
+import util::Math;
+toString(12)
+toString(3.14)
+```
+}
+@javaClass{org.rascalmpl.library.util.Math}
+public java str toString(num N);
+
+@synopsis{generate prime numbers up to a maximum}
+@memo
+public
+list[int] primes(int upTo)
+    // TODO: replaced "p <- ..." by "int p <- ..." to help new typechecker
+    = [p
+      | int p <- [2..upTo], p < 4 || all(i <- [2..toInt(sqrt(p)) + 1], p != i ? p % i != 0 : true)];
+
+// Some test code: https://gist.github.com/grammarware/839f63b1a4999992ade7
+// TODO: replaced "ps :=" by "list[int] ps :=" to help new typechecker
+public int arbPrime(int upTo) = ps[arbInt(size(ps))]
+    when list[int] ps := primes(upTo);
+### |project://rascal/src/org/rascalmpl/library/util/Maybe.rsc|
+@license{
+  Copyright (c) 2009-2022 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Atze van der Ploeg - ploeg@cwi.nl - CWI}
+@synopsis{Encapsulate any optional value using `Maybe[&T]`}
+module util::Maybe
+
+@synopsis{Generic data type to encapsulate any value, optionally.}
+@examples{
+```rascal-shell
+import util::Maybe;
+// nothing() can always be assigned to a variable of type `Maybe[Anytype]`
+Maybe[int] myIntOption = nothing();
+// another example of the same feature:
+Maybe[str] myStrOption = nothing();
+// if you do have a value, the type of the parameter of `just` must align:
+myStrOption = just("a string");
+```
+
+If you don't align the type of the parameter with the parameter type of `Maybe`, static errors ensue:
+```rascal-shell-continue-error
+myStrOption = just(42);
+```
+
+Here's a function that sometimes returns a value and otherwise returns `nothing()`:
+```rascal
+Maybe[int] indexOf(list[int] haystack, int needle) {
+   for (i <- index(haystack), haystack[i] == needle) {
+      return just(i);
+   }
+   
+   return nothing();
+}
+```
+}
+data Maybe[&A]
+    = nothing()
+    | just(&A val)
+    ;
+### |project://rascal/src/org/rascalmpl/library/util/Memo.rsc|
+module util::Memo
+
+data MemoExpireConfiguration
+    = expireAfter( // expires entries when  times has passed since last access
+                  // define only one of these limits
+                  int seconds = -1, int minutes = -1, int hours = -1)
+    | maximumSize(int entries)
+    // expires entries when the cache get's fuller than a certain size
+    ;
+### |project://rascal/src/org/rascalmpl/library/util/Monitor.rsc|
+@license{
+  Copyright (c) 2009-2015 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI}
+@synopsis{Monitor the progress of a task/job.}
+@bootstrapParser
+module util::Monitor
+
+import util::Math;
+import IO;
+import Exception;
+
+@synopsis{Log the start of a job.}
+@description{
+((jobStart)) registers a new current job on the job stack with an amount of
+steps todo and how much work it contributes (when it ends) to its parent job (if any).
+* The `label` _identifies_ as running task, which could be presented in the UI with a specific progress bar (for example)
+* Using ((jobStep)) with the same label, you can advance the amount of work done for the task.
+* All tasks should eventually end with ((jobEnd)), but most UI's will clean up open left-over tasks at natural intervals as well.
+* Use ((jobTodo)) to register additional dynamically discovered work for a task. The `totalWork` constant for that
+specific task will be increased by the given amount.
+
+
+}
+@benefits{
+* It is advised to use the "block" functions `job` instead of the raw `jobStart`, `jobStep` and `jobEnd`
+functions because these guarantee each started task is always ended, with and without exceptions. This improves
+the user experience for your users. Also these functions help by providing the job label in the scope of the task,
+such that this "magic constant" does not need to be repeated.
+}
+@pitfalls{
+* The job label is both an identity and a user facing string constant. Future versions of the API may
+split the identify from the label for better accuracy and better UI.
+}
+@javaClass{org.rascalmpl.library.util.Monitor}
+java void jobStart(str label, int work = 1, int totalWork = 100);
+
+@synopsis{Log to the user that a certain event has happened under the currently registered Job.}
+@javaClass{org.rascalmpl.library.util.Monitor}
+java void jobStep(str label, str message, int work = 1);
+
+@javaClass{org.rascalmpl.library.util.Monitor}
+@synopsis{Log the end of a job}
+java int jobEnd(str label, bool success = true);
+
+@javaClass{org.rascalmpl.library.util.Monitor}
+@synopsis{Register additional work for the identified job.}
+java void jobTodo(str label, int work = 1);
+
+@javaClass{org.rascalmpl.library.util.Monitor}
+@synopsis{Poll if the given job has been cancelled by a user interaction.}
+java void jobIsCancelled(str label);
+
+@javaClass{org.rascalmpl.library.util.Monitor}
+@synopsis{Register a warning in the same UI that job progress is shown in.}
+java void jobWarning(str message, loc src);
+
+@synopsis{A job block guarantees a start and end, and provides easy access to the stepper interface.}
+@description{
+The convenience function that is passed to the block can be used inside the block to register steps
+with a parameterized workload and the same label as the job name.
+}
+@benefits{
+* the block body does not need to repeat the `name` parameter when ending the job or making steps
+* the job is always properly ended, even when exceptions are thrown
+}
+@pitfalls{
+* additional work with ((jobTodo)) is still possible, but you have to repeat the right job label.
+}
+&T job(
+    str label,
+    &T(void(str message, int worked) step) block,
+    int totalWork = 100
+) {
+    try {
+        jobStart(label, totalWork = totalWork);
+        return
+            block(void(str message, int worked ) {
+                jobStep(label, message, work = worked);
+            });
+    }
+    catch "Never caught": {
+        // This is only here because we cannot have a "finally" clause in Rascal without a catch
+        throw "Never caught";
+    }
+    finally {
+        jobEnd(label);
+    }
+}
+
+@synopsis{A job block guarantees a start and end, and provides easy access to the stepper interface.}
+@description{
+The convenience function that is passed to the block can be used inside the block to register steps
+with a parameterized workload and the same label as the job name.
+}
+@benefits{
+* the block body does not need to repeat the `name` parameter when ending the job or making steps
+* the job is always properly ended, even when exceptions are thrown
+}
+@pitfalls{
+* additional work with ((jobTodo)) is still possible, but you have to repeat the right job label.
+}
+&T job(str label, &T(void(int worked) step) block, int totalWork = 1) {
+    if (void(void(int _) _) _ := block) {
+        throw IllegalArgument(
+                  block,
+                  "`block` argument can not be used by job because it returns `void` and `job` must return something."
+              );
+    }
+    try {
+        jobStart(label, totalWork = totalWork);
+        return block(void(int worked ) {
+                   jobStep(label, label, work = worked);
+               });
+    }
+    catch "Never caught": {
+        // This is only here because we cannot have a "finally" clause in Rascal without a catch
+        throw "Never caught";
+    }
+    finally {
+        jobEnd(label);
+    }
+}
+
+@synopsis{A job block guarantees a start and end, and provides easy access to the stepper interface.}
+@description{
+The convenience function that is passed to the block can be used inside the block to register steps
+with workload `1` and the same label as the job name.
+}
+@benefits{
+* the block body does not need to repeat the `name` parameter when ending the job or making steps
+* the job is always properly ended, even when exceptions are thrown
+}
+@pitfalls{
+* additional work with ((jobTodo)) is still possible, but you have to repeat the right job label.
+}
+&T job(str label, &T(void() step) block, int totalWork = 1) {
+    try {
+        jobStart(label, totalWork = totalWork);
+        return block(void( ) {
+                   jobStep(label, label, work = 1);
+               });
+    }
+    catch "Never caught": {
+        // This is only here because we cannot have a "finally" clause in Rascal without a catch
+        throw "Never caught";
+    }
+    finally {
+        jobEnd(label);
+    }
+}
+
+@synopsis{A job block guarantees a start and end, and provides easy access to the stepper interface.}
+@benefits{
+* the block code does not need to remember to end the job with the same job name.
+* the job is always properly ended, even when exceptions are thrown
+}
+&T job(str label, &T() block, int totalWork = 1) {
+    try {
+        jobStart(label, totalWork = totalWork);
+        return block();
+    }
+    catch "Never caught": {
+        // This is only here because we cannot have a "finally" clause in Rascal without a catch
+        throw "Never caught";
+    }
+    finally {
+        jobEnd(label);
+    }
+}
+
+@synopsis{Puts the monitor API to work by racing 5 horses against each other.}
+test bool horseRaceTest() {
+    distance = 3000000;
+    stride = 50;
+    horses = 5;
+    handicaps = [arbInt(stride * 15 / 100) | _ <- [0..horses]];
+    labels = ["Horse  (handicap is )" | h <- [0..horses]];
+    progress = [0 | _ <- [0..horses]];
+    
+    for (int h <- [0..horses])
+        jobStart(labels[h], totalWork = distance);
+    
+    race :while(true)
+        for (int h <- [0..horses]) {
+            advance = arbInt(stride - handicaps[h]);
+            progress[h] += advance;
+            
+            jobStep(labels[h], "Pacing horse  with ...", work = advance);
+            
+            if (progress[h] >= distance) {
+                break race;
+            }
+        }
+    
+    for (int h <- [0..horses])
+        jobEnd(labels[h]);
+    
+    return true;
+}
+
+test bool simpleAsyncPrintTest() {
+    jobStart("job", totalWork = 3);
+    println("a");
+    jobStep("job", "step 1", work = 1);
+    println("b");
+    jobStep("job", "step 2", work = 1);
+    println("c");
+    jobStep("job", "step 3", work = 1);
+    println("d");
+    jobEnd("job");
+    return true;
+}
+
+test bool unfinishedInputTest() {
+    jobStart("job", totalWork = 26);
+    for (// := "abcdefghijklmnopqrstuwvxyz") {
+        print(l);
+        // no newline!
+        jobStep("job", "letter ", work = 1);
+        if (arbInt(10) == 0) {
+            println();
+        // break it
+        }
+    }
+    
+    // println(); // flush it
+    jobEnd("job");
+    return true;
+}
+
+test bool unfinishedLinesAtTheEndTest() {
+    jobStart("job", totalWork = 3);
+    print("ab\nc");
+    jobStep("job", "1.5", work = 1);
+    print("d\ne");
+    jobStep("job", "2.5", work = 1);
+    print("f\ngh\n");
+    jobStep("job", "3", work = 1);
+    jobEnd("job");
+    return true;
+}
+
+// repo of issue #2138
+test bool printLongUnfinishedLine() {
+    jobStart("job", totalWork = 1);
+    singleString = iprintToString(( "" | it + "ab" | _ <- [0..1000000] ));
+    // avoid concat tree printing in chunks
+    println(singleString);
+    jobStep("job", "prog", work = 1);
+    println("Done");
+    jobEnd("job");
+    return true;
+}
+### |project://rascal/src/org/rascalmpl/library/util/ParseErrorRecovery.rsc|
+/**
+ * Copyright (c) 2024-2025, NWO-I Centrum Wiskunde & Informatica (CWI)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ **/module util::ParseErrorRecovery
+
+import ParseTree;
+import String;
+
+@synopsis{Library functions for handling parse trees with error nodes.}
+@description{
+When parse functions are called with `errorRecovery=true`, the resulting parse trees can contain error and skipped nodes. For an in-depth description of these nodes see the `ParseTree` module.
+Functions in this module can be used to detect, isolate, and analyze these error nodes.
+Note that these are fairly low-level functions. Better high-level support for error trees will be added to Rascal and its standard library in the future.
+As such, this code should be considered experimental and used with care.
+}
+@synopsis{Check if a parse tree contains any error nodes, the result of error recovery.}
+@javaClass{org.rascalmpl.library.util.ParseErrorRecovery}
+java bool hasParseErrors(Tree tree);
+
+@javaClass{org.rascalmpl.library.util.ParseErrorRecovery}
+@synopsis{Find all error productions in a parse tree.
+Note that children of an error tree can contain errors themselves.
+The list of errors returned by this method is created by an outermost visit of the parse tree so if an error tree contains other errors the outermost tree is
+returned first.
+Often error trees are highly ambiguous and can contain a lot of error trees. This function is primarily used to analyze small examples as calling this function
+on a tree with many errors will result in long runtimes and out-of-memory errors.
+}
+java list[Tree] findAllParseErrors(Tree tree);
+
+@synopsis{Get the symbol (sort) of the failing production}
+Symbol getErrorSymbol(appl(error(Symbol sym, _, _), _)) = sym;
+
+@synopsis{Get the production that failed}
+Production getErrorProduction(appl(error(_, Production prod, _), _)) = prod;
+
+@synopsis{Get the dot (position in the production) of the failing element in a production}
+int getErrorDot(appl(error(_, _, int dot), _)) = dot;
+
+@synopsis{Get the skipped tree}
+Tree getSkipped(appl(error(_, _, _), [*_, skip: appl(skipped(_), _)])) = skip;
+
+@synopsis{Get the text that failed to parse. This is only the text of the part that has been skipped to be able to continue parsing.
+If you want the text of the whole error tree, you can just use string interpolation: `""`.
+}
+str getErrorText(appl(error(_, _, _), [*_, sk: appl(skipped(_), _)]))
+    = "";
+
+@javaClass{org.rascalmpl.library.util.ParseErrorRecovery}
+@synopsis{Error recovery often produces ambiguous trees where errors can be recovered in multiple ways.
+This filter removes error trees until no ambiguities caused by error recovery are left.
+Note that regular ambiguous trees remain in the parse forest unless `allowAmbiguity` is set to false in which case an error is thrown.
+This method uses simple and somewhat arbitrary heuristics, so its usefulness is limited.
+}
+java &T <: Tree disambiguateParseErrors(
+    &T <: Tree t,
+    bool allowAmbiguity = true
+);
+
+@synopsis{Disambiguate the error ambiguities in a tree and return the list of remaining errors. 
+The list is created by an outermost visit of the parse tree so if an error tree contains other errors the outermost tree is returned first.
+Error disambiguation is based on heuristics and are therefore unsuitable for many use-cases. We primarily use this functionality
+for testing and for presenting recovered errors in VSCode.
+}
+list[Tree] findBestParseErrors(Tree tree)
+    = findAllParseErrors(disambiguateParseErrors(tree));
+
+@synopsis{Create a parse filter based on `disambiguateErrors` with or without `allowAmbiguity`.}
+Tree(Tree) createParseErrorFilter(bool allowAmbiguity) = Tree( Tree t ) {
+    return disambiguateParseErrors(t, allowAmbiguity = allowAmbiguity);
+};
+
+@javaClass{org.rascalmpl.library.util.ParseErrorRecovery}
+java &T <: Tree pruneAmbiguities(&T <: Tree t, int maxDepth = 3);
+### |project://rascal/src/org/rascalmpl/library/util/PathConfig.rsc|
+@license{
+  Copyright (c) 2009-2015 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@synopsis{Standard intermediate storage format for configuring language processors (such as interpreters, type-checkers and compilers)}
+@description{
+The module offers the ((PathConfig)) datatype which standardizes the configuration of source code projects.
+Together with ((LanguageFileConfig)) automatic mappings from source files to target files, and back, can
+be computed.
+
+The following reusable invertible mapping functions are provided for the sake of convenience and
+for the sake of consistent interpretation of a ((PathConfig)) instance. We map fully qualified
+module names to their corresponding file locations on disk:
+
+| qualified module name (`str`) to file path (`loc`) | file path (`loc`) to qualified module name (`str`)  | ((PathConfig)) field used |
+| ----------------- |------------------- | -------------         | 
+| ((srcsFile))    | ((srcsModule))   | `srcs`                |
+| ((binFile))    | ((binModule))   | `bin`                 |
+| ((libsFile))   | ((libsModule))  | `libs`                | 
+}
+module util::PathConfig
+
+import Exception;
+import IO;
+import Location;
+import Message;
+import String;
+import util::UUID;
+
+@synopsis{General configuration (via locations) of a language processor.}
+@description{
+A PathConfig is the result of dependency resolution and other configuration steps. Typically,
+IDEs produce the information to fill a PathConfig, such that language tools can consume it
+transparently. A PathConfig is also a log of the configuration process. Typically a single
+((pathConfig)) instance configures the language processor for a single source project tree.
+
+* `projectRoot` is the root directory of the source project tree that is being configured.
+* `srcs` list of root directories to search for source files; to interpret or to compile.
+* `ignores` list of directories and files to not compile or not interpret (these are typically subtracted from the `srcs` tree, and/or skipped when the compiler arrives there.)
+* `bin` is the target root directory for the output of a compiler. Typically this directory would be linked into a zip or a jar or some other executable archive later.
+* `libs` is a list of binary dependencies (typically jar files or bin folders) on other projects, for checking and linking purposes. Each entry is expected to return `true` for ((isDirectory)).
+* `resources` is a list of files or folders that will be copied *by the compiler* to the bin folder, synchronized with its other (binary) output files..
+* `messages` is a list of info, warning and error messages informing end-users about the quality of the configuration process. Typically missing dependencies would be reported here, and clashing versions.
+}
+@benefits{
+* `main` functions which have a keyword parameter of type ((PathConfig)) are automatically augmented with commandline parameters for every field of ((PathConfig))
+* `messages` can be printed in a standard way using ((mainMessageHandler))
+* ((PathConfig)) is a reusable bridge between language processing functions and different execution environments such as VScode, the commandline or Maven.
+* ((PathConfig)) makes all configuration processors of file-based language processors explicit and transparent
+* ((PathConfig)) is programming language and domain-specific language independent
+* This module contains *bidirectional* transformation functions between fully qualified module names and their file locations in source folders and library dependencies.
+}
+data PathConfig
+    = pathConfig(
+          loc projectRoot = |unknown:///|,
+          list[loc] srcs = [],
+          list[loc] ignores = [],
+          loc bin = |unknown:///|,
+          list[loc] resources = [],
+          list[loc] libs = [],
+          list[Message] messages = []);
+
+@synopsis{Defines the parameters of mappings between qualified module names and source, target, and library files.}
+@description{
+For most languages a single `fileConfig()` instance is enough to define:
+* the mapping from source files and source folders to fully qualified module names, and back: ((srcsModule)) and ((srcsFile))
+* the mapping from binary library files to fully qualified module names and back: ((libsModule)) and ((libsFile))
+* the mapping from source files to target files in the bin folder, and back: ((binFile)) and ((binModule))
+
+Together with a ((PathConfig)) instance, the above six functions can be re-used to build a language processor that supports:
+* execution (testing) of generated files from the `bin` folder, using `libs` as run-time dependencies
+* using binary compile-time libraries, using `libs` to find binary interfaces to previously generated targets
+* packaging binary (generated) files as `jar` files to be re-used later as `libs` dependencies
+* modular language processors that work incrementally per changed source file or changed dependency
+}
+@benefits{
+* one ((fileConfig)) constant can be reused for configure all six different mapping functions.  
+* a simple `fileConfig()` constant is configured for the Rascal compiler by default (.tpl files as binary extension).
+* the mapping functions that use ((LanguageFileConfig)) can always use the same ((PathConfig)) instance for one project.
+* more complex mappings can be made by combining the six functions. For example first retrieving the module name using `srcsModule` and then 
+seeing if it exists also in one of the libraries using `libsFile`.
+}
+@pitfalls{
+* If a compiler produces multiple target files from a single source file, then you might have to configure
+different instances of ((fileConfig)) for every target `binExt`.
+* If the mapping between qualified module names and source files or binary files is different ---it has more parameters than defined by ((LanguageFileConfig))--- then you have to write your own
+versions of ((srcsModule)), ((srcsFile)), ((libsModule)), ((libsFile)), ((binFile)) and ((binModule)).
+}
+data LanguageFileConfig
+    = fileConfig(
+          str packageSep = "::",
+          str binExt = "tpl",
+          str targetRoot = "rascal",
+          str targetEsc = "$",
+          str srcsExt = "rsc");
+
+@synopsis{Produces the latest up-to-date file to load for a given module name, searching in the bin folder, the srcs folder and the libraries.}
+@description{
+We find the right file to source for the given `moduleName`:
+1. If the binary target file is younger than the source file, the binary target wins
+2. If a binary target is found, without a corresponding source unit, we try the libraries instead because a source module can have been deleted.
+3. If a source file is found, without a binary target, this source file is returned.
+4. Otherwise we search in the libraries for a binary file and return it.
+5. We throw ((PathNotFound)) if a module can not be resolved using either the bin, srcs, or libs, and also
+if the only place we found the module in was a bin folder (signifying a deleted source module). 
+
+In other words, ((latestModuleFile)) prefers newer binaries over older source files, and source files over library modules.
+If a module is present in both libraries and the current project, then the current project's sources shadow the libraries.
+
+This function is based on the core features of ((srcsFile)), ((binFile)), and ((libsFile)).
+}
+@benefits{
+* Finicky issues with file IO are dealt with here in a language parametric way, based on ((LanguageFileConfig)) and ((relativize)).
+* Provides the basic setup for creating a programming language or DSL with independent libraries/components to depend on.
+* Use `returnValue.extension == fcfg.binExt` to detect if you need to read a binary or parse the source code.
+* The `PathNotFound` exception should be caught by the code that processes `import` statements in your language.
+}
+loc latestModuleFile(
+    str qualifiedModuleName, PathConfig pcfg, LanguageFileConfig fcfg
+) throws PathNotFound {
+    loc source(str name) {
+        try {
+            return srcsFile(name, pcfg, fcfg);
+        }
+        catch PathNotFound(_):
+            return |notFound:///|;
+    }
+    loc target(str name) {
+        try {
+            return binFile(name, pcfg, fcfg);
+        }
+        catch PathNotFound(_):
+            return |notFound:///|;
+    }
+    
+    switch() {
+        case <|notFound:///|, |notFound:///|>:
+            return libsFile(qualifiedModuleName, pcfg, fcfg);
+        case <|notFound:///|, loc _tgt>:
+            return libsFile(qualifiedModuleName, pcfg, fcfg);
+        case <loc src, |notFound:///|>:
+            return src;
+        case <loc src, loc tgt>:
+            return lastModified(src) > lastModified(tgt) ? src : tgt;
+    }
+    
+    assert false : "unreachable code";
+    throw PathNotFound(|module:///| + qualifiedModuleName);
+}
+
+@synopsis{Compute a fully qualified module name for a module file, relative to the source roots of a project}
+@description{
+* ((srcsModule)) is the inverse of ((srcsFile))
+}
+str srcsModule(
+    loc moduleFile, PathConfig pcfg, LanguageFileConfig fcfg
+) throws PathNotFound
+    = srcsModule(moduleFile, pcfg.srcs, fcfg);
+
+str srcsModule(
+    loc moduleFile, list[loc] srcs, LanguageFileConfig fcfg
+) throws PathNotFound {
+    loc relative = relativize(srcs, moduleFile);
+    relative.extension = "";
+    
+    return replaceAll(relative.path[1..], "/", fcfg.packageSep);
+}
+
+@synopsis{Compute a fully qualified module name for a library file, relative to the library roots of a project}
+@description{
+* ((libsModule)) is the inverse of ((libsFile))
+}
+str libsModule(
+    loc libsFile, PathConfig pcfg, LanguageFileConfig fcfg
+) throws PathNotFound
+    = libsModule(libsFile, pcfg.libs, fcfg);
+
+str libsModule(
+    loc libsFile, list[loc] libs, LanguageFileConfig fcfg
+) throws PathNotFound {
+    loc relative = relativize(libs, libsFile);
+    
+    relative.file = relative.file[size(fcfg.targetEsc)..];
+    relative.path = relative.path[1 + size(fcfg.targetRoot)..];
+    relative.extension = "";
+    
+    return replaceAll(relative.path[1..], "/", fcfg.packageSep);
+}
+
+@synopsis{Find out in which library file a module was implemented.}
+@description{
+* ((libsFile)) is the inverse of ((libsModule))
+* the computed file has to exist in at least one of the library modules. Otherwise ((PathNotFound)) is thrown.
+}
+loc libsFile(
+    str qualifiedModuleName, PathConfig pcfg, LanguageFileConfig fcfg
+) throws PathNotFound
+    = libsFile(qualifiedModuleName, pcfg.libs, fcfg);
+
+loc libsFile(
+    str qualifiedModuleName, list[loc] libs, LanguageFileConfig fcfg
+) throws PathNotFound {
+    loc relativeFile
+        = |relative:///|
+        + fcfg.targetRoot
+        + replaceAll(qualifiedModuleName, fcfg.packageSep, "/");
+    relativeFile.extension = fcfg.binExt;
+    relativeFile.file = "";
+    
+    return resolve(libs, relativeFile);
+}
+
+@synopsis{Find out in which source file a module was implemented.}
+@description{
+* ((srcsFile)) is the inverse of ((srcsModule))
+* throws ((PathNotFound)) if the designated source file does not exist, unless `force == true`.
+* if `force` then the first element of the `srcs` path is used as parent to the new module file.
+}
+loc srcsFile(
+    str qualifiedModuleName,
+    PathConfig pcfg,
+    LanguageFileConfig fcfg,
+    bool force = false
+) throws PathNotFound
+    = srcsFile(qualifiedModuleName, pcfg.srcs, fcfg, force = force);
+
+loc srcsFile(
+    str qualifiedModuleName,
+    list[loc] srcs,
+    LanguageFileConfig fcfg,
+    bool force = false
+) throws PathNotFound {
+    loc relative
+        = |relative:///| + replaceAll(qualifiedModuleName, fcfg.packageSep, "/");
+    relative.extension = fcfg.srcsExt;
+    return resolve(srcs, relative, force = force);
+}
+
+@synopsis{Compute the binary file location for a fully qualified source module name}
+@description{
+* ((binFile)) is the inverse of ((binModule)).
+* the returned target location does not have to exist yet.
+}
+loc binFile(str srcsModule, PathConfig pcfg, LanguageFileConfig fcfg)
+    = binFile(srcsModule, pcfg.bin, fcfg);
+
+@synopsis{Compute a target file name for a generated folder with a given extension}
+loc binFile(str srcsModule, loc generated, LanguageFileConfig fcfg) {
+    relative = |relative:///| + replaceAll(srcsModule, fcfg.packageSep, "/");
+    relative.file = "";
+    relative.extension = fcfg.binExt;
+    
+    return generated + fcfg.targetRoot + relative.path;
+}
+
+@synopsis{Computing a fully qualified module name back from a file in the bin folder}
+@description{
+* ((binModule)) is the inverse of ((binFile))
+}
+str binModule(
+    loc binFile, PathConfig pcfg, LanguageFileConfig fcfg
+) throws PathNotFound
+    = binModule(binFile, pcfg.bin, fcfg);
+
+@synopsis{Recovers the original module name back from a file that was generated.}
+str binModule(
+    loc targetFile, loc bin, LanguageFileConfig fcfg
+) throws PathNotFound {
+    relative = relativize(bin, targetFile);
+    relative.extension = "";
+    relative.file = relative.file[size(fcfg.targetEsc)..];
+    relative.path = relative.path[1 + size(fcfg.targetRoot)..];
+    
+    return replaceAll(relative.path[1..], "/", fcfg.packageSep);
+}
+
+// below we have some core tests of the above features
+private loc testLibraryLoc = |memory://myTestLibrary-< uuid().authority >/| ;
+
+test bool inverseBinFileModule() {
+    pcfg
+        = pathConfig(
+              bin = testLibraryLoc + "target/classes",
+              srcs = [|project://rascal/src/org/rascalmpl/library/|]
+          );
+    fcfg = fileConfig();
+    
+    tgt = binFile("util::Monitor", pcfg, fcfg);
+    writeFile(tgt, "blabla");
+    
+    return binModule(tgt, pcfg, fcfg) == "util::Monitor";
+}
+
+test bool inverseSrcsFileModule() {
+    pcfg
+        = pathConfig(
+              bin = testLibraryLoc + "target/classes",
+              srcs = [testLibraryLoc + "src/main/rascal"]
+          );
+    
+    writeFile(pcfg.srcs[0] + "util/Monitor.rsc", "module util::Monitor");
+    fcfg = fileConfig();
+    
+    src = srcsFile("util::Monitor", pcfg, fcfg);
+    
+    return srcsModule(src, pcfg, fcfg) == "util::Monitor";
+}
+
+test bool inverseLibsFileModule() {
+    pcfg
+        = pathConfig(
+              bin = testLibraryLoc + "target/classes", libs = [testLibraryLoc + "libs"]
+          );
+    fcfg = fileConfig(binExt = "tpl");
+    
+    writeFile(testLibraryLoc + "libs" + "rascal/util/$Monitor.tpl", "blabla");
+    lib = libsFile("util::Monitor", pcfg, fcfg);
+    
+    return libsModule(lib, pcfg, fcfg) == "util::Monitor";
+}
+
+test bool moduleExceptionWithSrc() {
+    pcfg = pathConfig(srcs = [|project://rascal/src/org/rascalmpl/library/|]);
+    fcfg = fileConfig();
+    return
+        srcsModule(
+            |project://rascal/src/org/rascalmpl/library/Exception.rsc|, pcfg, fcfg
+        ) == "Exception";
+}
+
+test bool moduleReflectiveWithSrc() {
+    pcfg = pathConfig(srcs = [|project://rascal/src/org/rascalmpl/library/|]);
+    fcfg = fileConfig();
+    
+    return
+        srcsModule(
+            |project://rascal/src/org/rascalmpl/library/util/Reflective.rsc|,
+            pcfg,
+            fcfg
+        ) == "util::Reflective";
+}
+
+test bool moduleExceptionOnlyTplModule() {
+    tplFile = testLibraryLoc + "/lib/rascal/$Exception.tpl";
+    writeFile(
+        tplFile, "$Exception.tpl (only file matters, content irrelevant)"
+    );
+    pcfg = pathConfig(libs = [testLibraryLoc + "/lib/"]);
+    fcfg = fileConfig();
+    
+    return libsModule(tplFile, pcfg, fcfg) == "Exception";
+}
+
+test bool moduleExceptionOnlyTplFile() {
+    tplFile = testLibraryLoc + "/lib/rascal/$Exception.tpl";
+    writeFile(
+        tplFile, "$Exception.tpl (only file matters, content irrelevant)"
+    );
+    pcfg = pathConfig(libs = [testLibraryLoc + "/lib/"]);
+    fcfg = fileConfig(binExt = "tpl");
+    
+    return libsFile("Exception", pcfg, fcfg) == tplFile;
+}
+
+test bool moduleReflectiveOnlyTplModule() {
+    writeFile(
+        testLibraryLoc + "/libs/rascal/util/$Reflective.tpl",
+        "util::Reflective (only file matters, content irrelevant)"
+    );
+    pcfg = pathConfig(srcs = [], libs = [testLibraryLoc + "libs"]);
+    fcfg = fileConfig();
+    
+    return
+        libsModule(
+            testLibraryLoc + "libs/rascal/util/$Reflective.tpl", pcfg, fcfg
+        ) == "util::Reflective";
+}
+
+test bool moduleReflectiveOnlyTplFile() {
+    libFile = testLibraryLoc + "/libs/rascal/util/$Reflective.tpl";
+    writeFile(
+        libFile,
+        "util::$Reflective.tpl (only file matters, content irrelevant)"
+    );
+    pcfg = pathConfig(srcs = [], libs = [testLibraryLoc + "libs"]);
+    fcfg = fileConfig(binExt = "tpl");
+    
+    return libsFile("util::Reflective", pcfg, fcfg) == libFile;
+}
+
+test bool longestModuleReflectiveOnlyTpl() {
+    writeFile(
+        testLibraryLoc + "/1/libs/rascal/$Reflective.tpl",
+        "$Reflective.tpl at top level (only file matters, content irrelevant)"
+    );
+    writeFile(
+        testLibraryLoc + "/2/libs/rascal/util/$Reflective.tpl",
+        "util::$Reflective.tpl in subdir util (only file matters, content irrelevant)"
+    );
+    pcfg
+        = pathConfig(
+              srcs = [], libs = [testLibraryLoc + "1/libs", testLibraryLoc + "2/libs"]
+          );
+    fcfg = fileConfig(binExt = "tpl");
+    return
+        libsFile("util::Reflective", pcfg, fcfg) == testLibraryLoc + "2/libs/rascal/util/$Reflective.tpl";
+}
+
+test bool moduleOnlyInSecondSrc() {
+    testLibrarySrc = testLibraryLoc + "src/org/rascalmpl/library/";
+    ESrc = testLibrarySrc + "E.rsc";
+    writeFile(ESrc, "module E");
+    
+    pcfg
+        = pathConfig(
+              srcs = [|project://rascal/src/org/rascalmpl/library/|, testLibrarySrc]
+          );
+    fcfg = fileConfig();
+    return srcsModule(ESrc, pcfg, fcfg) == "E";
+}
+### |project://rascal/src/org/rascalmpl/library/util/PriorityQueue.rsc|
+@license{
+  Copyright (c) 2009-2015 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI}
+@contributor{Paul Klint - Paul.Klint@cwi.nl - CWI}
+@synopsis{A `PriorityQueue` datatype and associated functions.}
+@usage{
+`import util::PriorityQueue;`
+}
+@description{
+Priority queues maintain (priority, value) pairs in sorted order. They are implemented using a
+[Binomial heap](http://en.wikipedia.org/wiki/Binomial_heap) Priority queue are, for instance, used to implement shortest path algorithms.
+
+Provides the following functions:
+(((TOC)))
+}
+@examples{
+
+}
+@benefits{
+
+}
+@pitfalls{
+Currently, both priority and associated value ("payload") have to be integers. This will be generalized.
+}
+module util::PriorityQueue
+
+/*
+ * Priority queues maintain (value, priority) pairs in sorted order.
+ * They are implemented using a binomial heap, see http://en.wikipedia.org/wiki/Binomial_heap
+ * The following operations are provided:
+ * - PriorityQueue priorityQueue():
+ *		create an empty queue.
+ * - PriorityeQueue priorityQueue(int priority, int val):
+ *		create queue with one pair.
+ * - bool isEmpty(PriorityQueue Q):
+ *		test for empty queue.
+ * - PriorityQueue insertElement(PriorityQueue Q, int priority, int val):
+ *		insert pair in queue.
+ * - int findMinimum(PriorityQueue Q):
+ *		find the minimum priority.
+ * - tuple[int, int, PriorityQueue] extractMinimum(PriorityQueue Q):
+ *		find the pair with minimum priority and delete it.
+ *//*
+  * TODO: the value in each pair is now an int but should become &T.
+ */import util::Math;
+import List;
+
+// Binomial Trees
+private data BinomialTree
+    = binomialTree(int priority, // priority of this tree
+                                int val, // payload
+                                         int degree, // degree of tree
+                                                     list[BinomialTree] children// subtrees
+                                                                                 );
+
+private BinomialTree addSubTree(BinomialTree p, BinomialTree q) {
+    return binomialTree(p.priority, p.val, p.degree + 1, p.children + [q]);
+}
+
+private BinomialTree mergeTree(BinomialTree p, BinomialTree q) {
+    return (p.priority <= q.priority) ? addSubTree(p, q) : addSubTree(q, p);
+}
+
+private str toString(BinomialTree T) {
+    str res = "[" + toString(T.priority) + "/" + toString(T.val);
+    if (!isEmpty(T.children))
+        res = res + ":";
+    for (BinomialTree child <- T.children) {
+        res = res + " " + toString(child);
+    }
+    return res + "]";
+}
+
+// Priority Queues implemented as Binomial Heap
+data PriorityQueue = priorityQueue(list[BinomialTree] trees, // trees in the heap
+                                                            int minIndex// index of minimal tree
+                                                                         );
+
+public PriorityQueue mkPriorityQueue() {
+    return priorityQueue([], -1);
+}
+
+public PriorityQueue mkPriorityQueue(int priority, int val) {
+    return priorityQueue([binomialTree (
+                              priority,
+                              val,
+                              0,
+                              []
+                          )], 0);
+}
+
+public bool isEmpty(PriorityQueue Q) {
+    return size(Q.trees) == 0;
+}
+
+public PriorityQueue insertElement(PriorityQueue Q, int priority, int val) {
+    return mergeQueue(Q, mkPriorityQueue(priority, val));
+}
+
+public int findMinimum(PriorityQueue Q) {
+    return Q.trees[Q.minIndex].priority;
+// throw exception for empty queue.
+}
+
+public tuple[int, int, PriorityQueue] extractMinimum(PriorityQueue Q) {
+    minTree = Q.trees[Q.minIndex];
+    
+    Q.trees = delete(Q.trees, Q.minIndex);
+    
+    // Determine the new minimal tree
+    int minIndex = -1;
+    int minPrio = 10000;
+    for (int i <- index(Q.trees)) {
+        if (Q.trees[i].priority < minPrio) {
+            minPrio = Q.trees[i].priority;
+            minIndex = i;
+        }
+    }
+    Q.minIndex = minIndex;
+    
+    return
+        ;
+}
+
+public str toString(PriorityQueue Q) {
+    str res = "(";
+    for (int i <- index(Q.trees)) {
+        res = res + "\n" + toString(i) + ":" + toString(Q.trees[i]);
+    }
+    return res + "\n)";
+}
+
+private int minPrio = 100000 ;
+private int minIndexFromEnd = -1 ;
+
+private list[BinomialTree] add(list[BinomialTree] heap, BinomialTree t) {
+    if (isEmpty(heap)) {
+        if (t.priority == minPrio)
+            minIndexFromEnd = 0;
+        return [t];
+    }
+    else if (head(heap).degree == t.degree) {
+        m = mergeTree(head(heap), t);
+        if (m.priority == minPrio) {
+            minIndexFromEnd = size(heap);
+        }
+        return [m, *tail(heap)];
+    }
+    else {
+        if (t.priority == minPrio)
+            minIndexFromEnd = size(heap);
+        return [t, *heap];
+    }
+}
+
+private PriorityQueue mergeQueue(PriorityQueue p, PriorityQueue q) {
+    pTrees = p.trees;
+    if (isEmpty(pTrees))
+        return q;
+    qTrees = q.trees;
+    if (isEmpty(qTrees))
+        return p;
+    int fromEnd = -1;
+    
+    // index of smallest tree from end
+    if (pTrees[p.minIndex].priority <= qTrees[q.minIndex].priority) {
+        minPrio = pTrees[p.minIndex].priority;
+        fromEnd = size(pTrees) - p.minIndex;
+    }
+    else {
+        minPrio = qTrees[q.minIndex].priority;
+        fromEnd = size(qTrees) - q.minIndex;
+    }
+    minIndexFromEnd = -1;
+    
+    list[BinomialTree] heapTrees = [];
+    
+    while(!isEmpty(pTrees) && !isEmpty(qTrees)) {
+        hp = head(pTrees);
+        hq = head(qTrees);
+        if (hp.degree < hq.degree) {
+            heapTrees = add(heapTrees, hp);
+            pTrees = tail(pTrees);
+        }
+        else if (hp.degree == hq.degree) {
+            heapTrees = add(heapTrees, mergeTree(hp, hq));
+            pTrees = tail(pTrees);
+            qTrees = tail(qTrees);
+        }
+        else {
+            heapTrees = add(heapTrees, hq);
+            qTrees = tail(qTrees);
+        }
+    }
+    rest = isEmpty(pTrees) ? qTrees : pTrees;
+    heapTrees = rest + heapTrees;
+    
+    int min = -1;
+    
+    if (minIndexFromEnd == -1) {
+        // Minimal element appears in rest
+        min = fromEnd;
+    }
+    else {
+        // Minimal element already seen.
+        min = size(heapTrees) - minIndexFromEnd - 1;
+    }
+    
+    //println("heapTrees=");
+    //println("minIndexFromEnd=, minPrio=, min=");
+    return priorityQueue(heapTrees, min);
+}
+### |project://rascal/src/org/rascalmpl/library/util/Progress.rsc|
+@license{
+  Copyright (c) 2009-2015 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Jouke Stoel - jouke.stoel@cwi.nl - CWI}
+@deprecated{Use util::Monitor for the same effect with more support for different IDEs and commandline environments.}
+module util::Progress
+
+import String;
+import IO;
+import util::Math;
+
+@synopsis{This progress bar can be used in terminal applications to show the progress of some process in the terminal.}
+@description{
+The total number of steps is the only required parameter to be passed in. All other parameters are optional.
+   - `prefix` is the string that is displayed in front of the progress bar (default "").
+   - `length` is the length (number of characters) of the displayed bar (default 50).
+   - `limit` allows for the throttling of the number of times the progress bar is printed. For instance if the total is 1000 and the limit is set to 100 then the progress bar will be updated every 10 iterations.
+   - `fill` is the character used for the percentage used (default "\u2588").
+   - `unfill` is the character used for the unused part (default "-").
+   - `printEnd` is the character used at the end of the line (default "\r").
+   
+  The return is a tuple with 2 functions, the `report` and the `finished` function.
+  - `report(str suffix)` needs to be called for every iteration update. The suffix is displayed after the progress bar and can differ per iteration
+  - `finished()` can be called at the end of the iteration to add a new line to the terminal  
+
+  It is inspired on the progress bar described here: https://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console
+}
+tuple[void(str) report, void() finished] progressBar(
+    int total,
+    str prefix = "Progress:",
+    int length = 50,
+    int limit = total,
+    str fill = "\u2588",
+    str unfill = "-",
+    str printEnd = "\r"
+) {
+    limit = limit > total ? total : limit;
+    
+    int iteration = 0;
+    int showAt = floor(total / limit);
+    
+    return
+        <void
+         (str suffix){
+             iteration += 1;
+             
+             if (iteration % showAt == 0 || iteration == total) {
+                 int perc = percent(iteration, total);
+                 int filled = floor((length * iteration) / total);
+                 print(
+                     "\r || % "
+                 );
+             }
+         },
+         void (){ println(); }>;
+}
+
+@synopsis{Simple spinner to display progress for some terminal process for which the total number of steps is not known.}
+@description{
+`prefix` - Contains the string displayed in front the spinner (default " ").
+     
+   It returns a function that can be called to make the spinner spin one rotation.
+   This function takes a `suffix` string parameter that will be displayed behind the spinner
+}
+void(str) spinner(str prefix = " ", str printEnd = "\r") {
+    int stat = 0;
+    
+    return
+        void
+        (str suffix){
+            switch(stat) {
+                case 0:
+                    print("\r\\  ");
+                case 1:
+                    print("\r|  ");
+                case 2:
+                    print("\r/  ");
+                case 3:
+                    print("\r-  ");
+            }
+            
+            stat = (stat + 1) % 4;
+        };
+}
+### |project://rascal/src/org/rascalmpl/library/util/REPL.rsc|
+module util::REPL
+
+extend Content;
+
+alias Completion = map[str completion, str group];
+
+data REPL
+    = repl(
+          str title = "",
+          str welcome = "",
+          str prompt = "\n\>",
+          str quit = "",
+          loc history = |home:///.term-repl-history|,
+          Content(str command) handler = echo,
+          Completion(str line, str word) completor = noSuggestions,
+          str() stacktrace = str (){ return ""; });
+
+private Content echo(str line) = plainText(line);
+
+private Completion noSuggestions(str _, str _) = ();
+
+alias Terminal = tuple[void() run, void(str) send];
+
+@javaClass{org.rascalmpl.library.util.TermREPL}
+@reflect{
+Makes closures
+}
+java Terminal newREPL(
+    REPL repl,
+    // filling in defaults from the repl constructor, for use in the Java code:
+    str title = repl.title,
+    str welcome = repl.welcome,
+    str prompt = repl.prompt,
+    str quit = repl.quit,
+    loc history = repl.history,
+    Content(str) handler = repl.handler,
+    Completion(str, str) completor = repl.completor,
+    str() stacktrace = repl.stacktrace
+);
+
+void startREPL(
+    REPL repl,
+    // filling in defaults from the repl constructor, for use in the Java code:
+    str title = repl.title,
+    str welcome = repl.welcome,
+    str prompt = repl.prompt,
+    str quit = repl.quit,
+    loc history = repl.history,
+    Content(str) handler = repl.handler,
+    Completion(str, str) completor = repl.completor,
+    str() stacktrace = repl.stacktrace
+) {
+    Terminal tm
+        = newREPL(
+              repl,
+              title = title,
+              welcome = welcome,
+              prompt = prompt,
+              quit = quit,
+              history = history,
+              handler = handler,
+              completor = completor,
+              stacktrace = stacktrace
+          );
+    tm.run();
+}
+### |project://rascal/src/org/rascalmpl/library/util/Random.rsc|
+@license{
+  Copyright (c) 2019 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Davy Landman}
+@contributor{Jurgen J. Vinju}
+module util::Random
+
+@synopsis{Get a random value of a certain type}
+@javaClass{org.rascalmpl.library.Prelude}
+java &T randomValue(type[&T] ofType, int depth = 5, int width = 5);
+
+@synopsis{Get a random value of a certain type}
+@javaClass{org.rascalmpl.library.Prelude}
+java &T randomValue(type[&T] ofType, int seed, int depth = 5, int width = 5);
+### |project://rascal/src/org/rascalmpl/library/util/Reflective.rsc|
+@license{
+  Copyright (c) 2009-2015 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI}
+@contributor{Tijs van der Storm - Tijs.van.der.Storm@cwi.nl}
+@contributor{Mark Hills - Mark.Hills@cwi.nl (CWI)}
+@contributor{Paul Klint - Paul.Klint@cwi.nl (CWI)}
+@bootstrapParser
+module util::Reflective
+
+extend util::PathConfig;
+
+import IO;
+import List;
+import ParseTree;
+import String;
+import util::FileSystem;
+import Message;
+
+import lang::rascal::\syntax::Rascal;
+import lang::manifest::IO;
+
+@synopsis{Returns the system-dependent line separator string}
+@javaClass{org.rascalmpl.library.util.Reflective}
+public java str getLineSeparator();
+
+@javaClass{org.rascalmpl.library.util.Reflective}
+public
+java lrel[str result, str out, str err] evalCommands(
+    list[str] command, loc org
+);
+
+@synopsis{Just parse a module at a given location without any further processing (i.e., fragment parsing) or side-effects (e.g. module loading)}
+public
+lang::rascal::\syntax::Rascal::Module parseModule(loc location)
+    = parseModuleWithSpaces(location).top;
+
+@synopsis{Parse a module (including surrounding spaces) at a given location without any further processing (i.e., fragment parsing) or side-effects (e.g. module loading)}
+@javaClass{org.rascalmpl.library.util.Reflective}
+public java start[Module] parseModuleWithSpaces(loc location);
+
+data RascalConfigMode
+    = compiler()
+    | interpreter()
+    ;
+
+@deprecated{not in use anymore}
+data RascalManifest
+    = rascalManifest(
+          str \Project-Name = "Project",
+          str \Main-Module = "Plugin",
+          str \Main-Function = "main",
+          list[str] Source = ["src"],
+          str Bin = "bin",
+          list[str] \Required-Libraries = [],
+          list[str] \Required-Dependencies = []);
+
+@deprecated{not in use anymore}
+data JavaBundleManifest
+    = javaManifest(
+          str \Manifest-Version = "",
+          str \Bundle-SymbolicName = "",
+          str \Bundle-RequiredExecutionEnvironment = "JavaSE-1.8",
+          list[str] \Require-Bundle = [],
+          str \Bundle-Version = "0.0.0.qualifier",
+          list[str] \Export-Package = [],
+          str \Bundle-Vendor = "",
+          str \Bundle-Name = "",
+          list[str] \Bundle-ClassPath = [],
+          list[str] \Import-Package = []);
+
+@synopsis{Makes the location of a jar file explicit, based on the project name}
+@description{
+The classpath of the current JVM is searched and jar files are searched that contain
+META-INF/MANIFEST.MF file that match the given `projectName`.
+}
+@benefits{
+* This is typically used to link bootstrap libraries such as rascal.jar and rascal-lsp.jar
+into testing ((PathConfig))s.
+* The classpath is not used implicitly in this way, but rather explicitly. This helps
+in making configuration issues tractable.
+* The resulting `loc` value can be used to configure a ((PathConfig)) instance directly.
+}
+@javaClass{org.rascalmpl.library.util.Reflective}
+java loc resolveProjectOnClasspath(str projectName);
+
+@javaClass{org.rascalmpl.library.util.Reflective}
+@synopsis{Search a module name in the interpreter's search path}
+@description{
+This exists to help debugging interactive loading and reloading of Rascal modules
+by the REPL and the interpreter. It does not work in a compiled context.
+}
+@pitfalls{
+This is internal information for the interpreter and should not be used by client code.
+Only for debugging purposes of the interpreter and the REPL.
+}
+java loc resolveModuleOnCurrentInterpreterSearchPath(str moduleName);
+
+@synopsis{Makes the location of the currently running rascal jar explicit.}
+loc resolvedCurrentRascalJar() = resolveProjectOnClasspath("rascal");
+
+loc metafile(loc l) = l + "META-INF/RASCAL.MF";
+
+@synopsis{Converts a PathConfig and replaces all references to roots of projects or bundles
+  by the folders which are nested under these roots as configured in their respective
+  META-INF/RASCAL.MF files.}
+@deprecated{Not in use anymore.}
+PathConfig applyManifests(PathConfig cfg) {
+    mf
+        = (l: readManifest(#RascalManifest, metafile(l))
+          | l <- cfg.srcs + cfg.libs + [cfg.bin], exists(metafile(l))
+          );
+    
+    list[loc] expandSrcs(loc p) = [p + s | s <- mf[p].Source]
+        when mf[p]?;
+    default list[loc] expandSrcs(loc p, str _) = [p];
+    
+    list[loc] expandlibs(loc p) = [p + s | s <- mf[p].\Required-Libraries]
+        when mf[p]?;
+    default list[loc] expandlibs(loc p, str _) = [p];
+    
+    loc expandBin(loc p) = p + mf[p].Bin
+        when mf[p]?;
+    default loc expandBin(loc p) = p;
+    
+    cfg.srcs = [*expandSrcs(p) | p <- cfg.srcs];
+    cfg.libs = [*expandlibs(p) | p <- cfg.libs];
+    cfg.bin = expandBin(cfg.bin);
+    
+    return cfg;
+}
+
+@deprecated{Function will be moved to Rascal compiler}
+str makeFileName(str qualifiedModuleName, str extension = "rsc") {
+    str qnameSlashes = replaceAll(qualifiedModuleName, "::", "/");
+    int n = findLast(qnameSlashes, "/");
+    str prefix = extension == "rsc" ? "" : "$";
+    str package = extension == "rsc" ? "" : "rascal/";
+    qnameSlashes
+        = n < 0
+              ? "" + qnameSlashes
+              : qnameSlashes[0..n] + "/" + qnameSlashes[n + 1..];
+    return "">";
+}
+
+@deprecated{Function will be moved to Rascal compiler}
+loc getSearchPathLoc(str filePath, PathConfig pcfg) {
+    for (loc dir <- pcfg.srcs + pcfg.libs) {
+        fileLoc = dir + filePath;
+        if (exists(fileLoc)) {
+            //println("getModuleLocation  =\> ");
+            return fileLoc;
+        }
+    }
+    throw "Module with path  not found";
+}
+
+@deprecated{Function will be moved to Rascal compiler}
+@synopsis{Get the location of a named module, search for `src` in srcs and `tpl` in libs}
+loc getModuleLocation(str qualifiedModuleName, PathConfig pcfg) {
+    fileName = makeFileName(qualifiedModuleName, extension = "rsc");
+    for (loc dir <- pcfg.srcs) {
+        fileLoc = dir + fileName;
+        if (exists(fileLoc)) {
+            return fileLoc;
+        }
+    }
+    fileName = makeFileName(qualifiedModuleName, extension = "tpl");
+    for (loc dir <- pcfg.libs) {
+        fileLoc = dir + fileName;
+        
+        if (exists(fileLoc)) {
+            return fileLoc;
+        }
+    }
+    throw "Module `` not found;\n";
+}
+
+@deprecated{Function will be moved to Rascal compiler}
+tuple[str, str] splitFileExtension(str path) {
+    int n = findLast(path, ".");
+    if (n < 0)
+        return ;
+    return ;
+}
+
+@deprecated{Function will be moved to Rascal compiler}
+@synopsis{Determine length of common suffix of list of strings}
+int commonSuffix(list[str] dir, list[str] m)
+    = commonPrefix(reverse(dir), reverse(m));
+
+@deprecated{Function will be moved to Rascal compiler}
+@synopsis{Determine length of common prefix of list of strings}
+int commonPrefix(list[str] rdir, list[str] rm) {
+    for (int i <- index(rm)) {
+        if (i >= size(rdir)) {
+            return i;
+        }
+        else if (rdir[i] != rm[i]) {
+            return i;
+        }
+        else {
+            continue;
+        }
+    }
+    return size(rm);
+}
+
+@deprecated{Function will be moved to Rascal compiler}
+@synopsis{Find the module name corresponding to a given module location via its (src or tpl) location}
+str getModuleName(loc moduleLoc, PathConfig pcfg) {
+    modulePath = moduleLoc.path;
+    
+    rscFile = endsWith(modulePath, "rsc");
+    tplFile = endsWith(modulePath, "tpl");
+    
+    if (!(rscFile || tplFile)) {
+        throw "Not a Rascal .rsc or .tpl file: ";
+    }
+    // Find matching .rsc file in source directories
+    if (rscFile) {
+        for (loc dir <- pcfg.srcs) {
+            if (moduleLoc.authority == dir.authority && startsWith(modulePath, dir.path))
+            {
+                moduleName = replaceFirst(modulePath, dir.path, "");
+                if (exists(dir + moduleName)) {
+                     = splitFileExtension(moduleName);
+                    if (moduleName[0] == "/") {
+                        moduleName = moduleName[1..];
+                    }
+                    moduleName = replaceAll(moduleName, "/", "::");
+                    return moduleName;
+                }
+            }
+        }
+    }
+    // Find longest matching .tpl file in library directories
+     = splitFileExtension(modulePath);
+    while(modulePathNoExt[0] == "/") {
+        modulePathNoExt = modulePathNoExt[1..];
+    }
+    
+    modulePathAsList = split("/", modulePathNoExt);
+    if (tplFile) {
+        lastName = modulePathAsList[-1];
+        if (lastName[0] == "$") {
+            modulePathAsList = [*modulePathAsList[..-1], lastName[1..]];
+        }
+    }
+    if (modulePathAsList[0] == "rascal") {
+        modulePathAsList = modulePathAsList[1..];
+    }
+    modulePathReversed = reverse(modulePathAsList);
+    
+    int longestSuffix = 0;
+    for (loc dir <- pcfg.libs) {
+        dir = dir + "rascal";
+        dpath = dir.path;
+        
+        while(dpath[0] == "/") {
+            dpath = dpath[1..];
+        }
+        
+        for (loc file <- find(dir, "tpl")) {
+            candidate = replaceFirst(file.path, dpath, "");
+             = splitFileExtension(candidate);
+            while(candidate[0] == "/") {
+                candidate = candidate[1..];
+            }
+            
+            candidateAsList = split("/", candidate);
+            lastName = candidateAsList[-1];
+            if (lastName[0] == "$") {
+                candidateAsList = [*candidateAsList[..-1], lastName[1..]];
+            }
+            // println("cand: , modpath: ");
+            n = commonPrefix(reverse(candidateAsList), modulePathReversed);
+            
+            if (n > longestSuffix) {
+                longestSuffix = n;
+            }
+        }
+    }
+    
+    if (longestSuffix > 0) {
+        lastName = modulePathAsList[-1];
+        if (lastName[0] == "$") {
+            modulePathAsList = [*modulePathAsList[..-1], lastName[1..]];
+        }
+        return
+            intercalate(
+                "::", modulePathAsList[size(modulePathAsList) - longestSuffix..]
+            );
+    }
+    throw "No module name found for ;\nsrcs=;\nlibs=";
+}
+
+@deprecated{Function will be moved to Rascal compiler}
+@synopsis{Derive a location from a given module name for reading}
+@description{
+Given a module name, a file name extension, and a PathConfig,
+a path name is constructed from the module name + extension.
+
+If a file F with this path exists in one of the directories in the PathConfig,
+then the pair  is returned. Otherwise  is returned.
+
+For a source extension (typically "rsc" or "mu" but this can be configured) srcs is searched, otherwise binPath + libs.
+}
+@examples{
+```rascal-shell
+import util::Reflective;
+getDerivedReadLoc("List", "rsc", pathConfig());
+getDerivedReadLoc("experiments::Compiler::Compile", "rvm", pathConfig());
+getDerivedReadLoc("experiments::Compiler::muRascal2RVM::Library", "mu", pathConfig());
+```
+}
+@benefits{
+This function is useful for type checking and compilation tasks, when derived information related to source modules has to be read
+from locations in different, configurable, directories.
+}
+tuple[bool, loc] getDerivedReadLoc(
+    str qualifiedModuleName,
+    str extension,
+    PathConfig pcfg,
+    set[str] srcExtensions = {"rsc", "mu"},
+    str rootDir = ""
+) {
+    fileName = makeFileName(qualifiedModuleName, extension = extension);
+    
+    //println("getDerivedReadLoc: ");
+    if (extension in srcExtensions) {
+        for (loc dir <- pcfg.srcs) {
+            // In a source directory?
+            fileLoc = dir + rootDir + fileName;
+            if (exists(fileLoc)) {
+                //println("getDerivedReadLoc: ,  =\> ;
+            }
+        }
+    }
+    else {
+        for (loc dir <- pcfg.bin + pcfg.libs) {
+            // In a bin or lib directory?
+            fileLoc = dir + rootDir + fileName;
+            if (exists(fileLoc)) {
+                //println("getDerivedReadLoc: ,  =\> ");
+                return <true, fileLoc>;
+            }
+        }
+    }
+    
+    //println("getDerivedReadLoc: ,  =\> |error:///|");
+    return <false, |error:///|>;
+}
+
+@deprecated{Function will be moved to Rascal compiler}
+@synopsis{Derive a location from a given module name for writing}
+@description{
+Given a module name, a file name extension, and a PathConfig,
+a path name is constructed from the module name + extension.
+
+For source modules, a writable location cannot be derived.
+For other modules, a location for this path in bin will be returned.
+}
+@examples{
+```rascal-shell
+import util::Reflective;
+getDerivedWriteLoc("List", "rvm", pathConfig());
+getDerivedWriteLoc("experiments::Compiler::Compile", "rvm", pathConfig());
+```
+
+```rascal-shell,error
+getDerivedWriteLoc("experiments::Compiler::muRascal2RVM::Library", "rsc", pathConfig());
+```
+}
+@benefits{
+This function is useful for type checking and compilation tasks, when derived information related to source modules has to be written
+to locations in separate, configurable, directories.
+}
+loc getDerivedWriteLoc(
+    str qualifiedModuleName,
+    str extension,
+    PathConfig pcfg,
+    set[str] srcExtensions = {"rsc", "mu"},
+    str rootDir = ""
+) {
+    if (extension in srcExtensions) {
+        throw "Cannot derive writable location for module  with extension ";
+    }
+    fileNameSrc = makeFileName(qualifiedModuleName);
+    fileNameBin = makeFileName(qualifiedModuleName, extension = extension);
+    
+    bin = pcfg.bin;
+    fileLocBin = bin + rootDir + fileNameBin;
+    
+    //println("getDerivedWriteLoc: ,  =\> ");
+    return fileLocBin;
+}
+
+@javaClass{org.rascalmpl.library.util.Reflective}
+public
+java PathConfig getProjectPathConfig(
+    loc projectRoot,
+    RascalConfigMode mode = compiler()
+);
+
+@synopsis{Is the current Rascal code executed by the compiler or the interpreter?}
+@javaClass{org.rascalmpl.library.util.Reflective}
+public java bool inCompiledMode();
+
+@synopsis{Give a textual diff between two values.}
+@javaClass{org.rascalmpl.library.util.Reflective}
+public java str diff(value old, value new);
+
+@synopsis{Compute a fingerprint of a value for the benefit of the compiler and the compiler runtime}
+@javaClass{org.rascalmpl.library.util.Reflective}
+public java int getFingerprint(value val, bool concretePatterns);
+
+@synopsis{Compute a fingerprint of a value and arity modifier for the benefit of the compiler and the compiler runtime}
+@javaClass{org.rascalmpl.library.util.Reflective}
+public java int getFingerprint(value val, int arity, bool concretePatterns);
+
+@synopsis{Compute a fingerprint of a complete node for the benefit of the compiler and the compiler runtime}
+@javaClass{org.rascalmpl.library.util.Reflective}
+public java int getFingerprintNode(node nd);
+
+@synopsis{Get the internal hash code of a value. For the benefit of debugging the Rascal implementation.}
+@description{
+This function is useless for Rascal programmer's as it is a part of the under-the-hood implementation of values.
+You can use a value directly as a lookup key. The internal data-structures probably use this hashCode for
+optimal lookups in `O(log(size))`. 
+
+We use this function to diagnose possible performance issues caused by hash collisions.
+}
+@javaClass{org.rascalmpl.library.util.Reflective}
+public java int getHashCode(value v);
+
+@synopsis{Throw a raw Java NullPointerException, to help simulate an unexpected exception in test scenarios}
+@javaClass{org.rascalmpl.library.util.Reflective}
+java void throwNullPointerException();
+
+@synopsis{Return a list of all Rascal reserved identifiers (a.k.a. keywords)}
+set[str] getRascalReservedIdentifiers()
+    = {n| /lit(n) := #RascalKeywords.definitions[keywords("RascalKeywords")]};
+
+@javaClass{org.rascalmpl.library.util.Reflective}
+java str getRascalVersion();
+
+@synopsis{Create a folder structure for an empty Rascal project with Maven support}
+void newRascalProject(
+    loc folder,
+    str group = "org.rascalmpl",
+    str version = "0.1.0-SNAPSHOT"
+) {
+    if (exists(folder)) {
+        throw " exists already. Please provide an non-existing and empty folder name";
+    }
+    str name = folder.file;
+    
+    if (/[^a-z0-9\-_]/ := name) {
+        throw "Folder  should have only lowercase characters, digits and dashes from [a-z0-9\\-]";
+    }
+    newRascalPomFile(folder, name = name, group = group, version = version);
+    newRascalMfFile(folder, name = name);
+    newRascalVsCodeSettings(folder);
+    
+    mkDirectory(folder + "src/main/rascal");
+    writeFile((folder + "src/main/rascal") + "Main.rsc", emptyModule());
+}
+
+private loc pomFile(loc folder) = folder + "pom.xml";
+
+private
+str emptyModule()
+    = "module Main
+      '
+      'import IO;
+      '
+      'int main(int testArgument=0) {
+      '    println(\"argument: \\");
+      '    return testArgument;
+      '}
+      '";
+
+private
+str rascalMF(str name)
+    = "Manifest-Version: 0.0.1
+      'Project-Name: 
+      'Source: src/main/rascal
+      '
+      '";
+
+@synopsis{Create a new META-INF/RASCAL.MF file.}
+@description{
+The `folder` parameter should point to the root of a project folder.
+The name of the project will be derived from the name of that folder
+and a META-INF/RASCAL.MF file will be generated and written.
+
+The folder is created if it does not exist already.
+}
+void newRascalMfFile(loc folder, str name = folder.file) {
+    mkDirectory(metafile(folder).parent);
+    writeFile(metafile(folder), rascalMF(name));
+}
+
+@synopsis{Create a new pom.xml for a Rascal project}
+@description{
+The `folder` parameter should point to the root of a project folder.
+The name of the project will be derived from the name of that folder
+and a pom.xml file will be generated and written.  
+
+The folder is created if it does not exist already.
+}
+void newRascalPomFile(
+    loc folder,
+    str name = folder.file,
+    str group = "org.rascalmpl",
+    str version = "0.1.0-SNAPSHOT"
+) {
+    mkDirectory(folder);
+    writeFile(pomFile(folder), pomXml(name, group, version));
+}
+
+private
+str pomXml(str name, str group, str version)
+    = "\
+      '  \
+      '  \4.0.0\
+      '
+      '  \\
+      '  \\
+      '  \\
+      '
+      '  \
+      '    \UTF-8\
+      '  \
+      '
+      '  \
+      '    \
+      '        \usethesource\
+      '        \https://releases.usethesource.io/maven/\
+      '    \
+      '  \
+      '
+      '  \
+      '    \
+      '       \usethesource\
+      '       \https://releases.usethesource.io/maven/\
+      '    \
+      '  \
+      '
+      '  \
+      '    \
+      '      \org.rascalmpl\
+      '      \rascal\
+      '      \\
+      '    \
+      '  \
+      '
+      '  \
+      '    \
+      '      \
+      '        \org.apache.maven.plugins\
+      '        \maven-compiler-plugin\
+      '        \3.8.0\
+      '        \
+      '          \-parameters\ 
+      '          \11\
+      '        \
+      '      \
+      '      \
+      '        \org.rascalmpl\
+      '        \rascal-maven-plugin\
+      '        \0.8.2\
+      '        \
+      '          \true\
+      '          \${project.build.outputDirectory}\
+      '          \
+      '            \${project.basedir}/src/main/rascal\
+      '          \
+      '        \
+      '      \
+      '    \
+      '  \
+      '\
+    ";
+
+private
+str vscodeSettings()
+    = "{
+      '    \"search.exclude\": {
+      '        \"/target/\": true,
+      '    },
+      '}";
+
+private loc vscodeFolder(loc folder) = folder + ".vscode";
+private
+loc vscodeSettingsFile(loc folder)
+    = vscodeFolder(folder) + "settings.json";
+
+@synopsis{Create a `.vscode` folder for a Rascal project}
+@description{
+The `folder` parameter should point to the root of a project folder.
+A `.vscode` folder will be generated and written.
+
+The project folder is created if it does not exist already.
+}
+void newRascalVsCodeSettings(loc folder) {
+    mkDirectory(vscodeFolder(folder));
+    writeFile(vscodeSettingsFile(folder), vscodeSettings());
+}
+### |project://rascal/src/org/rascalmpl/library/util/Sampling.rsc|
+@license{
+  Copyright (c) 2009-2022 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI}
+@synopsis{Utilities to randomly select smaller datasets from larger datasets}
+@description{
+Sampling is important when the analysis algorithms do not scale to the size of 
+the original corpus, or when you need to train an analysis on a representative
+set without overfitting on the entire corpus. These sampling functions all
+assume that a uniformly random selection is required.
+}
+module util::Sampling
+
+import util::Math;
+import Map;
+import List;
+import Set;
+
+@synopsis{Reduce the arity of a set by selecting a uniformly distributed sample.}
+@description{
+A uniform subset is computed by iterating over the set and skipping every element
+with a probability of `1/(size(corpus) / target)`. This rapidly generates a new set of
+expected `target` size, but most probably a little smaller or larger.
+}
+@examples{
+```rascal-shell
+import util::Sampling;
+sample({"a","b","c","e","f","g","h","i","j","k"}, 4)
+sample({"a","b","c","e","f","g","h","i","j","k"}, 4)
+sample({"a","b","c","e","f","g","h","i","j","k"}, 4)
+```
+}
+set[&T] sample(set[&T] corpus, int target)
+    = {e
+      | int factor := size(corpus) / target, e <- corpus, factor == 0 ? true : arbInt(factor) == 0
+      };
+
+@synopsis{Reduce the length of a list by selecting a uniformly distributed sample.}
+@description{
+The random selection of elements does not change their initial order in the list.
+A uniform sublist is computed by iterating over the list and skipping every element
+with a probability of `1/(size(corpus) / target)`. This rapidly generates a new list of
+expected `target` size, but most probably a little smaller or larger.
+}
+@examples{
+```rascal-shell
+import util::Sampling;
+sample([1..1000], 30)
+sample([1..1000], 30)
+sample([1..1000], 30)
+```
+}
+list[&T] sample(list[&T] corpus, int target)
+    = [e
+      | int factor := size(corpus) / target, e <- corpus, factor == 0 ? true : arbInt(factor) == 0];
+
+@synopsis{Reduce the size of a map by selecting a uniformly distributed sample.}
+@description{
+A uniform submap is computed by iterating over the map's keys and skipping every key
+with a probability of `1/(size(corpus) / target)`. This rapidly generates a new map of
+expected `target` size, but most probably a little smaller or larger.
+}
+map[&T, &U] sample(map[&T, &U] corpus, int target)
+    = (k: corpus[k]
+      | int factor := size(corpus) / target, k <- corpus, factor == 0 ? true : arbInt(factor) == 0
+      );
+### |project://rascal/src/org/rascalmpl/library/util/SemVer.rsc|
+module util::SemVer
+
+@synopsis{Semantic Versioning}
+@description{
+Check that a given version string satisfies a range-set as defined by:
+(See https://github.com/npm/node-semver):
+``````
+range-set  ::= range ( logical-or range ) *
+logical-or ::= ( ' ' ) * '||' ( ' ' ) *
+range      ::= hyphen | simple ( ' ' simple ) * | ''
+hyphen     ::= partial ' - ' partial
+simple     ::= primitive | partial | tilde | caret
+primitive  ::= ( '<' | '>' | '>=' | '<=' | '=' | ) partial
+partial    ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
+xr         ::= 'x' | 'X' | '*' | nr
+nr         ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
+tilde      ::= '~' partial
+caret      ::= '^' partial
+qualifier  ::= ( '-' pre )? ( '+' build )?
+pre        ::= parts
+build      ::= parts
+parts      ::= part ( '.' part ) *
+part       ::= nr | [-0-9A-Za-z]+
+``````
+}
+@javaClass{org.rascalmpl.library.util.SemVerLib}
+java bool satisfiesVersion(str version, str rangeSet);
+
+@javaClass{org.rascalmpl.library.util.SemVerLib}
+java bool lessVersion(str version1, str version2);
+
+@javaClass{org.rascalmpl.library.util.SemVerLib}
+java bool lessEqualVersion(str version1, str version2);
+
+@javaClass{org.rascalmpl.library.util.SemVerLib}
+java bool greaterVersion(str version1, str version2);
+
+@javaClass{org.rascalmpl.library.util.SemVerLib}
+java bool greaterEqualVersion(str version1, str version2);
+
+@javaClass{org.rascalmpl.library.util.SemVerLib}
+java bool equalVersion(str version1, str version2);
+
+@javaClass{org.rascalmpl.library.util.SemVerLib}
+java str getRascalVersion();
+
+@javaClass{org.rascalmpl.library.util.SemVerLib}
+java str getRascalRuntimeVersion();
+
+@javaClass{org.rascalmpl.library.util.SemVerLib}
+java str getRascalCompilerVersion();
+
+@javaClass{org.rascalmpl.library.util.SemVerLib}
+java str getJavaRuntimeVersion();
+### |project://rascal/src/org/rascalmpl/library/util/ShellExec.rsc|
+@license{
+  Copyright (c) 2009-2015 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Mark Hills - Mark.Hills@cwi.nl (CWI)}
+@synopsis{Execute and manage external processes.}
+module util::ShellExec
+
+@synopsis{Start a new external process.}
+@deprecated{
+Use the createProcess function that takes `loc` for processCommand for better portability behavior between operating systems.
+}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+java PID createProcess(
+    str processCommand,
+    loc workingDir = |cwd:///|,
+    list[str] args = [],
+    map[str, str] envVars = ()
+);
+
+@synopsis{Start a new external process.}
+@description{
+The file schemes that are allowed for the `processCommand` are limited to the `file:///` schemes
+and all logical schemes that directly resolve to `file:///` such as `cwd:///` and `tmp:///`.
+`PATH:///` is also a handy scheme for `processCommand` since it searches for the binary/script
+in the underlying system's search path.
+
+The arguments to `args` given are all converted to strings before passing them into the command.
+Special treatment is given to `loc` arguments, which are first resolved to `file:///` schemes and
+then printed to OS-specific absolute path names. Also `list[loc]` and set[loc] are treated by
+converting to OS-specific path strings, separated by Java's `File.pathSeparator`. 
+
+For environment variables in `envVars` the same treatment is given to convert values to strings.
+}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+java PID createProcess(
+    loc processCommand,
+    loc workingDir = |cwd:///|,
+    list[value] args = [],
+    map[str, value] envVars = ()
+);
+
+@synopsis{start, run and kill an external process returning its output as a string.}
+@deprecated{
+Use the `exec`` function that takes `loc` for processCommand for better portability behavior between operating systems.
+}
+str exec(
+    str processCommand,
+    loc workingDir = |cwd:///|,
+    list[str] args = [],
+    map[str, str] env = ()
+) {
+    pid
+        = createProcess(
+              processCommand, workingDir = workingDir, args = args, envVars = env
+          );
+    result = readEntireStream(pid);
+    killProcess(pid);
+    return result;
+}
+
+@synopsis{start, run and kill an external process returning its output as a string.}
+str exec(
+    loc processCommand,
+    loc workingDir = |cwd:///|,
+    list[value] args = [],
+    map[str, value] env = ()
+) {
+    pid
+        = createProcess(
+              processCommand, workingDir = workingDir, args = args, envVars = env
+          );
+    result = readEntireStream(pid);
+    killProcess(pid);
+    return result;
+}
+
+@deprecated{
+Use the `execWithCode` function that takes `loc` for processCommand for better portability behavior between operating systems.
+}
+tuple[str output, int exitCode] execWithCode(
+    str processCommand,
+    loc workingDir = |cwd:///|,
+    list[str] args = [],
+    map[str, str] env = ()
+) {
+    pid
+        = createProcess(
+              processCommand, workingDir = workingDir, args = args, envVars = env
+          );
+    result = readEntireStream(pid);
+    code = exitCode(pid);
+    killProcess(pid);
+    
+    return ;
+}
+
+@synopsis{start, run and kill an external process returning its output as a string with an exit code.}
+tuple[str output, int exitCode] execWithCode(
+    loc processCommand,
+    loc workingDir = |cwd:///|,
+    list[value] args = [],
+    map[str, value] env = ()
+) {
+    pid
+        = createProcess(
+              processCommand, workingDir = workingDir, args = args, envVars = env
+          );
+    result = readEntireStream(pid);
+    code = exitCode(pid);
+    killProcess(pid);
+    
+    return ;
+}
+
+@synopsis{Kill a running process, or a zombie process (a process which is not alive yet not killed)}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+public java void killProcess(PID processId, bool force = false);
+
+@synopsis{Check whether a process is still alive}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+public java bool isAlive(PID processId);
+
+@synopsis{Check whether a process is still registered but not actually running anymore. A zombie process may be cleaned up using killProcess.}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+public java bool isZombie(PID processId);
+
+@synopsis{Waits for the process to exit and then returns its return code. This is a blocking operation.}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+public java int exitCode(PID processId);
+
+@synopsis{Read from an existing process's output stream. This is non-blocking.}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+public java str readFrom(PID processId);
+
+@synopsis{Read from an existing process's output stream with a given wait timeout. Some processes are a little slower in producing output. The wait is used to give the process some extra time in producing output. This is non-blocking apart from the waiting.}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+public java str readWithWait(PID processId, int wait);
+
+@synopsis{Read from an existing process's error output stream. This is non-blocking.}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+public java str readFromErr(PID processId);
+
+@synopsis{Read from an existing process's error output stream. This blocks until a full line is read and waits for one second maximally for this line to appear.}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+public java str readLineFromErr(PID processId, int wait = 200, int maxTries = 5);
+
+@synopsis{Read the entire stream from an existing process's output stream. This is blocking.}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+public java str readEntireStream(PID processId);
+
+@synopsis{Read the entire error stream from an existing process's output stream. This is blocking.}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+public java str readEntireErrStream(PID processId);
+
+@synopsis{Write to an existing process's input stream.}
+@javaClass{org.rascalmpl.library.util.ShellExec}
+public java void writeTo(PID processId, str msg);
+
+@synopsis{Process Identifiers (PID).}
+@description{
+A PID is returned by ((createProcess)) and is required for any further interaction with the created process.
+}
+public alias PID = int;
+### |project://rascal/src/org/rascalmpl/library/util/SystemAPI.rsc|
+@license{
+   Copyright (c) 2009-2015 CWI
+   All rights reserved. This program and the accompanying materials
+   are made available under the terms of the Eclipse Public License v1.0
+   which accompanies this distribution, and is available at
+   http://www.eclipse.org/legal/epl-v10.html
+ }
+@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI}
+@contributor{Bert Lisser - Bert.Lisser@cwi.nl (CWI)}
+@synopsis{API to the System for example System Variables or System Commands.}
+module util::SystemAPI
+
+@javaClass{org.rascalmpl.library.util.SystemAPI}
+java str getSystemProperty(str property);
+
+@javaClass{org.rascalmpl.library.util.SystemAPI}
+java map[str, str] getSystemProperties();
+
+@javaClass{org.rascalmpl.library.util.SystemAPI}
+java map[str, str] getSystemEnvironment();
+### |project://rascal/src/org/rascalmpl/library/util/Test.rsc|
+@synopsis{Provides occasionally useful access to Rascal's testing framework}
+@description{
+Rascal's test framework can normally be accessed via UI and commandline interfaces:
+
+  * Running as JUnit tests in IDEs
+  * Running as JUnit tests from Maven
+  * Executing the `:test` command in a Rascal REPL
+  
+This module provides a programmatic interface, and reports the test results
+as values. It can be handy to construct more UI components which interact
+with tests, but also to query larger volumes of failing tests.
+}
+module util::Test
+
+data TestResult
+    = \testResult(
+      str name, bool success, loc src, str message = "", list[value] exceptions = []
+      );
+
+@synopsis{Run all tests for the given module name}
+@description{
+This function works under the assumption that the named module is available in the current execution environment.
+}
+@javaClass{org.rascalmpl.library.util.RunTests}
+@reflect{
+to access interpreter functionality to run tests
+}
+java list[TestResult] runTests(str moduleName);
+
+private test bool testTest() = true;
+### |project://rascal/src/org/rascalmpl/library/util/UUID.rsc|
+module util::UUID
+
+@javaClass{org.rascalmpl.library.Prelude}
+@synopsis{generates a unique identifier shaped as a `loc`}
+@description{
+This function generates a UUID, see .
+Since UUIDs are useful to assign an opaque and unique identity to data, the function returns
+a location (which is the preferred representation for encoding **identities** in Rascal)
+}
+@examples{
+```rascal-shell
+import util::UUID;
+```
+
+The uuid() function generates a location with the authority showing the literal canonical UUID string
+```rascal-shell,continue
+uuid()
+```
+
+Use it to relate identities to data objects, as in this example which adds a field to a relation:
+
+```rascal-shell,continue
+myData = {  | i <- [1..11] }; 
+rel[int n, int square, loc id] myUniqueData = {  |  <- myData };
+map[tuple[int i, int j] t, loc id] myUniqueMap = (:uuid() |  <- myData );
+```
+Note how uuid() should always generate a fresh value:
+```rascal-shell,continue
+assert uuid() != uuid(); 
+```
+}
+@benefits{
+* Locations are used for identifying program elements or model elements in Rascal. The uuid() function provides
+an quick-and-easy way of acquiring such an identity without having to design a naming scheme.
+}
+@pitfalls{
+*  UUIDs are a quick and dirty way of identifying data which may lead to hard to debug code. A naming scheme for locations is better because it generates human readable
+locations which carry meaning. For example consider the difference in readability between these two values:
+`|uuid://47fdcd64-4fd0-41a1-8aa3-61c5b272c3fc|` and `|java+class:///java/lang/Object|`. Both may lead to the same 
+results in your computation, but if we print either of them out, one of them is opaque and the other is transparent. A transparent naming scheme is preferable for
+debugging purposes.
+}
+java loc uuid();
+
+@javaClass{org.rascalmpl.library.Prelude}
+@synopsis{see [uuid], this function does the same except return the UUID as an int.}
+@pitfalls{
+*  beware that this integer is almost guaranteed to use 128 bits, so communicating it outside of
+Rascal should not be done via a Java 32-bit integer.
+}
+java int uuidi();
+### |project://rascal/src/org/rascalmpl/library/util/Validator.rsc|
+@synopsis{Generic validator function that can convert values of the `node` type to instances of abstract `data` type constructors.}
+@description{
+The intended use-case is to read structured data externally, say an XML or JSON or YAML file, as generic `node` values and then
+to use the `validate` function to map the untyped representation to a typed representation, if it can be validated accordingly.
+}
+module util::Validator
+
+import Type;
+import Node;
+import List;
+import Exception;
+import IO;
+
+data RuntimeException = invalid(str \type, value v, list[value] path = []);
+
+private data RuntimeException = none();
+
+@synopsis{The general and simple validation case is when a value's run-time type already matches the expected static type}
+@memo
+&T validate(
+    type[&T] expected,
+    value v,
+    list[value] path = [],
+    bool relaxed = false
+) {
+    if (&T x := v) {
+        return x;
+    }
+    else {
+        fail validate;
+    }
+}
+
+@synopsis{To validate nodes we can try whether or not it can be matched to a constructor of a defined data type with the same name and (resp. validating) children.}
+@memo
+&T validate(
+    type[&T] expected,
+    node v,
+    list[value] path = [],
+    bool relaxed = false
+) {
+    Symbol lookup(str name, [*_, label(key, sym), *_]) = sym;
+    default Symbol lookup(str _, list[Symbol] _) = \value();
+    Symbol unlabel(label(_, Symbol sym)) = sym;
+    default Symbol unlabel(Symbol sym) = sym;
+    
+    if (expected.symbol == \node(), &T vv := v) {
+        return vv;
+    }
+    if (def: adt(_, _) := expected.symbol, grammar := expected.definitions) {
+        RuntimeException msg = none();
+        name = getName(v);
+        children = getChildren(v);
+        params = getKeywordParameters(v);
+        int arity = size(children);
+        
+        candidates
+            // first the constructors with the right name
+            = [
+              | /\cons(label(name, def), symbols, kwTypes, _) := grammar[def] ? {}, size(symbols) == arity]
+            + // then the constructors with different names (only in relaxed mode)
+             [
+              | relaxed, /\cons(label(str other: !name, _), symbols, kwTypes, _) := grammar[def] ? {}, size(symbols) == arity];
+        
+        // there may be several constructors with this name; we try them all, backtracking over already validated sub-values:
+        for ( <- candidates) {
+            try {
+                // for the recursion it's important that we @memo the results to avoid rework in the next cycle of the surrounding for loop
+                children
+                    = [validate(
+                           type(unlabel(symbols[i]), grammar), children[i],
+                           path = path + [i],
+                           relaxed = relaxed
+                       ) | i <- index(children)];
+                
+                // similarly for recursion into the keyword parameters, we @memo this function to make sure we don't do sub-trees again and again:
+                params
+                    = (key:
+                           validate(
+                               type(lookup(name, kwTypes), grammar), params[key],
+                               path = path + [key],
+                               relaxed = relaxed
+                           )
+                      | key <- params
+                      );
+                
+                // TODO: make a more specific and faster version of `make` that can apply a specific constructor directly
+                return make(expected, otherName, children, params);
+            }
+            catch RuntimeException e: invalid(_, _): {
+                msg = e;
+                continue;
+            }
+            catch RuntimeException e: IllegalArgument(_): {
+                // pretty sure this can never happen, but `make` does potentially throw this in the general case...
+                msg = e;
+                continue;
+            }
+            catch RuntimeException e: IllegalArgument(_, _): {
+                // pretty sure this can never happen, but `make` does potentially throw this in the general case...
+                msg = e;
+                continue;
+            }
+        }
+        
+        if (msg != none()) {
+            throw msg;
+        }
+        else {
+            fail validate;
+        }
+    }
+    fail validate;
+}
+
+@synopsis{if a (sub)value can not be validated we report the expected type, the not-matching value and the path that led us there}
+default &T validate(
+    type[&T] expected,
+    value v,
+    list[value] path = [],
+    bool relaxed = false
+) {
+    throw invalid("", v, path = path);
+}
+
+test bool simpleInt() {
+    value x = 1;
+    
+    return int _ := validate(#int, x);
+}
+
+test bool defaultNode() {
+    value x = "hello"();
+    
+    return node _ := validate(#node, x);
+}
+
+test bool adtTest() {
+    value x = "invalid" (
+                  "XXX",
+                  [[[]]]
+                  , path = [1, 0, 0]
+              );
+    
+    return RuntimeException _ := validate(#RuntimeException, x);
+}
+
+test bool adtRelaxedTest() {
+    value x = "object" (
+                  "XXX",
+                  [[[]]]
+                  , path = [1, 0, 0]
+              );
+    
+    return RuntimeException _ := validate(#RuntimeException, x, relaxed = true);
+}
+
+test bool adtTestFail() {
+    value x = "invali" (
+                  "XXX",
+                  [[[]]]
+                  , path = [1, 0, 0]
+              );
+    
+    try {
+        validate(#RuntimeException, x);
+        return false;
+    }
+    catch invalid(_, _):
+        return true;
+}
+
+test bool adtTestFailNested() {
+    value x = "invalid" (
+                  2,
+                  [[[]]]
+                  , path = [1, 0, 0]
+              );
+    
+    try {
+        validate(#RuntimeException, x);
+        return false;
+    }
+    catch invalid(_, _):
+        return true;
+}
+
+test bool adtTestFailKeyword() {
+    value x = "invalid" (
+                  "hello",
+                  [[[]]]
+                  , path = "[1,0,0]"
+              );
+    
+    try {
+        validate(#RuntimeException, x);
+        return false;
+    }
+    catch invalid(_, _):
+        return true;
+}
+### |project://rascal/src/org/rascalmpl/library/util/Webserver.rsc|
+module util::Webserver
+
+extend Content;
+
+@javaClass{org.rascalmpl.library.util.Webserver}
+java void serve(loc server, Response(Request) callback, bool asDaemon = true);
+
+@javaClass{org.rascalmpl.library.util.Webserver}
+java void shutdown(loc server);
+
+test bool testWebServer() {
+    loc testLoc = |http://localhost:10001|;
+    
+    // simple get
+    // Response testServer(get("/hello")) = response("hello world!");
+    Response testServer(p: post("/upload8", value(type[value] _) stuff))
+        = response("uploaded:  ");
+    
+    try {
+        serve(testLoc, testServer);
+        return true;
+    }
+    catch value exception:
+        throw exception;
+    finally {
+        shutdown(testLoc);
+    }
+}
+### |project://rascal/src/org/rascalmpl/library/vis/Basic.rsc|
+@license{
+  Copyright (c) 2022 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI}
+@synopsis{Uses static HTML to visualize arbitrary Rascal values}
+@description{
+This modules provides a simple mapping from hierarchical (nested)
+Rascal values to HTML markup. The goal of the mapping is to provide
+a more visible and searchable representation of Rascal values than
+the standard textual expression format.
+
+This module is quite new and may undergo some tweaks in the coming time.
+}
+module vis::Basic
+
+import lang::html::IO;
+import lang::rascal::format::Grammar;
+import ParseTree;
+import Node;
+import util::IDEServices;
+import util::Math;
+import util::Sampling;
+import Content;
+import IO;
+import ValueIO;
+import List;
+import Set;
+import Map;
+
+Content showValue(value v) = content(md5Hash(v), valueServer(v));
+
+Response(Request) valueServer(value v) {
+    Response reply(get(/^\/editor/,parameters = pms)) {
+        if (pms["src"]?) {
+            edit(readTextValueString(#loc, pms["src"]));
+            return response(writeHTMLString(text("done")));
+        }
+        return response(writeHTMLString(text("could not edit ")));
+    }
+    
+    default Response reply(get(_)) {
+        return response(writeHTMLString(toHTML(v)));
+    }
+    
+    return reply;
+}
+
+HTMLElement toHTML(num i) = text("");
+HTMLElement toHTML(str s) = pre([p([text(s)])]);
+HTMLElement toHTML(loc l)
+    = a([ text("") ], href = "#", onclick = "fetch(\"/editor?src=\")" );
+
+HTMLElement toHTML(list[value] l)
+    = div([
+          b([text("<type(typeOf(l), ())> ()")]),
+          ul([li([toHTML(e)]) | e <- sample(l, 100)])
+      ]);
+
+default HTMLElement toHTML(set[value] l)
+    = div([
+          b([text("<type(typeOf(l), ())> ()")]),
+          ul([li([toHTML(e)]) | e <- sample(l, 100)])
+      ]);
+
+HTMLElement toHTML(rel[value, value] r)
+    = div([
+          b([text("<type(typeOf(r), ())> ")]),
+          table(
+              [tr([td([toHTML(a)]), td([toHTML(b)])]) |  <- sample(r, 100)],
+              border = "1"
+          )
+      ]);
+
+HTMLElement toHTML(rel[value, value, value] r)
+    = div([
+          b([text("<type(typeOf(r), ())> ")]),
+          table(
+              [tr([td([toHTML(a)]), td([toHTML(b)]), td([toHTML(c)])])
+              |  <- sample(r, 100)],
+              border = "1"
+          )
+      ]);
+
+HTMLElement toHTML(rel[value, value, value, value] r)
+    = div([
+          b([text("<type(typeOf(r), ())> ")]),
+          table(
+              [tr([td([toHTML(a)]), td([toHTML(b)]), td([toHTML(c)]), td([toHTML(d)])])
+              |  <- sample(r, 100)],
+              border = "1"
+          )
+      ]);
+
+HTMLElement toHTML(map[value, value] m)
+    = div([
+          b([text("<type(typeOf(m), ())> ")]),
+          table(
+              [tr([td([toHTML(k)]), td([toHTML(m[k])])]) | k <- sample(m, 100)],
+              border = "1"
+          )
+      ]);
+
+HTMLElement toHTML(t: <value a, value bb>)
+    = div([
+          b([text("<type(typeOf(t), ())>")]),
+          table([ tr([td([toHTML(a)]), td([toHTML(bb)])]) ], border = "1" )
+      ]);
+
+HTMLElement toHTML(t: <value a, value bb, value c>)
+    = div([
+          b([text("<type(typeOf(t), ())>")]),
+          table([
+              tr([td([toHTML(a)]), td([toHTML(bb)]), td([toHTML(c)])])
+              ],
+              border = "1"
+          )
+      ]);
+
+HTMLElement toHTML(t: <value a, value bb, value c, value d>)
+    = div([
+          b([text("<type(typeOf(t), ())>")]),
+          table([
+              tr([
+                  td([toHTML(a)]), td([toHTML(bb)]), td([toHTML(c)]), td([toHTML(d)])
+              ])
+              ],
+              border = "1"
+          )
+      ]);
+
+HTMLElement toHTML(Tree t: appl(Production p, list[Tree] args))
+    = div([
+          text(topProd2rascal(p)),
+          *(t@\loc? ? [toHTML(t@\loc)] : []),
+          ul([li([toHTML(a)]) | a <- args])
+      ]);
+
+HTMLElement toHTML(Tree t: amb(set[Tree] alts))
+    = div([text(symbol2rascal(typeOf(t))), ul([li([toHTML(a)]) | a <- alts])]);
+
+HTMLElement toHTML(char(int c))
+    = pre([text(" - codepoint ")]);
+
+HTMLElement toHTML(node n)
+    = div([
+          p([b([text("node:")]), text(getName(n))]),
+          *[table(
+                [tr([
+                     td([ toHTML(k) ], style = "vertical-align:top" ),
+                     td([ toHTML(kws[k]) ], style = "vertical-align:top" )
+                 ]) | k <- kws],
+                border = "1"
+            ) | kws := getKeywordParameters(n), kws != ()],
+          ul([li([toHTML(a)]) | a <- n])
+      ]);
+
+default HTMLElement toHTML(value x: !set[value] _) // set is also a default to avoid clashing with rel
+                                                  = text("");
+
+private
+str sampled(list[value] s, int count)
+    = size(s) > count ? "sampled /" : "";
+
+private
+str sampled(set[value] s, int count)
+    = size(s) > count ? "sampled /" : "";
+
+private
+str sampled(map[value, value] s, int count)
+    = size(s) > count ? "sampled /" : "";
+### |project://rascal/src/org/rascalmpl/library/vis/Charts.rsc|
+@license{
+  Copyright (c) 2022 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI}
+@synopsis{Simple data visualization using charts}
+@description{
+This modules provides a simple API to create charts for Rascal
+(numerical) data, based on [chart.js](https://chartjs.org/). 
+This library mirrors chart.js' JSON-based configuration API more or less one-to-one
+using data types of Rascal. Documentation about chart.js should be easy
+to interpret.
+
+This module is quite new and may undergo some tweaks in the coming time.
+}
+@examples{
+```rascal-shell
+import vis::Charts;
+import util::Math;
+scatterChart([ | x <- [1..100]])
+scatterChart(["down", "up"], [ | x <- [1..100]], [ | x <- [1..100]])
+```
+
+```rascal-shell-continue
+barChart([<"",x-arbInt(20)> | x <- [1..100]])
+barChart(["down", "up"], [<"",100-x+arbInt(20)> | x <- [1..100]], [<"",x+arbInt(20)> | x <- [1..100]])
+```
+
+```rascal-shell-continue
+lineChart([<"",x+arbInt(20)> | x <- [1..100]])
+lineChart(["down", "up"],
+    [<"",100-x+arbInt(20)> | x <- [1..100]], 
+    [<"",x+arbInt(20)> | x <- [1..100]]
+)
+lineChart(["down", "up", "even"],
+    [<"",100-x+arbInt(20)> | x <- [1..100]], 
+    [<"",x+arbInt(20)> | x <- [1..100]], 
+    [<"", 70-arbInt(20)> | x <- [1..100]]
+)
+```
+
+```rascal-shell-continue
+pieChart([<"",x+arbInt(25)> | x <- [1..10]])
+```
+}
+@benefits{
+* Easy to use for basic charting.
+* Uses ((ChartAutoColors)) extension for ease-of-use.
+* Support for 8 ((ChartType))s including multiple ((ChartDataSet))s in one chart.
+* This API is open to extension via adding common keyword parameters for supporting any extension to the basic chart.js configuration.
+* You can roll your own HTML or Server based on the building blocks in this module to include and use extensions, or to combine different charts in the same view.
+}
+@pitfalls{
+* Where `num` is asked, still `rat` values are _not_ supported.
+* All `real` values must stay within JVM's `double` datatype
+* All `int` values must fit within JVM's `long` datatype
+}
+module vis::Charts
+
+import lang::html::IO;
+import lang::html::AST;
+import Content;
+import Set;
+import List;
+
+@synopsis{A scatter plot from a binary numerical relation.}
+Content scatterChart(
+    lrel[num x, num y] v,
+    str title = "Scatterplot",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(title, v),
+              \type = scatter(),
+              title = title,
+              colorMode = colorMode,
+              legend = false
+          )
+      );
+
+Content scatterChart(
+    list[str] labels,
+    lrel[num x, num y] values...,
+    str title = "Scatterplots",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = scatter(),
+              title = title,
+              colorMode = colorMode
+          )
+      );
+
+Content scatterChart(
+    rel[num x, num y] v,
+    str title = "Scatterplot",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(title, v),
+              \type = scatter(),
+              title = title,
+              colorMode = colorMode,
+              legend = false
+          )
+      );
+
+Content scatterChart(
+    list[str] labels,
+    rel[num x, num y] values...,
+    str title = "Scatterplots",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = scatter(),
+              title = title,
+              colorMode = colorMode
+          )
+      );
+
+@synopsis{A bubble chart from a binary numerical list relation.}
+@pitfalls{
+* the radius is in raw pixels rather than scaled to the chart's axis
+}
+Content bubbleChart(
+    lrel[num x, num y, num r] v,
+    str title = "Scatterplot",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(title, v),
+              \type = bubble(),
+              title = title,
+              colorMode = colorMode,
+              legend = false
+          )
+      );
+
+Content bubbleChart(
+    list[str] labels,
+    lrel[num x, num y, num r] values...,
+    str title = "Scatterplots",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = scatter(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content bubbleChart(
+    rel[num x, num y, num r] v,
+    str title = "Scatterplot",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(title, v),
+              \type = scatter(),
+              title = title,
+              colorMode = colorMode,
+              legend = false
+          )
+      );
+
+Content bubbleChart(
+    list[str] labels,
+    rel[num x, num y, num r] values...,
+    str title = "Scatterplots",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = scatter(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+@synopsis{A bar chart from labeled numbers}
+Content barChart(
+    rel[str label, num val] values,
+    str title = "Bar Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \bar(),
+              title = title,
+              colorMode = colorMode,
+              legend = false
+          )
+      );
+
+Content barChart(
+    lrel[str label, num val] values,
+    str title = "Bar Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \bar(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content barChart(
+    list[str] labels,
+    rel[str label, num val] values...,
+    str title = "Bar Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \bar(),
+              title = title,
+              colorMode = colorMode,
+              legend = false
+          )
+      );
+
+Content barChart(
+    list[str] labels,
+    lrel[str label, num val] values...,
+    str title = "Bar Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \bar(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+@synopsis{A line chart from labeled numbers}
+Content lineChart(
+    rel[str label, num val] values,
+    str title = "Line Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \line(),
+              title = title,
+              colorMode = colorMode,
+              legend = false
+          )
+      );
+
+Content lineChart(
+    lrel[str label, num val] values,
+    str title = "Line Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \line(),
+              title = title,
+              colorMode = colorMode,
+              legend = false
+          )
+      );
+
+Content lineChart(
+    list[str] labels,
+    rel[str label, num val] values...,
+    str title = "Line Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \line(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content lineChart(
+    list[str] labels,
+    lrel[str label, num val] values...,
+    str title = "Line Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \line(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+@synopsis{A polar area chart from labeled numbers}
+Content polarAreaChart(
+    rel[str label, num val] values,
+    str title = "Polar Area Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \polarArea(),
+              title = title,
+              colorMode = colorMode,
+              legend = false
+          )
+      );
+
+Content polarAreaChart(
+    lrel[str label, num val] values,
+    str title = "Polar Area Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \polarArea(),
+              title = title,
+              colorMode = colorMode,
+              legend = false
+          )
+      );
+
+Content polarAreaChart(
+    list[str] labels,
+    rel[str label, num val] values...,
+    str title = "Polar Area Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \polarArea(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content polarAreaChart(
+    list[str] labels,
+    lrel[str label, num val] values...,
+    str title = "Polar Area Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \polarArea(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+@synopsis{A radar chart from labeled numbers}
+Content radarChart(
+    rel[str label, num val] values,
+    str title = "Radar Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \radar(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content radarChart(
+    lrel[str label, num val] values,
+    str title = "Radar Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \radar(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content radarChart(
+    list[str] labels,
+    rel[str label, num val] values...,
+    str title = "Radar Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \radar(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content radarChart(
+    list[str] labels,
+    lrel[str label, num val] values...,
+    str title = "Radar Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \radar(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+@synopsis{A pie chart from labeled numbers}
+Content pieChart(
+    rel[str label, num val] values,
+    str title = "Pie Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \pie(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content pieChart(
+    lrel[str label, num val] values,
+    str title = "Pie Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \pie(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content pieChart(
+    list[str] labels,
+    rel[str label, num val] values...,
+    str title = "Pie Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \pie(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content pieChart(
+    list[str] labels,
+    lrel[str label, num val] values...,
+    str title = "Pie Chart",
+    ChartAutoColorMode colorMode = \dataset()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \pie(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+@synopsis{A doughnut chart from labeled numbers}
+Content doughnutChart(
+    rel[str label, num val] values,
+    str title = "Doughnut Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \doughnut(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content doughnutChart(
+    lrel[str label, num val] values,
+    str title = "Doughnut Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(values),
+              \type = \doughnut(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content doughnutChart(
+    list[str] labels,
+    rel[str label, num val] values...,
+    str title = "Doughnut Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \doughnut(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+Content doughnutChart(
+    list[str] labels,
+    lrel[str label, num val] values...,
+    str title = "Doughnut Chart",
+    ChartAutoColorMode colorMode = \data()
+)
+    = content(
+          title,
+          chartServer(
+              chartData(labels, values),
+              \type = \doughnut(),
+              title = title,
+              colorMode = colorMode,
+              legend = true
+          )
+      );
+
+@synopsys{
+converts plain data sources into chart.js datasets
+}
+ChartDataSet chartDataSet(str label, rel[num x, num y] r)
+    = chartDataSet([point(x, y) |  <- r], label = label);
+
+ChartDataSet chartDataSet(str label, map[num x, num y] r)
+    = chartDataSet([point(x, r[x]) | x <- r], label = label);
+
+ChartDataSet chartDataSet(str label, rel[num x, num y, num rad] r)
+    = chartDataSet([point(x, y, r = rad) |  <- r], label = label);
+
+ChartDataSet chartDataSet(str label, lrel[num x, num y] r)
+    = chartDataSet([point(x, y) |  <- r], label = label);
+
+ChartDataSet chartDataSet(str label, lrel[num x, num y, num r] r)
+    = chartDataSet([point(x, y, r = rad) |  <- r], label = label);
+
+@synopsys{
+converts plain data sources into the chart.js data representation
+}
+ChartData chartData(rel[str label, num val] v)
+    = chartData(labels = [l |  <- v], datasets = [chartDataSet([n | <_, n> <- v])]);
+
+ChartData chartData(map[str label, num val] v)
+    = chartData(labels = [l | l <- v], datasets = [chartDataSet([v[l] | l <- v])]);
+
+ChartData chartData(lrel[str label, num val] v)
+    = chartData(labels = [l |  <- v], datasets = [chartDataSet([n | <_, n> <- v])]);
+
+ChartData chartData(list[str] labels, lrel[num x, num y] values...)
+    = chartData(
+          labels = labels,
+          datasets = [chartDataSet(labels[i], values[i]) | i <- [0..size(labels)]]
+      );
+
+ChartData chartData(list[str] labels, lrel[num x, num y, num r] values...)
+    = chartData(
+          labels = labels,
+          datasets = [chartDataSet(labels[i], values[i]) | i <- [0..size(labels)]]
+      );
+
+ChartData chartData(list[str] labels, rel[num x, num y] values...)
+    = chartData(
+          labels = labels,
+          datasets = [chartDataSet(labels[i], values[i]) | i <- [0..size(labels)]]
+      );
+
+ChartData chartData(list[str] labels, rel[num x, num y, num r] values...)
+    = chartData(
+          labels = labels,
+          datasets = [chartDataSet(labels[i], values[i]) | i <- [0..size(labels)]]
+      );
+
+ChartData chartData(list[str] setLabels, lrel[str label, num val] values...)
+    = chartData(
+          // first merge the label sets, while keeping the order as much as possible
+          labels = labels,
+          // now sort the datasets accordingly, missing data is represented by `0`
+          datasets =
+          [chartDataSet([*(values[i][l] ? [0]) | l <- labels], label = setLabels[i])
+          | i <- [0..size(setLabels)]]
+      )
+    when list[str] labels := ( [] | (l in it) ? it : it + l | r <- values, l <- r<0> );
+
+ChartData chartData(list[str] setLabels, rel[str label, num val] values...)
+    = chartData(
+          // first merge the label sets, while keeping the order as much as possible
+          labels = labels,
+          // now sort the datasets accordingly, missing data is represented by `0`
+          datasets =
+          [chartDataSet([*(values[i][l] ? {0}) | l <- labels], label = setLabels[i])
+          | i <- [0..size(setLabels)]]
+      )
+    when list[str] labels := ( [] | (l in it) ? it : it + l | r <- values, l <- r<0> );
+
+ChartData chartData(list[str] labels, list[num] values...)
+    = chartData(labels = labels, datasets = [chartDataSet(v) | v <- values]);
+
+ChartData chartData(str label, lrel[num x, num y] values)
+    = chartData(datasets = [chartDataSet(label, values)]);
+
+ChartData chartData(str label, map[num x, num y] values)
+    = chartData(datasets = [chartDataSet(label, values)]);
+
+ChartData chartData(str label, lrel[num x, num y, num r] values)
+    = chartData(datasets = [chartDataSet(label, values)]);
+
+ChartData chartData(str label, rel[num x, num y] values)
+    = chartData(datasets = [chartDataSet(label, values)]);
+
+ChartData chartData(str label, rel[num x, num y, num r] values)
+    = chartData(datasets = [chartDataSet(label, values)]);
+
+@synopsis{Toplevel chart structure}
+data Chart
+    = chart(
+          ChartType \type = scatter(),
+          ChartOptions options = chartOptions(),
+          ChartData \data = chartData());
+
+@synopsis{Wrapper for a set of datasets, each with a label}
+data ChartData
+    = chartData( list[str] labels = [], list[ChartDataSet] datasets = []);
+
+@synopsis{A dataset is a list of values to chart, with styling properties.}
+@description{
+The `data` field is a list of supported values, of which the constraints
+are not expressible by data types. These are currently supported:
+
+* ((ChartDataPoint)), with and without a `r`adius
+* `num`, but within `double` precision (!) and no `rat`
+}
+data ChartDataSet (str label = "",
+                   list[str] backgroundColor = [],
+                   list[str] borderColor = [],
+                   list[str] color = []) = chartDataSet(list[value] \data);
+
+@synopsis{A data point is one of the types of values in a ChartDataSet}
+data ChartDataPoint = point(num x, num y, num r = 0);
+
+data ChartType
+    = scatter()
+    | bar()
+    | bubble()
+    | line()
+    | polarArea()
+    | radar()
+    | pie()
+    | doughnut()
+    ;
+
+data ChartOptions
+    = chartOptions(
+          bool responsive = true,
+          bool animations = true,
+          ChartPlugins plugins = chartPlugins());
+
+data ChartPlugins
+    = chartPlugins(
+          ChartTitle title = chartTitle(),
+          ChartLegend legend = chartLegend(),
+          ChartColors colors = chartColors(),
+          ChartAutoColors autocolors = chartAutoColors());
+
+data ChartAutoColors = chartAutoColors( ChartAutoColorMode \mode = \data());
+
+data ChartAutoColorMode
+    = \data()
+    | \dataset()
+    ;
+
+data ChartLegend
+    = chartLegend( LegendPosition position = top(), bool display = true);
+
+data LegendPosition
+    = \top()
+    | \bottom()
+    | \left()
+    | \right()
+    ;
+
+data ChartTitle = chartTitle( str text = "", bool display = true);
+
+data ChartColors = chartColors( bool enabled = true);
+
+@synopsis{Utility function that constructs a Chart from ChartData and a given Chart type and a title.}
+@description{
+A chart has a typical default layout that we can reuse for all kinds of chart types. This function
+provides the template and immediately instantiates the client and the server to start displaying the chart
+in a browser.
+}
+Response(Request) chartServer(
+    ChartData theData,
+    ChartType \type = \bar(),
+    str title = "Chart",
+    ChartAutoColorMode colorMode = \data(),
+    bool legend = true,
+    bool animations = false
+)
+    = chartServer(
+          chart(
+              \type = \type,
+              \data = theData,
+              options =
+              chartOptions(
+                  responsive = true,
+                  animations = animations,
+                  plugins =
+                  chartPlugins(
+                      legend = chartLegend(position = top(), display = legend),
+                      title = chartTitle(display = true, text = title),
+                      colors = chartColors(enabled = true),
+                      autocolors = chartAutoColors(mode = colorMode)
+                  )
+              )
+          )
+      );
+
+@synopsis{this is the main server generator for any chart value}
+@description{
+Given a Chart value this server captures the value and serves it
+as a JSON value to the HTML client generated by ((vis::Charts::plotHTML)).
+}
+Response(Request) chartServer(Chart ch) {
+    Response reply(get(/^\/chart/)) {
+        return response(ch);
+    }
+    
+    // returns the main page that also contains the callbacks for retrieving data and configuration
+    default Response reply(get(_)) {
+        return response(writeHTMLString(plotHTML()));
+    }
+    
+    return reply;
+}
+
+@synopsis{default HTML wrapper for a chart}
+private
+HTMLElement plotHTML()
+    = html([
+          div([
+              // put div here instead of `head` to work around issue
+              script([  ], src = "https://cdn.jsdelivr.net/npm/chart.js" ),
+              script([
+                  ],
+                  src = "https://cdn.jsdelivr.net/npm/chartjs-plugin-autocolors"
+              )
+          ]),
+          body([
+              div([canvas([  ], id = "visualization" )]),
+              script([
+                  \data(
+                      "var container = document.getElementById(\'visualization\');
+                      'const autocolors = window[\'chartjs-plugin-autocolors\'];
+                      'Chart.register(autocolors);
+                      'fetch(\'/chart\').then(resp =\> resp.json()).then(chart =\> {
+                      '   new Chart(container, chart);   
+                      '})
+                      '"
+                  )
+                  ],
+                  \type = "text/javascript"
+              )
+              ],
+              style =
+              "position: fixed; top:50%; left:50%; transform: translate(-50%, -50%); width:min(75%,800px);"
+          )
+      ]);
+### |project://rascal/src/org/rascalmpl/library/vis/Graphs.rsc|
+@license{
+  Copyright (c) 2022 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI}
+@contributor{Tijs van der Storm - storm@cwi.nl - CWI}
+@synopsis{Simple data visualization using graphs; based on cytoscape.js}
+@description{
+This modules provides a simple API to create graph visuals for Rascal
+(relational) data, based on [Cytoscape.js](https://js.cytoscape.org/). 
+
+This module is quite new and may undergo some tweaks in the coming time.
+}
+@benefits{
+* Easy to use for basic graph layouts.
+}
+module vis::Graphs
+
+import lang::html::IO;
+import lang::html::AST;
+import util::IDEServices;
+import Content;
+import ValueIO;
+import Set;
+
+@synopsis{Optional configuration attributes for graph style and graph layout}
+@description{
+These configuration options are used to map input graph data to layout properties
+and style properties. 
+
+* title - does what it says
+* nodeLinker - makes nodes clickable by providing an editor location
+* nodeLabeler - allows simplification or elaboration on node labels beyond their identity string
+* nodeClassifier - labels nodes with classes in order to later select them for specific styling
+* edgeLabeler - allows simplification or elaboration on edge labels 
+* layout - defines and configured the graph layout algorithm
+* nodeStyle - defines the default style for all nodes
+* edgeStyle - defines the default style for all edges
+* style - collects specific styles for specific ((CytoSelector)) edge/node selectors using ((CytoStyleOf)) tuples.
+
+Typically the functions passed into this configuration are closures that capture and use the original
+input data to find out about where to link and how to classify. The `&T` parameter reflects the type of
+the original input `Graph[&T]`; so that is the type of the nodes. Often this would be `loc` or `str`.
+}
+@examples{
+
+Let's experiment with a number of styling parameters based on the shape of a graph:
+```rascal-shell
+import vis::Graphs;
+// let's play with the genealogy of the "Simpsons"
+g = {
+    <"Abraham Simpson", "Homer Simpson">,
+    <"Mona Simpson", "Homer Simpson">,
+    <"Homer Simpson", "Bart Simpson">,
+    <"Homer Simpson", "Lisa Simpson">,
+    <"Homer Simpson", "Maggie Simpson">,
+    <"Marge Simpson", "Bart Simpson">,
+    <"Marge Simpson", "Lisa Simpson">,
+    <"Marge Simpson", "Maggie Simpson">,
+    <"Bart Simpson", "Rod Flanders">,
+    <"Bart Simpson", "Todd Flanders">,
+    <"Lisa Simpson", "Bart Simpson">,
+    <"Abraham Simpson", "Patty Bouvier">,
+    <"Abraham Simpson", "Selma Bouvier">,
+    <"Mona Simpson", "Patty Bouvier">,
+    <"Mona Simpson", "Selma Bouvier">
+};
+// visualizing this without styling:
+graph(g);
+// to style nodes, let's select some special nodes and "classify" them first. We reuse some generic graph analysis tools.
+import analysis::graphs::Graph;
+list[str] nodeClassifier(str simpson) = [
+  *["top" | simpson in top(g)],
+  *["bottom" | simpson in bottom(g)]
+];
+// once classified, we can style each node according to their assigned classes. Nodes can be in more than one class.
+styles = [
+    cytoStyleOf( 
+        selector=or([\node(className("top")),\node(className("bottom"))]),
+        style=defaultNodeStyle()[shape=CytoNodeShape::diamond()]
+    )
+];
+// we pick a sensible layout
+lyt = defaultDagreLayout();
+// we wrap the styling information into a configuration wrapper:
+cfg = cytoGraphConfig(nodeClassifier=nodeClassifier, styles=styles, \layout=lyt);
+// and now we see the effect:
+graph(g, cfg=cfg)
+// now let's style some edges:
+list[str] edgeClassifier(str from, str to) = ["grandparent" |  in g o g];
+// add another styling element
+styles += [
+    cytoStyleOf( 
+        selector=edge(className("grandparent")),
+        style=defaultEdgeStyle()[\line-style="dashed"]
+    )
+];
+// and draw again (while adding the grandparent edges too)
+graph(g + (g o g), cfg=cytoGraphConfig(nodeClassifier=nodeClassifier, edgeClassifier=edgeClassifier, styles=styles, \layout=lyt))
+```    
+}
+data CytoGraphConfig
+    = cytoGraphConfig(
+          str title = "Graph",
+          NodeLinker[&T] nodeLinker = defaultNodeLinker,
+          NodeLabeler[&T] nodeLabeler = defaultNodeLabeler,
+          NodeClassifier[&T] nodeClassifier = defaultNodeClassifier,
+          EdgeLabeler[&T] edgeLabeler = defaultEdgeLabeler,
+          EdgeClassifier[&T] edgeClassifier = defaultEdgeClassifier,
+          CytoLayout \layout = defaultCoseLayout(),
+          CytoStyle nodeStyle = defaultNodeStyle(),
+          CytoStyle edgeStyle = defaultEdgeStyle(),
+          list[CytoStyleOf] styles = []);
+
+@synopsis{A NodeLinker maps node identities to a source location to link to}
+alias NodeLinker[&T] = loc(&T _id1);
+
+@synopsis{The default node linker assumes any loc found in the node identity is a proper link.}
+loc defaultNodeLinker(/loc l) = l;
+default loc defaultNodeLinker(&T _) = |nothing:///|;
+
+@synopsis{A NodeLabeler maps node identities to descriptive node labels}
+alias NodeLabeler[&T] = str(&T _id2);
+
+@synopsis{The default node labeler searches for any `str`` in the identity, or otherwise a file name of a `loc`}
+str defaultNodeLabeler(/str s) = s;
+str defaultNodeLabeler(loc l) = l.file != "" ? l.file : "";
+default str defaultNodeLabeler(&T v) = "";
+
+@synopsis{A NodeClassifier maps node identities to classes that are used later to select specific layout and coloring options.}
+alias NodeClassifier[&T] = list[str](&T _id3);
+
+@synopsis{The default classifier produces no classes}
+list[str] defaultNodeClassifier(&T _) = [];
+
+@synopsis{An EdgeClassifier maps edge identities to classes that are used later to select specific layout and coloring options.}
+alias EdgeClassifier[&T] = list[str](&T _from, &T _to);
+
+@synopsis{The default edge classifier produces no classes}
+list[str] defaultEdgeClassifier(&T _, &T _) = [];
+
+@synopsis{An EdgeLabeler maps edge identities to descriptive edge labels.}
+alias EdgeLabeler[&T] = str(&T _source, &T _target);
+
+@synopsis{The default edge labeler returns the empty label for all edges.}
+str defaultEdgeLabeler(&T _source, &T _target) = "";
+
+@synopsis{A graph plot from a binary list relation.}
+@examples{
+```rascal-shell
+import vis::Graphs;
+graph([ | x <- [1..100]] + [<100,1>])
+graph([ | x <- [1..100]] + [<100,1>], cfg=cytoGraphConfig(\layout=\defaultCircleLayout()))
+```
+
+Providing locations as node identities automatically transforms them to node links:
+```rascal-shell
+import vis::Graphs;
+import IO;
+d = [<|std:///|, e> | e <- |std:///|.ls];
+d += [ | <_, e> <- d, isDirectory(e), f <- e.ls];
+graph(d, \layout=defaultDagreLayout());
+// here we adapt the node labeler to show only the last file name in the path of the location:
+graph(d, \layout=defaultDagreLayout(), cfg=cytoGraphConfig(nodeLabeler=str (loc l) { return l.file; }));
+```
+}
+Content graph(lrel[&T x, &T y] v, CytoGraphConfig cfg = cytoGraphConfig())
+    = content(cfg.title, graphServer(cytoscape(graphData(v, cfg = cfg))));
+
+@synopsis{A graph plot from a ternary list relation where the middle column is the edge label.}
+@examples{
+```rascal-shell
+import vis::Graphs;
+graph([ | x <- [1..100]] + [<100,101,1>])
+```
+}
+Content graph(
+    lrel[&T x, &L edge, &T y] v,
+    CytoGraphConfig cfg = cytoGraphConfig()
+)
+    = content(
+          cfg.title, graphServer(cytoscape(graphData(v, cfg = cfg), cfg = cfg))
+      );
+
+@synopsis{A graph plot from a binary relation.}
+@examples{
+```rascal-shell
+import vis::Graphs;
+graph({ | x <- [1..100]} + {<100,1>})
+``` 
+}
+Content graph(rel[&T x, &T y] v, CytoGraphConfig cfg = cytoGraphConfig())
+    = content(
+          cfg.title, graphServer(cytoscape(graphData(v, cfg = cfg), cfg = cfg))
+      );
+
+@synopsis{A graph plot from a ternary relation where the middle column is the edge label.}
+@examples{
+```rascal-shell
+import vis::Graphs;
+graph({ | x <- [1..100]} + {<100,101,1>})
+```
+}
+Content graph(
+    rel[&T x, &L edge, &T y] v,
+    CytoGraphConfig cfg = cytoGraphConfig()
+)
+    = content(
+          cfg.title, graphServer(cytoscape(graphData(v, cfg = cfg), cfg = cfg))
+      );
+
+@synopsis{This core workhorse mixes the graph data with the configuration to obtain a visualizable CytoScape.js data-structure.}
+@description{
+This data-structure is serialized to JSON and communicated directly to initialize cytoscape.js.
+The serialization is done by the generic ((lang::json::IO)) library under the hood of a ((util::Webserver)).
+}
+@synopsis{Produces an overall cytoscape.js wrapper which is sent as JSON to the client side.}
+Cytoscape cytoscape(
+    list[CytoData] \data,
+    CytoGraphConfig cfg = cytoGraphConfig()
+)
+    = cytoscape(
+          elements = \data,
+          style =
+          [
+              cytoNodeStyleOf(cfg.nodeStyle),
+              cytoEdgeStyleOf(cfg.edgeStyle),
+              *cfg.styles
+          ],
+          \layout = cfg.\layout
+      );
+
+@synopsis{Turns a `rel[loc from, loc to]` into a graph}
+list[CytoData] graphData(
+    rel[loc x, loc y] v,
+    CytoGraphConfig cfg = cytoGraphConfig()
+)
+    = [cytodata(
+           \node("", label = cfg.nodeLabeler(e), editor = ""),
+           classes = cfg.nodeClassifier(e)
+       ) | e <- {*v, *v}]
+    + [cytodata(
+           \edge("", "", label = cfg.edgeLabeler(from, to)),
+           classes = cfg.edgeClassifier(from, to)
+       ) |  <- v];
+
+@synopsis{Turns any `rel[&T from, &T to]` into a graph}
+default list[CytoData] graphData(
+    rel[&T x, &T y] v,
+    CytoGraphConfig cfg = cytoGraphConfig()
+)
+    = [cytodata(
+           \node("", label = cfg.nodeLabeler(e), editor = ""),
+           classes = cfg.nodeClassifier(e)
+       ) | e <- {*v, *v}]
+    + [cytodata(
+           \edge("", "", label = cfg.edgeLabeler(from, to)),
+           classes = cfg.edgeClassifier(from, to)
+       ) |  <- v];
+
+@synopsis{Turns any `lrel[loc from, &L edge, loc to]` into a graph}
+list[CytoData] graphData(
+    lrel[loc x, &L edge, loc y] v,
+    CytoGraphConfig cfg = cytoGraphConfig()
+)
+    = [cytodata(
+           \node("", label = cfg.nodeLabeler(e), editor = ""),
+           classes = cfg.nodeClassifier(e)
+       ) | e <- {*v, *v}]
+    + [cytodata(
+           \edge("", "", label = ""),
+           classes = cfg.edgeClassifier(from, to)
+       ) |  <- v];
+
+@synopsis{Turns any `lrel[&T from, &L edge, &T to]` into a graph}
+default list[CytoData] graphData(
+    lrel[&T x, &L edge, &T y] v,
+    CytoGraphConfig cfg = cytoGraphConfig()
+)
+    = [cytodata(
+           \node("", label = cfg.nodeLabeler(e), editor = ""),
+           classes = cfg.nodeClassifier(e)
+       ) | e <- {*v, *v}]
+    + [cytodata(
+           \edge("", "", label = ""),
+           classes = cfg.edgeClassifier(from, to)
+       ) |  <- v];
+
+@synopsis{Turns any `lrel[loc from, loc to]` into a graph}
+list[CytoData] graphData(
+    lrel[loc x, loc y] v,
+    CytoGraphConfig cfg = cytoGraphConfig()
+)
+    = [cytodata(
+           \node("", label = cfg.nodeLabeler(e), editor = ""),
+           classes = cfg.nodeClassifier(e)
+       ) | e <- {*v, *v}]
+    + [cytodata(
+           \edge("", "", label = cfg.edgeLabeler(from, to)),
+           classes = cfg.edgeClassifier(from, to)
+       ) |  <- v];
+
+@synopsis{Turns any `lrel[&T from, &T to]` into a graph}
+default list[CytoData] graphData(
+    lrel[&T x, &T y] v,
+    CytoGraphConfig cfg = cytoGraphConfig()
+)
+    = [cytodata(
+           \node("", label = cfg.nodeLabeler(e), editor = ""),
+           classes = cfg.nodeClassifier(e)
+       ) | e <- {*v, *v}]
+    + [cytodata(
+           \edge("", "", label = cfg.edgeLabeler(from, to)),
+           classes = cfg.edgeClassifier(from, to)
+       ) |  <- v];
+
+@synopsis{Turns any `rel[loc from, &L edge, loc to]` into a graph}
+list[CytoData] graphData(
+    rel[loc x, &L edge, loc y] v,
+    CytoGraphConfig cfg = cytoGraphConfig()
+)
+    = [cytodata(
+           \node("", label = cfg.nodeLabeler(e), editor = ""),
+           classes = cfg.nodeClassifier(e)
+       ) | e <- {*v, *v}]
+    + [cytodata(
+           \edge("", "", label = ""),
+           classes = cfg.edgeClassifier(from, to)
+       ) |  <- v];
+
+@synopsis{Turns any `rel[&T from, &L edge, &T to]` into a graph}
+default list[CytoData] graphData(
+    rel[&T x, &L edge, &T y] v,
+    CytoGraphConfig cfg = cytoGraphConfig()
+)
+    = [cytodata(
+           \node("", label = cfg.nodeLabeler(e), editor = ""),
+           classes = cfg.nodeClassifier(e)
+       ) | e <- {*v, *v}]
+    + [cytodata(
+           \edge("", "", label = ""),
+           classes = cfg.edgeClassifier(from, to)
+       ) |  <- v];
+
+data CytoNodeShape
+    = \ellipse()
+    | \triangle()
+    | \round-triangle()
+    | \rectangle()
+    | \round-rectangle()
+    | \bottom-round-rectangle()
+    | \cut-rectangle()
+    | \barrel()
+    | \rhomboid()
+    | \diamond()
+    | \round-diamond()
+    | \pentagon()
+    | \round-pentagon()
+    | \hexagon()
+    | \round-hexagon()
+    | \concave-hexagon()
+    | \heptagon()
+    | \round-heptagon()
+    | \octagon()
+    | \round-octagon()
+    | \star()
+    | \tag()
+    | \round-tag()
+    | \vee()
+    | \polygon()
+    ;
+
+@synopsis{Overall cytoscape.js object for sending to the client side.}
+data Cytoscape
+    = cytoscape(
+          list[CytoData] elements = [],
+          list[CytoStyleOf] style = [],
+          CytoLayout \layout = cytolayout());
+
+data CytoData = cytodata(CytoElement \data, list[str] classes = []);
+
+data CytoElement
+    = \node(str id, str label = id, str editor = "|none:///|")
+    | \edge(str source, str target, str id = "-", str label = "")
+    ;
+
+data CytoHorizontalAlign
+    = left()
+    | center()
+    | right()
+    | auto()
+    ;
+
+data CytoVerticalAlign
+    = top()
+    | center()
+    | bottom()
+    | auto()
+    ;
+
+data CytoArrowHeadStyle
+    = triangle()
+    | \triangle-tee()
+    | \circle-triangle()
+    | \triangle-cross()
+    | \triangle-backcurve()
+    | vee()
+    | tee()
+    | square()
+    | circle()
+    | diamond()
+    | chevron()
+    | none()
+    ;
+
+data CytoTextWrap
+    = none()
+    | wrap()
+    | ellipses()
+    ;
+
+data CytoCurveStyle
+    = bezier()
+    | \unbundled-bezier()
+    | straight()
+    | segments()
+    | \straight-triangle()
+    | taxi()
+    | haystack()
+    ;
+
+data CytoStyleOf
+    = cytoStyleOf(
+       CytoSelector selector = \node(), CytoStyle style = cytoNodeStyle());
+
+CytoStyleOf cytoNodeStyleOf(CytoStyle style)
+    = cytoStyleOf(selector = \node(), style = style);
+CytoStyleOf cytoEdgeStyleOf(CytoStyle style)
+    = cytoStyleOf(selector = \edge(), style = style);
+
+@synopsis{Instantiates a default node style}
+@description{
+Because the JSON writer can not instantiate default values for keyword fields,
+we have to do it manually here.
+}
+CytoStyle defaultNodeStyle()
+    = cytoNodeStyle(
+          visibility = "visible",
+          /* hidden, collapse */          opacity = "1",
+          width = "label",
+          padding = "10pt",
+          \background-color = "blue",
+          color = "white",
+          \font-size = "20pt",
+          \font-weight = bold(),
+          label = "data(label)",
+          shape = \round-rectangle(),
+          \text-halign = CytoHorizontalAlign::\center(),
+          \text-valign = CytoVerticalAlign::\center()
+      );
+
+@synopsis{Instantiates a default edge style}
+@description{
+Because the JSON writer can not instantiate default values for keyword fields
+we have to do it manually here.
+}
+CytoStyle defaultEdgeStyle()
+    = cytoEdgeStyle(
+          visibility = "visible",
+          /* hidden, collapse */          opacity = "1",
+          \line-opacity = "1",
+          width = 3,
+          \line-style = "solid",
+          /* dotted, dashed */          \color = "red",
+          \line-color = "black",
+          \target-arrow-color = "black",
+          \source-arrow-color = "black",
+          \target-arrow-shape = CytoArrowHeadStyle::triangle(),
+          \source-arrow-shape = CytoArrowHeadStyle::none(),
+          \curve-style = \unbundled-bezier(),
+          \label = "data(label)"
+      );
+
+data CytoFontWeight
+    = normal()
+    | lighter()
+    | bold()
+    | bolder()
+    ;
+
+data CytoStyle
+    = cytoNodeStyle(
+          str visibility = "visible",
+          /* hidden, collapse */          str opacity = "1",
+          str width = "label",
+          str padding = "10pt",
+          str color = "white",
+          str \text-opacity = "1",
+          str \font-family = "",
+          str \font-size = "12pt",
+          str \font-style = "",
+          CytoFontWeight \font-weight = normal(),
+          str \background-color = "blue",
+          str label = "data(label)",
+          CytoNodeShape shape = CytoNodeShape::ellipse(),
+          CytoHorizontalAlign \text-halign = CytoHorizontalAlign::center(),
+          CytoVerticalAlign \text-valign = CytoVerticalAlign::\top(),
+          CytoTextWrap \text-wrap = CytoTextWrap::none(),
+          str \text-max-width = "100px",
+          CytoHorizontalAlign \text-justification = CytoHorizontalAlign::center(),
+          int \line-height = 1)
+    | cytoEdgeStyle(
+          str visibility = "visible",
+          /* hidden, collapse */          str opacity = "1",
+          str \line-opacity = "1",
+          int width = 3,
+          str \line-color = "black",
+          str \line-style = "solid",
+          /* dotted, dashed */          str color = "red",
+          str \target-arrow-color = "black",
+          str \source-arrow-color = "black",
+          CytoArrowHeadStyle \target-arrow-shape = CytoArrowHeadStyle::triangle(),
+          CytoArrowHeadStyle \source-arrow-shape = CytoArrowHeadStyle::none(),
+          CytoCurveStyle \curve-style = CytoCurveStyle::\unbundled-bezier(),
+          int \source-text-offset = 1,
+          int \target-text-offset = 1,
+          str label = "data(label)")
+    ;
+
+@synopsis{A combinator language that translates down to strings in JSON}
+@description{
+* For field names you can use the names, or the dot notation for array indices and fields of objects: `"labels.0"`, `"name.first"`.
+* `and` and `or` can not be nested; this will lead to failure to select anything at all. The or must be outside and the and must be inside.
+* `node()` selects all nodes
+* `edge()` selects all edges
+}
+data CytoSelector
+    = \node()
+    | \edge()
+    | \id(str id)
+    | \and(list[CytoSelector] conjuncts)
+    | \or(list[CytoSelector] disjuncts)
+    | \equal(str field, str \value)
+    | \equal(str field, int limit)
+    | \greater(str field, int limit)
+    | \less(str field, int limit)
+    | \greaterEqual(str field, int limit)
+    | \lessEqual(str field, int limit)
+    | \className(str)
+    ;
+
+@synopsis{Short-hand for a node with a single condition}
+CytoSelector \node(CytoSelector condition) = and([\node(), condition]);
+
+@synopsis{Short-hand for a node with a single condition}
+CytoSelector \edge(CytoSelector condition) = and([\edge(), condition]);
+
+@synopsis{Utility to generate class attributes with multiple names consistently.}
+str more(set[str] names) = "<for (str n <- sort(names)) {> <}>"[..-1];
+
+@synopsis{Serialize a ((CytoSelector)) to string for client side expression.}
+str formatCytoSelector(\node()) = "node";
+str formatCytoSelector(\edge()) = "edge";
+str formatCytoSelector(\id(str i)) = formatCytoSelector(equal("id", i));
+str formatCytoSelector(and(list[CytoSelector] cjs))
+    = "<for (cj <- cjs) {><}>";
+str formatCytoSelector(or(list[CytoSelector] cjs))
+    = "<for (cj <- cjs) {>,<}>"[..-1];
+str formatCytoSelector(className(str class)) = ".";
+str formatCytoSelector(equal(str field, str val))
+    = "[ = \"\"]";
+str formatCytoSelector(equal(str field, int lim)) = "[ = ]";
+str formatCytoSelector(greater(str field, int lim)) = "[ \> ]";
+str formatCytoSelector(greaterEqual(str field, int lim))
+    = "[ \>= ]";
+str formatCytoSelector(lessEqual(str field, int lim))
+    = "[ \<= ]";
+str formatCytoSelector(less(str field, int lim)) = "[ \< ]";
+
+@synopsis{Choice of different node layout algorithms.}
+@description{
+The different algorithms use heuristics to find a layout
+that shows the structure of a graph best. Different types
+of graph data call for different algorithms:
+* `grid` is best when there are very few edges or when edges are not important. The edge relation
+is not used at all for deciding where each node will end up. Grid 
+is typically used for an initial exploration of the graph. It is very fast.
+* `circle` puts all nodes on the edge of a circle and draws edges between them. The order on the
+circle is arbitrary. This layout fails on larger collections of nodes because the points on the 
+circle will become really small and indistinguishable. However for graphs with less than 100 nodes 
+it provides a quick and natural overview.
+* `breadthfirst` computes a breadth-first spanning tree, and uses path length to decide on which
+layer each node will reside. Cross-edges (between branches) and back-edges are allowed but if there
+are many the graph will be messy. So this layout is best when you have a mostly hierarchical graph.
+Examples are flow charts and dependency graphs.
+* `cose` is a so-called "force-directed" layout. The edges become springs that both push nodes
+apart as well as pull them together. Nodes drag on the surface but have an initial momentum such
+that they can find a spot on the plain. This layout is very natural for scale-free networks such
+as biological organisms, friends graphs and software ecosystems.
+}
+data CytoLayoutName
+    = grid()
+    | circle()
+    | breadthfirst()
+    | cose()
+    | dagre()
+    ;
+
+@synopsis{An alias for dagre layout for documentation purposes.}
+@description{
+Dagre is a hierarchical graph layout.
+}
+CytoLayoutName hierarchical() = dagre();
+
+data CytoLayout (CytoLayoutName name = dagre(), bool animate = false)
+    = cytolayout()
+    | breadthfirstLayout(
+          CytoLayoutName name = CytoLayoutName::breadthfirst(),
+          num spacingFactor = 1,
+          list[str] roots = [],
+          bool circle = false,
+          bool grid = !circle,
+          bool directed = false)
+    | gridLayout(
+          CytoLayoutName name = CytoLayoutName::grid(),
+          int rows = 2,
+          int cols = 2,
+          bool avoidOverlap = true,
+          num spacingFactor = .1)
+    | circleLayout(
+          CytoLayoutName name = CytoLayoutName::circle(),
+          bool avoidOverlap = true,
+          num spacingFactor = .1)
+    | coseLayout( CytoLayoutName name = cose())
+    | dagreLayout(
+          CytoLayoutName name = dagre(),
+          num spacingFactor = .1,
+          DagreRanker ranker = \network-simplex()// network-simples tight-tree, or longest-path
+                                                 )
+    ;
+
+data DagreRanker
+    = \network-simplex()
+    | \tight-tree()
+    | \longest-path()
+    ;
+
+CytoLayout defaultCoseLayout() = coseLayout(name = cose(), animate = false);
+
+CytoLayout defaultCircleLayout(
+    bool avoidOverlap = true,
+    num spacingFactor = .1
+)
+    = circleLayout(
+          name = CytoLayoutName::circle(),
+          animate = false,
+          avoidOverlap = avoidOverlap,
+          spacingFactor = spacingFactor
+      );
+
+CytoLayout defaultGridLayout(
+    int rows = 2,
+    int cols = rows,
+    bool avoidOverlap = true,
+    num spacingFactor = .1
+)
+    = gridLayout(
+          name = CytoLayoutName::grid(),
+          animate = false,
+          rows = rows,
+          cols = cols,
+          avoidOverlap = avoidOverlap,
+          spacingFactor = spacingFactor
+      );
+
+CytoLayout defaultBreadthfirstLayout(
+    num spacingFactor = .1,
+    bool circle = false,
+    bool grid = !circle,
+    bool directed = false
+)
+    = breadthfirstLayout(
+          name = CytoLayoutName::breadthfirst(),
+          animate = false,
+          spacingFactor = spacingFactor,
+          circle = circle,
+          grid = grid,
+          directed = directed
+      );
+
+CytoLayout defaultDagreLayout(num spacingFactor = 1)
+    = dagreLayout(
+          name = CytoLayoutName::dagre(),
+          animate = false,
+          spacingFactor = spacingFactor,
+          ranker = \network-simplex()
+      );
+
+@synopsis{this is the main server generator for any graph value}
+@description{
+Given a Graph value this server captures the value and serves it
+as a JSON value to the HTML client generated by ((vis::Graphs::plotHTML)).
+}
+Response(Request) graphServer(Cytoscape ch) {
+    Response reply(get(/^\/editor/,parameters = pms)) {
+        if (pms["src"]?) {
+            edit(readTextValueString(#loc, pms["src"]));
+            return response(writeHTMLString(text("done")));
+        }
+        return response(writeHTMLString(text("could not edit ")));
+    }
+    
+    Response reply(get(/^\/cytoscape/)) {
+        return response(ch, formatCytoSelector);
+    }
+    
+    // returns the main page that also contains the callbacks for retrieving data and configuration
+    default Response reply(get(_)) {
+        return response(writeHTMLString(plotHTML()));
+    }
+    
+    return reply;
+}
+
+@synopsis{default HTML wrapper for a cytoscape.js graph}
+@description{
+This client features:
+* cytoscape.js loading with cytoscape-dagre and dagre present.
+* fetching of graph data via `http://localhost/cytoscape` URL
+* clickable links in every node that has an 'editor' data field that holds a `loc`, via the `http://localhost/editor?src=loc` URL
+* full screen graph view
+
+This client mirrors the server defined by ((graphServer)).
+}
+private
+HTMLElement plotHTML()
+    = html([
+          head([
+              script([
+                  ],
+                  src =
+                  "https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.28.1/cytoscape.umd.js"
+              ),
+              script([
+                  ],
+                  src = "https://cdnjs.cloudflare.com/ajax/libs/dagre/0.8.5/dagre.min.js"
+              ),
+              script([
+                  ],
+                  src =
+                  "https://cdn.jsdelivr.net/npm/cytoscape-dagre@2.5.0/cytoscape-dagre.min.js"
+              ),
+              style([
+                  \data(
+                      "#visualization {
+                      '  width: 100%;
+                      '  height: 100%;
+                      '  position: absolute;
+                      '  top: 0px;
+                      '  left: 0px;
+                      '}"
+                  )
+              ])
+          ]),
+          body([
+              div([  ], id = "visualization" ),
+              script([
+                  \data(
+                      "fetch(\'/cytoscape\').then(resp =\> resp.json()).then(cs =\> {
+                      '   cs.container = document.getElementById(\'visualization\');
+                      '   const cy = cytoscape(cs);
+                      '   cy.on(\'tap\', \'node\', function (evt) {
+                      '       var n = evt.target;
+                      '       if (n.data(\'editor\') !== undefined) {
+                      '           fetch(\'/editor?\' + new URLSearchParams({
+                      '                src: n.data(\'editor\')
+                      '           })) ;
+                      '       }
+                      '   });
+                      '});
+                      '"
+                  )
+                  ],
+                  \type = "text/javascript"
+              )
+          ])
+      ]);
+### |project://rascal/src/org/rascalmpl/library/vis/Text.rsc|
+@license{
+  Copyright (c) 2022 CWI
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+}
+@contributor{Tijs van der Storm - Tijs.van.der.Storm - CWI}
+@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI}
+@synopsis{Visualizing values using "ASCII art".}
+@synopsis{This module provides functions that map values to strings using ASCII Art pretty printing.
+
+The words [ASCII Art](https://en.wikipedia.org/wiki/ASCII_art) refers to the technique of 
+constructing images from text characters that are in the ASCII set. However, in this case
+we may use any Unicode character for visual representation purposes.}
+@examples{
+```rascal-shell
+syntax E = "e" | E "+" E;
+import IO;
+import vis::Text;
+ex = prettyTree([E] "e+e+e");
+println(ex);
+```
+}
+module vis::Text
+
+import Node;
+import List;
+import ListRelation;
+import ParseTree;
+import Type;
+
+@synopsis{Pretty prints parse trees using ASCII art lines for edges.}
+str prettyTree(
+    Tree t,
+    bool src = false,
+    bool characters = true,
+    bool \layout = false,
+    bool literals = \layout
+) {
+    bool include(appl(prod(lit(_), _, _), _)) = literals;
+    bool include(appl(prod(cilit(_), _, _), _)) = literals;
+    bool include(appl(prod(\layouts(_), _, _), _)) = \layout;
+    bool include(amb({*_, appl(prod(\layouts(_), _, _), _)})) = \layout;
+    bool include(char(_)) = characters;
+    default bool include(Tree _) = true;
+    
+    str nodeLabel(appl(prod(label(str l, Symbol nt), _, _), _))
+        = "<type(nt, ())> = : ";
+    str nodeLabel(appl(prod(Symbol nt, as, _), _))
+        = "<type(nt, ())> = <for (a <- as) {><type(a, ())> <}>";
+    str nodeLabel(appl(error(Symbol nt, Production p, int dot), _))
+        = "!error dot=: ";
+    str nodeLabel(appl(skipped(Symbol s), chars)) = "skipped";
+    str nodeLabel(appl(regular(Symbol nt), _)) = "<type(nt, ())>";
+    str nodeLabel(char(32)) = "⎵";
+    str nodeLabel(char(10)) = "\\r";
+    str nodeLabel(char(13)) = "\\n";
+    str nodeLabel(char(9)) = "\\t";
+    str nodeLabel(amb(_)) = "❖";
+    str nodeLabel(loc src) = "";
+    str nodeLabel(cycle(Symbol nt, int len)) = "cycle(<type(nt, ())>, )";
+    default str nodeLabel(Tree v) = "";
+    
+    lrel[str, value] edges(Tree t: appl(_, list[Tree] args))
+        = [<"src", t@\loc> | src, t@\loc?] + [<"", k> | Tree k <- args, include(k)];
+    lrel[str, value] edges(amb(set[Tree] alts)) = [<"", a> | Tree a <- alts];
+    lrel[str, value] edges(loc _) = [];
+    default lrel[str, value] edges(Tree _) = [];
+    
+    return ppvalue(t, nodeLabel, edges);
+}
+
+@synopsis{Pretty prints nodes and ADTs using ASCII art for the edges.}
+str prettyNode(node n, bool keywords = true) {
+    str nodeLabel(list[value] _) = "[…]";
+    str nodeLabel(set[value] _) = "{…}";
+    str nodeLabel(map[value, value] _) = "(…)";
+    str nodeLabel(value t) = "\<…\>"
+        when typeOf(t) is \tuple;
+    str nodeLabel(node k) = getName(k);
+    default str nodeLabel(value v) = "";
+    
+    lrel[str, value] edges(list[value] l) = [<"", x> | value x <- l];
+    lrel[str, value] edges(value t) = [<"", x> | value x <- carrier([t])]
+        when typeOf(t) is \tuple;
+    lrel[str, value] edges(set[value] s) = [<"", x> | value x <- s];
+    lrel[str, value] edges(map[str, value] m) = [<"", m[x]> | value x <- m];
+    lrel[str, value] edges(map[num, value] m) = [<"", m[x]> | value x <- m];
+    lrel[str, value] edges(map[loc, value] m) = [<"", m[x]> | value x <- m];
+    lrel[str, value] edges(map[node, value] m)
+        = [<"key", x>, <"value", m[x]> | value x <- m];
+    lrel[str, value] edges(node k)
+        = [<"", kid> | value kid <- getChildren(k)]
+        + [ | keywords, map[str, value] m := getKeywordParameters(k), str l <- m];
+    default lrel[str, value] edges(value _) = [];
+    
+    return ppvalue(n, nodeLabel, edges);
+}
+
+private
+str ppvalue(value e, str(value) nodeLabel, lrel[str, value](value) edges)
+    = " 
+      '";
+
+private
+str ppvalue_(
+    value e,
+    str(value) nodeLabel,
+    lrel[str, value](value) edges,
+    str indent = ""
+) {
+    lrel[str, value] kids = edges(e);
+    int i = 0;
+    
+    str indented(str last, str other, bool doSpace)
+        = " <if (i == size(kids) - 1) {><}else {><}><if (doSpace) {> <}>";
+    
+    return
+        "<for (<str l, value sub> <- kids) {><if (l != "") {>──→<}>
+        '";
+}
diff --git a/src/org/rascalmpl/library/lang/box/syntax/Box.rsc b/src/org/rascalmpl/library/lang/box/syntax/Box.rsc
index bf6ef897bb9..ae9928ebcc6 100644
--- a/src/org/rascalmpl/library/lang/box/syntax/Box.rsc
+++ b/src/org/rascalmpl/library/lang/box/syntax/Box.rsc
@@ -10,6 +10,8 @@
 @synopsis{An abstract declarative language for two dimensional text layout}
 module lang::box::\syntax::Box
 
+import List;
+
 @synopsis{Every kind of boxes encodes one or more parameterized two-dimensional text constraints.}
 @description{
 * `H` puts their elements next to each other one the same line separated by `hs` spaces.
@@ -22,9 +24,11 @@ module lang::box::\syntax::Box
 * `SPACE` produces `space` spaces
 * `L` produces A literal word. This word may only contain printable characters and no spaces; this is a required property that the formatting algorithm depends on for correctness.
 * `U` splices its contents in the surrounding box, for automatic flattening of overly nested structures in syntax trees.
-* `G` is an additional group-by feature that reduces tot the above core features
-* `SL` is a convenience box for separated syntax lists based on `G`
-* `NULL()` is the group that will disappear from its context, useful for skipping content. It is based on the `U` box.
+* `G` is an additional group-by feature for `list[Box]` that reduces tot the above core features. You can use it to wrap another
+box around every `gs` elements.
+* `AG` is an additional group-by feature for array `Row`s that reduces to the above core features. You can use it to wrap a `R` row
+around every `gs` elements and then construct an `A` around those rows.
+* `NULL()` is the group that will dissappear from its context, useful for skipping content. It is based on the `U` box.
 }
 @benefits{
 * Box expressions are a declarative mechanism to express formatting rules that are flexible enough to deal
@@ -38,20 +42,35 @@ set on every `I` Box according to the current preferences of the user.
 * `U(boxes)` is rendered as `H(boxes)` if it's the outermost Box.
 }
 data Box(int hs=1, int vs=0, int is=4)
-    = H(list[Box] boxes)
-    | V(list[Box] boxes)
-    | HOV(list[Box] boxes)
-    | HV(list[Box] boxes)
-    | I(list[Box] boxes)
-    | WD(list[Box] boxes)
-    | A(list[Row] rows, list[Alignment] columns=[l() | [R(list[Box] cs), *_] := rows, _ <- cs] /* learns the amount of columns from the first row */)
+    = H_(list[Box] boxes)
+    | V_(list[Box] boxes)
+    | HOV_(list[Box] boxes)
+    | HV_(list[Box] boxes)
+    | I_(list[Box] boxes) 
+    | WD_(list[Box] boxes)
+    | A_(list[Row] rows, Box rs=NULL(), list[Alignment] columns=[])
+    | AG_(list[Box] boxes, int gs=2, list[Alignment] columns=[], Box rs=NULL())
     | SPACE(int space)
     | L(str word)
-    | U(list[Box] boxes)
-    | G(list[Box] boxes, Box(list[Box]) op = H, int gs=2)
+    | U_(list[Box] boxes)
+    | G_(list[Box] boxes, bool backwards=false, int gs=2, Box op = H([]))
     | NULL()
     ;
 
+Box H(Box boxes..., int hs=1) = H_(boxes, hs=hs);
+Box V(Box boxes..., int vs=0) = V_(boxes, vs=vs);
+Box HOV(Box boxes..., int hs=1, int vs=0) = HOV_(boxes, hs=hs, vs=vs);
+Box HV(Box boxes..., int hs=1, int vs=0) = HV_(boxes, hs=hs, vs=vs);
+Box I(Box boxes...) = I_(boxes);
+Box WD(Box boxes...) = WD_(boxes);
+Box A(Row rows..., Box rs=NULL(), list[Alignment] columns=[])
+    = A_(rows, rs=rs, columns=columns);
+Box AG(Box boxes..., int gs=2, list[Alignment] columns=[], Box rs=NULL())
+    = AG_(boxes, gs=gs, columns=columns, rs=rs);
+Box U(Box boxes...) = U_(boxes);
+Box G(Box boxes..., bool backwards=false, int gs=2, Box op = H([]))
+    = G_(boxes, backwards=backwards, gs=gs, op=op);
+
 @synopsis{A row is a list of boxes that go into an `A` array/table.}
 @description{
 Rows do not have parameters. These are set on the `A` level instead,
@@ -59,6 +78,8 @@ or per cell Box.
 }
 data Row = R(list[Box] cells);
 
+// Row R(Box cells...) = _R(cells);
+
 data Alignment = l() | r() | c();  
 
 @synopsis{NULL can be used to return a Box that will completely disappear in the surrounding context.}
@@ -81,4 +102,74 @@ algorithm starts counting boxes and widths.
 * Do not use `NULL` for empty Row cells, unless you do want your cells aligned to the left and filled up to the right with empty H boxes.
 * NULL will be formatted as `H([])` if it's the outermost Box.
 }
-Box NULL() = U([]);
\ No newline at end of file
+Box NULL() = U([]);
+
+@synopsis{Convenience box for adding separators to an existing box list}
+@description{
+Each element is wrapped by the `op` operator together with the next separator.
+The resulting list is wrapped by a G box, of which the elements will be spliced
+into their context. 
+}
+Box SL(list[Box] boxes, Box sep, Box op = H([], hs=0))
+  = G([b, sep | b <- boxes][..-1], op=op, gs=2);
+
+@synopsis{Flatten and fold U and G boxes to simplify the Box structure}
+@description{
+U and G and AG boxes greatly simplify the Box tree before it is formatted. This
+happens "just-in-time" for efficiency reasons. However, from a Box tree
+with many U and G boxes it can become hard to see what the actual formatting
+constraints are going to be. 
+
+This function applies the semantics of G and U and returns a Box that renders
+exactly the same output, but with a lot less nested structure. 
+}
+@benefits{
+* useful to debug complex `toBox` mappings
+* formatting semantics preserving transformation
+}
+@pitfalls{
+* only useful for debugging purposes, because it becomes a pipeline bottleneck otherwise.
+}
+Box debUG(Box b) {
+    list[Box] groupBy([], int _gs, Box _op) = [];
+    list[Box] groupBy(list[Box] boxes:[Box _, *_], int gs, Box op)
+        = [op[boxes=boxes[..gs]], *groupBy(boxes[gs..], gs, op)];
+
+    list[Box] groupByBackward([], int _gs, Box _op) = [];
+    list[Box] groupByBackward(list[Box] boxes:[Box _, *_], int gs, Box op)
+        = [op[boxes=boxes[..size(boxes) mod gs]], *groupBy(boxes[size(boxes) mod gs..], gs, op)];
+        
+    list[Row] groupRows([], int _gs) = [];
+    list[Row] groupRows(list[Box] boxes:[Box _, *_], int gs)
+        = [R(boxes[..gs]), *groupRows(boxes[gs..], gs)];
+
+    return innermost visit(b) {
+        case [*Box pre, U_([*Box mid]), *Box post]           => [*pre, *mid, *post]
+        case G_(list[Box] boxes, gs=gs, op=op, backwards=bw) => U_(bw ? groupByBackward(boxes, gs, op) : groupBy(boxes, gs, op))
+        case AG_(list[Box] boxes, gs=gs, columns=cs, rs=rs)  => A(groupRows(boxes, gs), columns=cs, rs=rs)
+    }
+}
+
+@synopsis{Short-hand for `H(hs=0)`}
+Box H0(Box boxes...) = H_(boxes, hs=0);
+
+@synopsis{Short-hand for `H(hs=1)`}
+Box H1(Box boxes...) = H_(boxes, hs=1);
+
+@synopsis{Short-hand for `HOV(hs=0)``}
+Box HOV0(Box boxes ...) = HOV_(boxes, hs=0);
+
+@synopsis{Short-hand for `HV(hs=0)``}
+Box HV0(Box boxes...) = HV_(boxes, hs=0);
+
+@synopsis{Short-hand for indented H}
+Box IH(Box boxes..., int hs=1) = I(H_(boxes, hs=hs));
+
+@synopsis{Short-hand for indented V}
+Box IV(Box boxes..., int hs=1) = I(V_(boxes, hs=hs));
+
+@synopsis{Short-hand for indented HOV}
+Box IHOV(Box boxes..., int hs=1, int vs=0) = I(HOV_(boxes, hs=hs, vs=vs));
+
+@synopsis{Short-hand for indented HV}
+Box IHV(Box boxes..., int hs=1, int vs=0) = I(HV_(boxes, hs=hs, vs=vs));
\ No newline at end of file
diff --git a/src/org/rascalmpl/library/lang/box/util/Box2Text.rsc b/src/org/rascalmpl/library/lang/box/util/Box2Text.rsc
index 08026eaf619..9cf3f8cf253 100644
--- a/src/org/rascalmpl/library/lang/box/util/Box2Text.rsc
+++ b/src/org/rascalmpl/library/lang/box/util/Box2Text.rsc
@@ -32,15 +32,15 @@ This demonstrates the semantics of the main hard constraints:
 ```rascal-shell
 import lang::box::util::Box2Text;
 import lang::box::\syntax::Box;
-format(H([L("A"), L("B"), L("C")], hs=2))
-format(H([L("A"), L("B"), L("C")], hs=1))
-format(H([L("A"), L("B"), L("C")], hs=0))
-format(V([L("A"), L("B"), L("C")], vs=2))
-format(V([L("A"), L("B"), L("C")], vs=1))
-format(V([L("A"), L("B"), L("C")], vs=0))
-format(H([L("A"), V([L("B"), L("C")])]))
-format(H([L("A"), I([L("B")]), L("C")]))
-format(H([L("A"), V([L("B"), H([L("C"), L("D")])])]))
+format(H(L("A"), L("B"), L("C"), hs=2))
+format(H(L("A"), L("B"), L("C"), hs=1))
+format(H(L("A"), L("B"), L("C"), hs=0))
+format(V(L("A"), L("B"), L("C"), vs=2))
+format(V(L("A"), L("B"), L("C"), vs=1))
+format(V(L("A"), L("B"), L("C"), vs=0))
+format(H(L("A"), V(L("B"), L("C"))))
+format(H(L("A"), I(L("B")), L("C")))
+format(H(L("A"), V(L("B"), H(L("C"), L("D")))))
 ```
 
 The "soft" constraints change their behavior based on available horizontal room:
@@ -57,9 +57,9 @@ format(HOV([L("W") | i <- [0..30]]));
 
 By cleverly combining constraints, a specifically desired behavior is easy to achieve:
 ```rascal-shell,continue
-format(H([L("if"), H([L("("), L("true"), L(")")], hs=0), HOV([L("doSomething")])]))
-format(H([L("if"), H([L("("), L("true"), L(")")], hs=0), HOV([L("W") | i <- [0..30]])]))
-format(H([L("if"), H([L("("), L("true"), L(")")], hs=0), HV([L("W") | i <- [0..30]])]))
+format(H(L("if"), H(L("("), L("true"), L(")"), hs=0), HOV(L("doSomething"))))
+format(H(L("if"), H(L("("), L("true"), L(")"), hs=0), HOV([L("W") | i <- [0..30]])))
+format(H(L("if"), H(L("("), L("true"), L(")"), hs=0), HV([L("W") | i <- [0..30]])))
 ```
 }
 @pitfalls{
@@ -71,6 +71,33 @@ import util::Math;
 import List;
 import String;
 import lang::box::\syntax::Box;
+import IO;
+
+@synopsis{formatting options for ((Box2Text))}
+@description{
+    * `maxWidth` is the constraint that makes HV and HOV boxes switch to vertical mode (origin: ((Box2Text))
+    * `wrapAfter` is the lowerbound that makes HV and HOV stay horizontal (origin ((Box2Text)))
+    * `tabSize` is the default indentation used when an `I` box does not have an explicit `is` parameter (origin: ((util::LanguageServer)))
+    * `insertSpaces`, when set to true it prefers spaces over tabs, when set to false we use tabs for indentation (see `tabSize`)
+    * `trimTrailingWhitespace` when `true` the formatter can not leave spaces or tabs after the last non-whitespace character,
+    when false it does not matter. (origin: ((util::LanguageServer)))
+    * `insertFinalNewline`,  insert a newline character at the end of the file if one does not exist. (origin: ((util::LanguageServer)))
+    * `trimFinalNewlines`, trim all newlines after the final newline at the end of the file. (origin: ((util::LanguageServer))
+
+Note that there may be more FormattingOptions due to other elements of a formatting pipeline, such as ((layoutDiff)).
+}
+data FormattingOptions(
+    int maxWidth=120, 
+    int wrapAfter=90,
+    int tabSize = 4,
+    bool insertSpaces = true,
+    bool trimTrailingWhitespace = true,
+	bool insertFinalNewline = true,
+    bool trimFinalNewlines = true
+) = formattingOptions();
+
+@synopsis{Specialized alignments for the final column to implement `trimTrailingWhitespace`}
+private data Alignment = fl() | fc();
 
 @synopsis{Converts boxes into a string by finding an "optimal" two-dimensional layout}
 @description{
@@ -84,9 +111,26 @@ fit it will still be printed. We say `maxWidth` is a _soft_ constraint.
 * HV and HOV are the soft constraints that allow for better solutions, so use them where you can to allow for 
 flexible layout that can handle deeply nested expressions and statements.
 } 
-public str format(Box b, int maxWidth=80, int wrapAfter=70)
-    = "
-      '<}>";
+public str format(Box b, FormattingOptions opts = formattingOptions())
+    = finalNewlineOptions("
+                          '<}>", opts.insertFinalNewline, opts.trimFinalNewlines
+    );
+
+private str finalNewlineOptions(str lines, bool insertFinalNewline, bool trimFinalNewlines) {
+    if (!insertFinalNewline) {
+        lines = lines[..-1];
+
+        if (trimFinalNewlines, /^\s+$/ := lines) {
+           lines = prefix;
+        }
+    }
+    else if (trimFinalNewlines, /^\s*$/ := lines) {
+        lines = "\n";
+    }
+
+    return lines;
+}
+
 
 @synopsis{Box2text uses list[str] as intermediate representation of the output during formatting}
 @benefits{
@@ -100,8 +144,8 @@ ANSI escape codes, and characters like \r and \n in `L` boxes _will break_ the a
 alias Text = list[str];
 
 @synopsis{Converts boxes into list of lines (Unicode)}      
-public Text box2text(Box b, int maxWidth=80, int wrapAfter=70) 
-    = box2data(b, options(maxWidth=maxWidth, wrapAfter=wrapAfter));
+public Text box2text(Box b, FormattingOptions opts = formattingOptions()) 
+    = box2data(b, options(maxWidth=opts.maxWidth, wrapAfter=opts.wrapAfter, is=opts.tabSize, trimTrailingWhitespace=opts.trimTrailingWhitespace, insertSpaces=opts.insertSpaces));
 
 ////////// private functions below implement the intermediate data-structures
 ////////// and the constraint solver
@@ -117,17 +161,19 @@ This is used during the algorithm, not for external usage.
 * `wrapAfter` is the threshold criterion for line fullness, to go to the next line in a HV box and to switching
 between horizontal and vertical for HOV boxes.
 }
-data Options = options(
+private data Options = options(
     int hs = 1, 
     int vs = 0, 
     int is = 4, 
     int maxWidth = 80, 
-    int wrapAfter = 70
+    int wrapAfter = 70,
+    bool trimTrailingWhitespace = true,
+    bool insertSpaces = true
 );
 
 @synopsis{Quickly splice in any nested U boxes, and empty H, V, HV, I or HOV boxes}
 list[Box] u(list[Box] boxes) {
-    return [*((U(list[Box] nested) := b) ? u(nested) : [b]) | b <- boxes, !isDegenerate(b)];
+    return [*((U_(list[Box] nested) := b) ? u(nested) : [b]) | b <- boxes, !isDegenerate(b)];
 }
 
 @synopsis{Empty H, V, HOV, HV, I boxes should not lead to accidental extra separators in their context}
@@ -137,29 +183,28 @@ private bool isDegenerate(Box b) = b has boxes && b.boxes == [];
 private Text vv(Text a, Text b) = [*a, *b];
 
 @synopsis{Create a string of spaces just as wide as the parameter a}
-private str blank(str a) = right("", width(a));
+private str blank(str a, Options opts) = hskip(size(a), opts)[0];
 
 @synopsis{Computes a white line with the length of the last line of a}
-Text wd([])             = [];
-Text wd([*_, str x])    = [blank(x)];
-
-@synopsis{Computes the length of unescaped string s}
-private int width(str s) = size(s); 
+Text wd([], Options _)             = [];
+Text wd([*_, str x], Options opts) = [blank(x, opts)];
      
 @synopsis{Computes the maximum width of text t}
 private int twidth([]) = 0;
-private default int twidth(Text t) = max([width(line) | line <- t]);
+private default int twidth(Text t) = max([size(line) | line <- t]);
      
 @synopsis{Computes the length of the last line of t}
 private int hwidth([])             = 0;
-private int hwidth([*_, str last]) = width(last);
+private int hwidth([*_, str last]) = size(last);
 
 @synopsis{Prepends str a before text b, all lines of b will be shifted}
-private Text bar(str a, [])                = [a];
-private Text bar(str a, [str bh, *str bt]) = vv([""], prepend(blank(a), bt));
+private Text bar(str a, [], Options _) = [a];
+private Text bar(str a, [str bh, *str bt], Options opts) = vv([""], prepend(blank(a, opts), bt));
 
 @synopsis{Produce text consisting of a white line of length  n}
-Text hskip(int n) = [right("", n)];
+Text hskip(int n, Options opts) = opts.insertSpaces 
+    ? [right("", n)]
+    : ["\t<}> <}>"];
     
 @synopsis{Produces text consisting of n white lines at length 0}
 private Text vskip(int n) = ["" | _ <- [0..n]];
@@ -168,26 +213,29 @@ private Text vskip(int n) = ["" | _ <- [0..n]];
 private Text prepend(str a, Text b) = ["" | line <- b];
 
 @synopsis{Implements horizontal concatenation, also for multiple lines}
-private Text hh([], Text b)  = b;
-private Text hh(Text a, [])  = a;
-private Text hh([a], Text b) = bar(a, b);
+private Text hh([], Text b, Options _)  = b;
+private Text hh(Text a, [], Options _)  = a;
+private Text hh([a], Text b, Options opts) = bar(a, b, opts);
 
-private default Text hh(Text a, Text b) = vv(a[0..-1], bar(a[-1], b));
+private default Text hh(Text a, Text b, Options opts) = vv(a[0..-1], bar(a[-1], b, opts));
         
 @synopsis{Horizontal concatenation, but if the left text is empty return nothing.}
-private Text lhh([], Text _) = [];
-private default Text lhh(a, b) = hh(a, b);
+private Text lhh([], Text _, Options _) = [];
+private default Text lhh(a, b, Options opts) = hh(a, b, opts);
 
 @synopsis{Horizontal concatenation, but if the right text is empty return nothing.}
-private Text rhh(Text _, []) = [];
-private Text rhh(Text a, Text b) = hh(a, b);
+private Text rhh(Text _, [], Options _) = [];
+private Text rhh(Text a, Text b, Options opts) = hh(a, b, opts);
 
 @synopsis{Vertical concatenation, but if the right text is empty return nothing.}
 private Text rvv(Text _, []) = [];
 private default Text rvv(Text a, Text b) = vv(a,b);
     
-private Text LL(str s ) = [s]; 
-   
+private Text LL(str s) {
+    assert s != "" : "literal strings must never be empty for Box2Text to work correctly.";
+    return [s];
+}
+
 private Text HH([], Box _, Options _opts, int _m) = [];
 
 private Text HH(list[Box] b:[_, *_], Box _, Options opts, int m) {
@@ -196,20 +244,34 @@ private Text HH(list[Box] b:[_, *_], Box _, Options opts, int m) {
     for (a <- b) {
         Text t = \continue(a, H([]), opts, m);
         int s = hwidth(t); 
-        r = hh(t, rhh(hskip(opts.hs), r));
+        r = hh(t, rhh(hskip(opts.hs, opts), r, opts), opts);
         m  = m - s - opts.hs;
     }
    
     return r;
 }
 
+private Text GG(list[Box] boxes, Box c, Options opts, int m, int gs, Box op, bool backwards)
+    = \continue(c[boxes=groupBy(boxes, gs, op, backwards)], c, opts, m);
+
+public list[Box] groupBy(list[Box] boxes, int gs, Box op, false) = groupBy(boxes, gs, op);
+
+@synopsis{simulates grouping as-if done from the back, by starting to peel off the rest instead of grouping the rest at the end}
+public list[Box] groupBy(list[Box] boxes, int gs, Box op, true) 
+    = [op[boxes=boxes[..size(boxes) mod gs]], *groupBy(boxes[size(boxes) mod gs..], gs, op)];
+
+public list[Box] groupBy([], int _gs, Box _op) = [];
+
+public list[Box] groupBy(list[Box] boxes:[Box _, *_], int gs, Box op)
+    = [op[boxes=boxes[..gs]], *groupBy(boxes[gs..], gs, op)];
+
 private Text VV([], Box _c, Options _opts, int _m) = [];
 
 private Text VV(list[Box] b:[_, *_], Box c, Options opts, int m) {
     Text r = [];
     b = reverse(b);
     for (a <- b) {
-        if (V(_) !:= c || L("") !:= a) {
+        if (V_(_) !:= c || L("") !:= a) {
             Text t = \continue(a, V([]), opts, m);
             r = vv(t, rvv(vskip(opts.vs), r));
         }
@@ -219,11 +281,11 @@ private Text VV(list[Box] b:[_, *_], Box c, Options opts, int m) {
 
 private Text II([], Box _c, Options _opts, int _m) = [];
 
-private Text II(list[Box] b:[_, *_]              , c:H(list[Box] _), Options opts, int m) 
+private Text II(list[Box] b:[_, *_]              , c:H_(list[Box] _), Options opts, int m) 
     = HH(b, c, opts, m);
 
-private Text II(list[Box] b:[Box _, *Box _], c:V(list[Box] _), Options opts, int m) 
-    = rhh(hskip(opts.is), \continue(V(b, vs=opts.vs), c, opts, m - opts.is));
+private Text II(list[Box] b:[Box _, *Box _], c:V_(list[Box] _), Options opts, int m) 
+    = rhh(hskip(opts.is, opts), \continue(V(b, vs=opts.vs), c, opts, m - opts.is), opts);
 
 private Text WDWD([], Box _c , Options _opts, int _m) 
     = [];
@@ -232,13 +294,13 @@ private Text WDWD([Box head, *Box tail], Box c , Options opts, int m) {
     int h  = head.hs ? opts.hs;
     Text t = \continue(head, c, opts, m);
     int s  = hwidth(t);
-    return  hh(wd(t), rhh(hskip(h) , WDWD(tail, c, opts, m - s - h)));
+    return  hh(wd(t, opts), rhh(hskip(h, opts) , WDWD(tail, c, opts, m - s - h), opts), opts);
 }
 
 private Text ifHOV([], Box b,  Box c, Options opts, int m) = [];
 
 private Text ifHOV(Text t:[str head], Box b,  Box c, Options opts, int m) 
-    = width(head) <= m ? t : \continue(b, c, opts, m);
+    = size(head) <= m ? t : \continue(b, c, opts, m);
 
 private Text ifHOV(Text t:[str head, str _, *str_], Box b,  Box c, Options opts, int m)
     = \continue(b, c, opts, m);
@@ -255,21 +317,21 @@ private Text HVHV(Text T, int s, Text a, Box A, list[Box] B, Options opts, int m
 
     if (size(a) > 1) { // Multiple lines 
         Text T1 = \continue(A, V([]), opts, m-i);
-        return vv(T, rvv(vskip(v), HVHV(T1, m-hwidth(T1), B, opts, m, H([]))));
+        return vv(T, rvv(vskip(v), HVHV(T1, m-hwidth(T1), B, opts, m, H_([]))));
     }
 
     if (n <= s) {  // Box A fits in current line
-        return HVHV(hh(lhh(T, hskip(h)), a), s-n, B, opts, m, H([]));
+        return HVHV(hh(lhh(T, hskip(h,opts), opts), a, opts), s-n, B, opts, m, H_([]));
     }
     else {
-        n -= h; // n == width(a)
+        n -= h; // n == size(a)
         if  (i + n < m) { // Fits in the next line, not in current line
             Text T1 =\continue(A, V([]), opts, m-i);
-            return vv(T, rvv(vskip(v), HVHV(T1, m-n-i, B, opts, m, H([]))));
+            return vv(T, rvv(vskip(v), HVHV(T1, m-n-i, B, opts, m, H_([]))));
         }
         else { // Doesn't fit in either lines
             Text T1 = \continue(A, V([]), opts, m-i);
-            return vv(T, rvv(vskip(v), HVHV(T1, m-hwidth(T1), B, opts, m, H([]))));
+            return vv(T, rvv(vskip(v), HVHV(T1, m-hwidth(T1), B, opts, m, H_([]))));
         }
     }
 }
@@ -285,47 +347,33 @@ private Text HVHV([], Box _, Options opts, int m)
     = [];
 
 private Text HVHV(list[Box] b:[Box head], Box _, Options opts, int m) 
-    = \continue(head, V([]), opts, m);
+    = \continue(head, V_([]), opts, m);
 
 private Text HVHV(list[Box] b:[Box head, Box next, *Box tail], Box _, Options opts, int m) {
-    Text T =  \continue(head, V([]), opts, m);  
-    return HVHV(T, m - hwidth(T), [next, *tail], opts, m, H([]));
+    Text T =  \continue(head, V_([]), opts, m);  
+    return HVHV(T, m - hwidth(T), [next, *tail], opts, m, H_([]));
 }
 
-// empty lists do not need grouping
-private Text GG([], Box(list[Box]) op, int gs, Box c, Options opts, int m)
-    = \continue(U([]), c, opts, m);
-
-// the last elements are smaller than the group size, just wrap them up and finish
-private Text GG([*Box last], Box(list[Box]) op, int gs, Box c, Options opts, int m) 
-    = \continue(op(u(last))[hs=opts.hs][vs=opts.vs][is=opts.is], c, opts, m)
-    when size(last) < gs;
-
-// we pick the head of (size group size) and then continue with the rest
-private Text GG([*Box heads, *Box tail], Box(list[Box]) op, int gs, Box c, Options opts, int m) 
-    = \continue(op(heads)[hs=opts.hs][vs=opts.vs][is=opts.is], NULL(), opts, m)
-    + \continue(G(tail, op=op, hs=opts.hs, vs=opts.vs, is=opts.is, gs=gs), c, opts, m)
-    when size(heads) == gs;
-
 private Text continueWith(Box b:L(str s)         , Box c, Options opts, int m) = LL(s);
-private Text continueWith(Box b:H(list[Box] bl)  , Box c, Options opts, int m) = HH(u(bl), c, opts, m); 
-private Text continueWith(Box b:V(list[Box] bl)  , Box c, Options opts, int m) = VV(u(bl), c, opts, m);
-private Text continueWith(Box b:I(list[Box] bl)  , Box c, Options opts, int m) = II(u(bl), c, opts, m);
-private Text continueWith(Box b:WD(list[Box] bl) , Box c, Options opts, int m) = WDWD(u(bl), c, opts, m);
-private Text continueWith(Box b:HOV(list[Box] bl), Box c, Options opts, int m) = HOVHOV(u(bl), c, opts, m);
-private Text continueWith(Box b:HV(list[Box] bl) , Box c, Options opts, int m) = HVHV(u(bl), c, opts, m);
-private Text continueWith(Box b:SPACE(int n)     , Box c, Options opts, int m) = hskip(n);
+private Text continueWith(Box b:H_(list[Box] bl)  , Box c, Options opts, int m) = HH(u(bl), c, opts, m); 
+private Text continueWith(Box b:V_(list[Box] bl)  , Box c, Options opts, int m) = VV(u(bl), c, opts, m);
+private Text continueWith(Box b:I_(list[Box] bl)  , Box c, Options opts, int m) = II(u(bl), c, opts, m);
+private Text continueWith(Box b:WD_(list[Box] bl) , Box c, Options opts, int m) = WDWD(u(bl), c, opts, m);
+private Text continueWith(Box b:HOV_(list[Box] bl), Box c, Options opts, int m) = HOVHOV(u(bl), c, opts, m);
+private Text continueWith(Box b:HV_(list[Box] bl) , Box c, Options opts, int m) = HVHV(u(bl), c, opts, m);
+private Text continueWith(Box b:SPACE(int n)     , Box c, Options opts, int m) = hskip(n, opts);
 
 // This is a degenerate case, an outermost U-Box without a wrapper around it.
-private Text continueWith(Box b:U(list[Box] bl)  , Box c, Options opts, int m) = HH(u(bl), c, opts, m);
+private Text continueWith(Box b:U_(list[Box] bl)  , Box c, Options opts, int m) = HH(u(bl), c, opts, m);
 
-private Text continueWith(Box b:A(list[Row] rows), Box c, Options opts, int m) 
-    = AA(rows, c, b.columns, opts, m);
+private Text continueWith(Box b:G_(list[Box] bl), Box c, Options opts, int m)
+    = GG(u(bl), c, opts, m,  b.gs, b.op, b.backwards);
 
-private Text continueWith(Box b:G(list[Box] bl), Box c, Options opts, int m) = GG(u(bl), b.op, b.gs, c, opts, m);
+private Text continueWith(Box b:A_(list[Row] rows), Box c, Options opts, int m) 
+    = AA(rows, c, b.columns, b.rs, opts, m);
 
-@synopsis{General shape of a Box operator, as a parameter to `G`}
-private alias BoxOp = Box(list[Box]);
+private Text continueWith(Box b:AG_(list[Box] boxes), Box c, Options opts, int m) 
+    = AAG(u(boxes), b.gs, b.columns, b.rs, c, opts, m);
 
 @synopsis{Option inheritance layer; then continue with the next box.}
 @description{
@@ -333,7 +381,7 @@ The next box is either configured by itself. Options are transferred from the
 box to the opts parameter for easy passing on to recursive calls.
 }
 private Text \continue(Box b, Box c, Options opts, int m)
-    = continueWith(b, c, opts[hs=b.hs][vs=b.vs][is=b.is], m);
+    = continueWith(b, c, opts[hs=b.hs][vs=b.vs][is=(b.is?)?b.is:opts.is], m);
 
 /* ------------------------------- Alignment ------------------------------------------------------------*/
 
@@ -358,37 +406,107 @@ private int Acolumns(list[Row] rows) = (0 | max(it, size(row.cells)) | row <- ro
 
 @synopsis{Compute the maximum cell width for each column in an array}
 private list[int] Awidth(list[list[Box]] rows) 
-    = [(0 | max(it, row[col].width) | row <- rows ) | int col <- [0..size(head(rows))]];
+    = [(0 | max(it, row[col].width) | row <- rows, col < size(row) ) | int col <- [0..size(head(rows))]];
 
 @synopsis{Adds empty cells to every row until every row has the same amount of columns.}
-list[Row] AcompleteRows(list[Row] rows, int columns=Acolumns(rows))
-    = [ R(u([*row.cells, *[H([]) | _ <- [0..columns - size(row.cells)]]])) | row <- rows];
+list[Row] AcompleteRows(list[Row] rows, int columns=Acolumns(rows), Box rs=NULL())
+    = [ R(u([*row.cells[..-1], H_([row.cells[-1], rs],hs=0), *[SPACE(1) | _ <- [0..columns - size(row.cells)]]])) | row <- rows[..-1]]
+    + [ R(u([*rows[-1].cells, *[SPACE(1) | _ <- [0..columns - size(rows[-1].cells)]]]))] ;
 
 @synopsis{Helper function for aligning Text inside an array cell}
 private Box align(l(), Box cell, int maxWidth) = maxWidth - cell.width > 0 
-    ? H([cell, SPACE(maxWidth - cell.width)], hs=0)
+    ? H_([cell, SPACE(maxWidth - cell.width)], hs=0)
     : cell;
 
+@synopsis{Helper function for aligning Text inside an array cell}
 private Box align(r(), Box cell, int maxWidth) = maxWidth - cell.width > 0 
-    ? H([SPACE(maxWidth - cell.width), cell], hs=0)
+    ? H_([SPACE(maxWidth - cell.width), cell], hs=0)
     : cell;
 
 private Box align(c(), Box cell, int maxWidth) = maxWidth - cell.width > 1 
-    ? H([SPACE((maxWidth - cell.width) / 2),  cell, SPACE((maxWidth - cell.width) / 2)], hs=0)
+    ? H_([SPACE((maxWidth - cell.width) / 2),  cell, SPACE((maxWidth - cell.width) / 2)], hs=0)
     : maxWidth - cell.width == 1 ?
         align(l(), cell, maxWidth)
         : cell;
 
-private Text AA(list[Row] table, Box c, list[Alignment] alignments, Options opts, int m) {
-    list[list[Box]] rows = RR(AcompleteRows(table), c, opts, m);
+
+// the last left box should not fill up to the right for the next non-existing column, to help implement `trimTrailingWhitespace`
+private Box align(fl(), Box cell, int maxWidth) = cell;
+
+// the last center box should not fill up to the right, only to the left to help implement `trimTrailingWhitespace`
+private Box align(fc(), Box cell, int maxWidth) = maxWidth - cell.width > 1 
+    ? H_([SPACE((maxWidth - cell.width) / 2),  cell], hs=0)
+    : maxWidth - cell.width == 1 ?
+        align(l(), cell, maxWidth)
+        : cell;
+
+private Text AA(list[Row] table, Box c, list[Alignment] alignments, Box rs, Options opts, int m) {
+    if (table == []) {
+        return [];
+    }
+
+    // first flatten any nested U cell lists into the Rows
+    table = [R(u(r.cells)) | Row r <- table];
+
+    // we remove any H-V backtracking because table cells are too small anyway, generally.
+    // so we prefer the less wide V over HOV and HV. This boosts efficiency radically, because
+    // later, ever cell will be formatted individually to an optimal width, and measured, before we even start
+    // to format the table. Then the same cells will be formatted again from scratch. By removing the
+    // backtracking, larger tables (like reified grammars) become doable.
+    table = visit (table) {
+        case Box b:HOV_(list[Box] boxes) => V_(boxes, vs=b.vs)
+        case Box b:HV_(list[Box] boxes)  => V_(boxes, vs=b.vs)
+    }
+
+    // then we can know the number of columns
+    int maxColumns = Acolumns(table);
+
+    // then we fill each row up to the maximum of columns
+    list[list[Box]] rows = RR(AcompleteRows(table, columns=maxColumns, rs=rs), c, opts, m);
+
+    // and we infer alignments where not provided
+    alignments = AcompleteAlignments(alignments, maxColumns);
+
+    if (opts.trimTrailingWhitespace) {
+        alignments = AfinalColumnSpecials(alignments);
+    }
+
+    // finally we compute alignment information
     list[int] maxWidths  = Awidth(rows);
-    
-    return \continue(V([
-        H([align(al, cell, mw) |  <- zip3(row, alignments, maxWidths)]) 
-    | row <- rows
-    ]),c, opts, m);
+
+    try {
+        // A row is simply an H box where each cell is filled with enough spaces to align for the next column
+        return \continue(V_([ 
+            H_([align(al, cell, mw) |  <- zip3(row, alignments, maxWidths)]) | row <- rows]), c, opts, m);
+    }
+    catch IllegalArgument(_, "List size mismatch"): {
+        throw IllegalArgument("Array alignments size is  while there are  columns.");
+    }
 }
 
+private Text AAG([], int _gs, list[Alignment] _columns, Box _rs, Box _c, Options _opts, int _m) = [];
+
+private Text AAG(list[Box] boxes:[Box _, *_], int gs, list[Alignment] columns, Box rs, Box c, Options opts, int m)
+    = \continue(A(groupRows(boxes, gs), columns=columns, rs=rs), c, opts, m);
+
+private list[Row] groupRows([], int _gs) = [];
+
+private list[Row] groupRows(list[Box] boxes:[Box _, *_], int gs)
+    = [R(boxes[..gs]), *groupRows(boxes[gs..], gs)];
+
+@synopsis{Cuts off and extends the alignment spec to the width of the table}
+@description{
+* if too few columns are specified: `l()`'s are added accordingly
+* if too many columns are specified: they are cut off from the right
+}
+private list[Alignment] AcompleteAlignments(list[Alignment] alignments, int maxColumns) 
+    = [*alignments[..maxColumns], *[l() | _ <- [0..maxColumns - size(alignments)]]];
+
+@synopsis{Translate l() and c() to fl() and fc() for the final columns to help implement `trimTrailingWhitespace`}
+private list[Alignment] AfinalColumnSpecials([*Alignment pre, l()]) = [*pre, fl()];
+private list[Alignment] AfinalColumnSpecials([*Alignment pre, c()]) = [*pre, fc()];
+private default list[Alignment] AfinalColumnSpecials(list[Alignment] as) = as;
+
 @synopsis{Check soft limit for HV and HOV boxes}
 // TODO this seems to ignore SPACE boxes?
 private bool noWidthOverflow(list[Box] hv, Options opts) 
@@ -396,46 +514,88 @@ private bool noWidthOverflow(list[Box] hv, Options opts)
 
 @synopsis{Changes all HV boxes that do fit horizontally into hard H boxes.}
 private Box applyHVconstraints(Box b, Options opts) = innermost visit(b) {
-    case HV(boxes, hs=h, is=i, vs=v) => H(boxes, hs=h, is=i, vs=v) 
+    case Box B:HV_(list[Box] boxes, hs=h, is=i, vs=v) => H_(boxes, hs=h, is=(B.is?)?i:opts.is, vs=v) 
         when noWidthOverflow(boxes, opts)
 };
 
 @synopsis{Changes all HOV boxes that do fit horizontally into hard H boxes,
 and the others into hard V boxes.}
 private Box applyHOVconstraints(Box b, Options opts) = innermost visit(b) {
-    case HOV(boxes, hs=h, is=i, vs=v) => noWidthOverflow(boxes, opts) 
-        ? H(boxes, hs=h, is=i, vs=v)
-        : V(boxes, hs=h, is=i, vs=v)      
+    case Box B:HOV_(list[Box] boxes, hs=h, is=i, vs=v) => noWidthOverflow(boxes, opts) 
+        ? H_(boxes, hs=h, is=(B.is?)?i:opts.is, vs=v)
+        : V_(boxes, hs=h, is=(B.is?)?i:opts.is, vs=v)      
 };
 
 @synopsis{Workhorse, that first applies hard HV and HOV limits and then starts the general algorithm}
 private Text box2data(Box b, Options opts) {
     b = applyHVconstraints(b, opts);
     b = applyHOVconstraints(b, opts);
-    return \continue(b, V([]), options(), opts.maxWidth);
+    return \continue(b, V_([]), options(is=opts.is, insertSpaces=opts.insertSpaces, trimTrailingWhitespace=opts.trimTrailingWhitespace), opts.maxWidth);
 }
     
 ///////////////// regression tests ////////////////////////////////
 
 test bool horizontalPlacement2()
-    = format(H([L("A"), L("B"), L("C")], hs=2))
+    = format(H(L("A"), L("B"), L("C"), hs=2))
     == "A  B  C
        '";
 
 test bool horizontalPlacement3()
-    = format(H([L("A"), L("B"), L("C")], hs=3))
+    = format(H(L("A"), L("B"), L("C"), hs=3))
     == "A   B   C
        '";
 
+test bool horizontalIndentIsNoop1() 
+    = format(H(L("A"), I(L("B"))))
+    == "A B
+       '";
+
+test bool horizontalIndentIsNoop2() 
+    = format(HV(L("A"), I(L("B"))))
+    == "A B
+       '";
+
+test bool horizontalIndentIsNoop3() 
+    = format(HOV(L("A"), I(L("B"))))
+    == "A B
+       '";
+
+test bool emptyBoxesNoExtraSpacing1()
+    = format(H(L("A"), H(), L("B")))
+    == "A B
+       '";
+
+test bool emptyBoxesNoExtraSpacing2()
+    = format(H(L("A"), V(), L("B")))
+    == "A B
+       '";
+
+test bool emptyBoxesNoExtraSpacing3()
+    = format(H(L("A"), I(), L("B")))
+    == "A B
+       '";
+
+test bool emptyBoxesNoExtraSpacing4()
+    = format(V(L("A"), H(), L("B")))
+    == "A
+       'B
+       '";
+
+test bool emptyBoxesNoExtraSpacing5()
+    = format(V(L("A"), V(), L("B")))
+    == "A
+       'B
+       '";
+
 test bool verticalPlacement0()
-    = format(V([L("A"), L("B"), L("C")], vs=0))
+    = format(V(L("A"), L("B"), L("C"), vs=0))
     == "A
        'B
        'C
        '";
 
 test bool verticalPlacement1()
-    = format(V([L("A"), L("B"), L("C")], vs=1))
+    = format(V(L("A"), L("B"), L("C"), vs=1))
     == "A
        '
        'B
@@ -444,14 +604,14 @@ test bool verticalPlacement1()
        '";
 
 test bool verticalIndentation2()
-    = format(V([L("A"), I([L("B")]), L("C")]))
+    = format(V(L("A"), I(L("B")), L("C")))
     == "A
        '    B
        'C
        '";
 
 test bool blockIndent()
-    = format(V([L("A"), I([V([L("B"), L("C")])]), L("D")]))
+    = format(V(L("A"), I(V(L("B"), L("C"))), L("D")))
     == "A
        '    B
        '    C
@@ -459,52 +619,52 @@ test bool blockIndent()
        '";
 
 test bool wrappingIgnoreIndent()
-    = format(HV([L("A"), I([L("B")]), L("C")], hs=0), maxWidth=2, wrapAfter=2)
+    = format(HV(L("A"), I(L("B")), L("C"), hs=0), opts=formattingOptions(maxWidth=2, wrapAfter=2))
     == "AB
        'C
        '";
 
 test bool wrappingWithIndent()
-    = format(HV([L("A"), I([L("B")]), I([L("C")])], hs=0), maxWidth=2, wrapAfter=2)
+    = format(HV(L("A"), I(L("B")), I(L("C")), hs=0),opts=formattingOptions( maxWidth=2, wrapAfter=2))
     == "AB
        '    C
        '";
 
 test bool multiBoxIndentIsVertical()
-    = format(I([L("A"), L("B")]))
+    = format(I(L("A"), L("B")))
     == "    A
        '    B
        '";
 
 test bool flipping1NoIndent()
-    = format(HOV([L("A"), L("B"), L("C")], hs=0, vs=0), maxWidth=2, wrapAfter=2)
+    = format(HOV(L("A"), L("B"), L("C"), hs=0, vs=0), opts=formattingOptions(maxWidth=2, wrapAfter=2))
     == "A
        'B
        'C
        '";
 
 test bool horizontalOfOneVertical()
-    = format(H([L("A"), V([L("B"), L("C")])]))
+    = format(H(L("A"), V(L("B"), L("C"))))
     == "A B
        '  C
        '";
 
 test bool stairCase()
-    = format(H([L("A"), V([L("B"), H([L("C"), V([L("D"), H([L("E"), L("F")])])])])]))
+    = format(H(L("A"), V(L("B"), H(L("C"), V(L("D"), H(L("E"), L("F")))))))
     == "A B
        '  C D
        '    E F
        '";
 
 test bool simpleTable() 
-    = format(A([R([L("1"),L("2"),L("3")]),R([L("4"), L("5"), L("6")]),R([L("7"), L("8"), L("9")])]))
+    = format(A(R([L("1"),L("2"),L("3")]),R([L("4"), L("5"), L("6")]),R([L("7"), L("8"), L("9")])))
     == "1 2 3
        '4 5 6
        '7 8 9
        '";
 
 test bool simpleAlignedTable() 
-    = format(A([R([L("1"),L("2"),L("3")]),R([L("44"), L("55"), L("66")]),R([L("777"), L("888"), L("999")])], 
+    = format(A(R([L("1"),L("2"),L("3")]),R([L("44"), L("55"), L("66")]),R([L("777"), L("888"), L("999")]), 
                 columns=[l(),c(),r()]))
     == "1    2    3
        '44  55   66
@@ -512,7 +672,7 @@ test bool simpleAlignedTable()
        '";
 
 test bool simpleAlignedTableDifferentAlignment() 
-    = format(A([R([L("1"),L("2"),L("3")]),R([L("44"), L("55"), L("66")]),R([L("777"), L("888"), L("999")])], 
+    = format(A(R([L("1"),L("2"),L("3")]),R([L("44"), L("55"), L("66")]),R([L("777"), L("888"), L("999")]), 
                 columns=[r(),c(),l()]))
     == "  1  2  3  
        ' 44 55  66 
@@ -520,54 +680,51 @@ test bool simpleAlignedTableDifferentAlignment()
        '";
 
 test bool WDtest() {
-    L1 = H([L("aap")]           , hs=0);
-    L2 = H([WD([L1]), L("noot")], hs=0);
-    L3 = H([WD([L2]), L("mies")], hs=0);
+    L1 = H(L("aap")         , hs=0);
+    L2 = H(WD(L1), L("noot"), hs=0);
+    L3 = H(WD(L2), L("mies"), hs=0);
 
-    return format(V([L1, L2, L3]))
+    return format(V(L1, L2, L3))
         == "aap
            '   noot
            '       mies
            '";
 }
 
-test bool groupBy() {
+test bool groupByTest() {
     lst  = [L("") | i <- [0..10]];
-    g1   = G(lst, op=H, gs=3);
-    lst2 = [H([L(""), L(""), L("")]) | i <- [0,3..7]] + [H([L("9")])];
+    g1   = G(lst, op=H(), gs=3);
+    lst2 = [H(L(""), L(""), L("")) | i <- [0,3..7]] + [H(L("9"))];
 
-    return format(V([g1])) == format(V(lst2));
+    return format(V(g1)) == format(V(lst2));
 }
 
-test bool noDegenerateHSeparators()
-    = format(H([L("a"),H([]),L("b")])) 
-    == "a b
-       '";
+test bool groupByBackwardsTest() {
+    lst  = [L("") | i <- [0..10]];
+    g1   = G(lst, op=H(), gs=3, backwards=true);
+    lst2 = [H(L("0"))] + [H(L(""), L(""), L("")) | i <- [1, 4..10]];
 
-test bool noDegenerateVSeparators()
-    = format(V([L("a"),H([]),L("b")])) 
-    == "a
-       'b
-       '";
+    return format(V([g1])) == format(V(lst2));
+}
 
 test bool noDegenerateHVSeparators1()
-    = format(HV([L("a"),V([]),L("b")])) 
+    = format(HV(L("a"),V(),L("b"))) 
     == "a b
        '";
 
 test bool noDegenerateHVSeparators2()
-    = format(HV([L("a"),V([]),L("b")]), maxWidth=1, wrapAfter=1) 
+    = format(HV(L("a"),V(),L("b")), opts=formattingOptions(maxWidth=1, wrapAfter=1)) 
     == "a
        'b
        '";
 
 test bool noDegenerateHOVSeparators1()
-    = format(HOV([L("a"),V([]),L("b")])) 
+    = format(HOV(L("a"),V(),L("b"))) 
     == "a b
        '";
 
 test bool noDegenerateHOVSeparators2()
-    = format(HOV([L("a"),V([]),L("b")]), maxWidth=1, wrapAfter=1) 
+    = format(HOV(L("a"),V(),L("b")), opts=formattingOptions(maxWidth=1, wrapAfter=1))
     == "a
        'b
        '";
diff --git a/src/org/rascalmpl/library/lang/box/util/Tree2Box.rsc b/src/org/rascalmpl/library/lang/box/util/Tree2Box.rsc
index bf580f16f4f..21ba5f513ec 100644
--- a/src/org/rascalmpl/library/lang/box/util/Tree2Box.rsc
+++ b/src/org/rascalmpl/library/lang/box/util/Tree2Box.rsc
@@ -47,7 +47,7 @@ b = toBox(program);
 import lang::box::util::Box2Text;
 format(b)
 // If you are not happy, then you should produce a specialization:
-Box toBox((Program) `begin  <{Statement ";"}* body> end`, FormatOptions opts=formatOptions())
+Box toBox((Program) `begin  <{Statement ";"}* body> end`, FormattingOptions opts=formattingOptions())
     = V([
         L("begin"),
         I([
@@ -67,12 +67,12 @@ module lang::box::util::Tree2Box
 import ParseTree;
 import lang::box::\syntax::Box;
 import String;
-import IO;
+
 
 @synopsis{Configuration options for toBox}
-data FormatOptions = formatOptions(
+data FormattingOptions(
     CaseInsensitivity ci = asIs()
-);
+) = formattingOptions();
 
 @synopsis{Normalization choices for case-insensitive literals.}
 data CaseInsensitivity
@@ -92,8 +92,6 @@ by the user is necessary.
 default Box toBox(t:appl(Production p, list[Tree] args), FO opts = fo()) {
     // the big workhorse switch identifies all kinds of special cases for shapes of
     // grammar rules, and accidental instances (emptiness, only whitespace, etc.)
-    Symbol _nl = #[\n].symbol;
-    Symbol notNl = #![\n].symbol;
     
     switch () {
         // nothing should not produce additional spaces
@@ -101,39 +99,48 @@ default Box toBox(t:appl(Production p, list[Tree] args), FO opts = fo()) {
             return NULL();
 
         // literals are printed as-is
-        case : 
-            return L("");
+        case : {
+            str yield =  "";
+            return yield != "" ? L(yield) : NULL();
+        }
         
         // case-insensitive literals are optionally normalized
-        case : 
-            return L(ci("", opts.ci));
+        case : {
+            str yield =  ""; 
+            return yield != "" ?  L(ci(opts.ci, "")) : NULL();
+        }
         
         // non-existing content should not generate accidental spaces
         case : 
-            return NULL();
+            return NULL(); 
+
+        case : 
+            return U([toBox(present)]); 
         
         // non-separated lists should stick without spacing (probably lexical)
         case :
             return H([toBox(e, opts=opts) | e <- elements], hs=0);
 
+         // non-separated lists should stick without spacing (probably lexical)
         case :  
             return H([toBox(e, opts=opts) | e <- elements], hs=0);
 
-        // comma's are usually for parameters separation
+        // comma's are usually for parameters separation. leaving it to 
+        // parent to wrap the box in the right context.
         case :
-            return HOV([
+            return U([
                 H([
-                    toBox(elements[i], opts=opts),
-                    *[H([toBox(elements[i+2], opts=opts)], hs=1) | i + 2 < size(elements)]
+                    toBox(elements[i], opts=opts),     // element
+                    *[L(",") | i + 2 < size(elements)] // separator
                 ], hs=0) | int i <- [0,4..size(elements)]
             ]);
 
         // comma's are usually for parameters separation
         case :
-            return HOV([
+            return HV([
                 H([
-                    toBox(elements[i], opts=opts),
-                    *[H([toBox(elements[i+2], opts=opts)], hs=1) | i + 2 < size(elements)]
+                    toBox(elements[i], opts=opts),     // element
+                    *[L(",") | i + 2 < size(elements)] // separator
                 ], hs=0) | int i <- [0,4..size(elements)]
             ]);
 
@@ -141,8 +148,8 @@ default Box toBox(t:appl(Production p, list[Tree] args), FO opts = fo()) {
         case :
             return V([
                 H([
-                    toBox(elements[i], opts=opts),
-                    *[H([toBox(elements[i+2], opts=opts)], hs=1) | i + 2 < size(elements)]
+                    toBox(elements[i], opts=opts),     // element
+                    *[L(";") | i + 2 < size(elements)] // separator
                 ], hs=0) | int i <- [0,4..size(elements)]
             ]);
 
@@ -150,17 +157,16 @@ default Box toBox(t:appl(Production p, list[Tree] args), FO opts = fo()) {
         case :
             return V([
                 H([
-                    toBox(elements[i], opts=opts),
-                    *[H([toBox(elements[i+2], opts=opts)], hs=1) | i + 2 < size(elements)]
+                    toBox(elements[i], opts=opts),     // element
+                    *[toBox(elements[i+2]) | i + 2 < size(elements)] // separator
                 ], hs=0) | int i <- [0,4..size(elements)]
             ]);
 
-        // semi-colons are usually for parameters separation
         case :
             return V([
                 H([
-                    toBox(elements[i], opts=opts),
-                    *[H([toBox(elements[i+2], opts=opts)], hs=1) | i + 2 < size(elements)]
+                    toBox(elements[i], opts=opts),     // element
+                    *[L(";") | i + 2 < size(elements)] // separator
                 ], hs=0) | int i <- [0,4..size(elements)]
             ]);
 
@@ -168,75 +174,89 @@ default Box toBox(t:appl(Production p, list[Tree] args), FO opts = fo()) {
         case :
             return V([
                 H([
-                    toBox(elements[i], opts=opts),
-                    *[H([toBox(elements[i+2], opts=opts)], hs=1) | i + 2 < size(elements)]
+                    toBox(elements[i], opts=opts),     // element
+                    *[toBox(elements[i+2]) | i + 2 < size(elements)] // separator
+                ], hs=0) | int i <- [0,4..size(elements)]
+            ]);
+
+        // now we have any other literal as separator
+        case :
+            return U([
+                H([
+                    toBox(elements[i], opts=opts),     // element
+                    *[toBox(elements[i+2]) | i + 2 < size(elements)] // separator
                 ], hs=0) | int i <- [0,4..size(elements)]
             ]);
 
-        case :
-            return V([G([toBox(e, opts=opts) | e <- elements], gs=4, hs=0, op=H)], hs=1);
+        case :
+            return U([
+                H([
+                    toBox(elements[i], opts=opts),     // element
+                    *[toBox(elements[i+2]) | i + 2 < size(elements)] // separator
+                ], hs=0) | int i <- [0,4..size(elements)]
+            ]);
+
+        
+        // this is a normal list
+        case :
+            return U([toBox(elements[i], opts=opts) | int i <- [0,2..size(elements)]]);
+
+        // this is likely a lexical  
+        case :
+            return H([toBox(e, opts=opts) | e <- elements], hs=0);
 
-        case :
-            return V([G([toBox(e, opts=opts) | e <- elements], gs=4, hs=0, op=H)], hs=1);
-          
-        // with only one separator it's probably a lexical
-        case :
-            return V([G([toBox(e, opts=opts) | e <- elements], gs=2, hs=0, op=H)], hs=0);
+        // this is likely a lexical  
+        case :
+            return H([toBox(e, opts=opts) | e <- elements], hs=0);
+    
+        // this is a normal list
+        case :
+            return U([toBox(elements[i], opts=opts) | int i <- [0,2..size(elements)]]);
 
-        case :
-            return V([G([toBox(e, opts=opts) | e <- elements], gs=2, hs=0, op=H)], hs=0);
+        // this is likely a lexical  
+        case :
+            return H([toBox(e, opts=opts) | e <- elements], hs=0);
 
+        // this is likely a lexical  
+        case :
+            return H([toBox(e, opts=opts) | e <- elements], hs=0);
+    
         // We remove all layout node positions to make the number of children predictable
         // Comments can be recovered by `layoutDiff`. By not recursing into layout
         // positions `toBox` becomes more than twice as fast.
         case :
             return NULL();
 
-        // if we are given a comment node, then we can format it here for use by layoutDiff
-        case :
-            return V([
-                    H([toBox(elements[0], opts=opts), 
-                        H([L(e) | e <- words("")], hs=1)
-                    ], hs=1)
-                ]);
-
-        // if we are given a comment node, then we can pretty print it here for use by layoutDiff     
-        case :
-            return V([
-                    H([toBox(elements[0], opts=opts), 
-                        H([L(w) | e <- elements[1..], w <- words("")], hs=1)
-                    ], hs=1)
-                ]);
-
-        // multiline comments are rewrapped for the sake of readability and fitting on the page
-        case :
-            return HV([toBox(elements[0], opts=opts),                     // recurse in case its a ci literal 
-                      *[L(w) | e <- elements[1..-1], w <- words("")], // wrap a nice paragraph
-                      toBox(elements[-1], opts=opts)                     // recurse in case its a ci literal 
-                    ], hs=1);
-
-        // lexicals are never split in pieces, unless it's comments but those are handled above.
-        case  :
-            return L("");
+        // lexicals are never split in pieces
+        case  : {
+            str yield = "";
+            return yield != "" ? L(yield) : NULL();
+        }
 
         // Now we will deal with a lot of cases for expressions and block-structured statements.
         // Those kinds of structures appear again and again as many languages share inspiration
-        // from their predecessors. Watching out not to loose any comments...
+        // from their pre-decessors.
 
-        case :
-            return HOV([toBox(elements[0], opts=opts), H([toBox(e, opts=opts) | e <- elements[1..]])]);
+        // binary operators become flat lists, but only if they are associative
+        case :
+            if ({\assoc(\left()), \assoc(\right()), \assoc(\assoc())} & attrs != {})
+                return U([toBox(elements[0]), L(op), toBox(elements[-1])]) ;
+            
 
         // postfix operators stick
-        case :
+        case :
             return H([toBox(e, opts=opts) | e <- elements], hs=0);
 
         // prefix operators stick
-        case :
+        case :
             return H([toBox(e, opts=opts) | e <- elements], hs=0);
 
         // brackets stick
-        case :
-            return H([toBox(e, opts=opts) | e <- elements], hs=0);
+        case :
+            return H(L("("), I(toExpBox(elements[2], wrapper=HOV(), opts=opts)), L(")"), hs=0);
+
+        case :
+            return toBox(single);
 
         // if the sort name is statement-like and the structure block-like, we go for 
         // vertical with indentation
@@ -247,6 +267,10 @@ default Box toBox(t:appl(Production p, list[Tree] args), FO opts = fo()) {
                 I([V([toBox(e, opts=opts) | Tree e <- elements[size(pre)+1..-1]])]),
                 toBox(elements[-1], opts=opts)
             ]);
+
+        // this is to simplify the tree structure for efficiency and readability
+        case :
+            return toBox(singleton);
     }
 
     return HV([toBox(a, opts=opts) | a <- args]);
@@ -256,34 +280,112 @@ default Box toBox(t:appl(Production p, list[Tree] args), FO opts = fo()) {
 default Box toBox(amb({Tree t, *Tree _}), FO opts=fo()) = toBox(t);
 
 @synopsis{When we end up here we simply render the unicode codepoint back.}
-default Box toBox(c:char(_), FormatOptions opts=fo() ) = L("");
+default Box toBox(c:char(_), FormattingOptions opts=fo() ) = L("");
 
 @synopsis{Cycles are invisible and zero length}
 default Box toBox(cycle(_, _), FO opts=fo()) = NULL();
 
-@synopsis{Private type alias for legibility's sake}
-private alias FO = FormatOptions;
+@synopsis{Create a V box of V boxes where the inner boxes are connected and the outer boxes are separated by an empty line.}
+@description{
+This function learns from the input trees how vertical clusters were layout in the original tree.
+The resulting box maintains the original clustering.
+For example, such lists of declarations which are separated by a newline, remain separated after formatting with `toClusterBox`
+```
+int a1 = 1;
+int a2 = 2;
+
+int b1 = 3;
+int b2 = 4;
+```
+}
+@benefits{
+* many programmers use vertical clustering, or "grouping statements", to indicate meaning or intent, by not throwing this
+away we are not throwing away the documentative value of their grouping efforts.
+}
+@pitfalls{
+* ((toClusterBox)) is one of the (very) few Box functions that use layout information from the input tree to 
+influence the layout of the output formatted code. It replaces a call to ((toBox)) for that reason.
+* ((toClusterBox)) does not work on separated lists, yet.
+}
+Box toClusterBox(list[Tree] lst, FO opts=fo()) {
+    list[Box] cluster([])  = [];
+
+    list[Box] cluster([Tree e]) = [V([toBox(e)], vs=0)];
 
-@synopsis{Removing production labels removes similar patterns in the main toBox function.}
-private Production delabel(prod(label(_, Symbol s), list[Symbol] syms, set[Attr] attrs))
-    = prod(s, delabel(syms), attrs);
+    list[Box] cluster([*Tree pre, Tree last, Tree first, *Tree post])
+        = [V([*[toBox(p, opts=opts) | p <- pre], toBox(last, opts=opts)], vs=0), *cluster([first, *post])]
+        when first@\loc.begin.line - last@\loc.end.line > 1
+        ;
 
-private default Production delabel(Production p) = p;
+    default list[Box] cluster(list[Tree] l) = [V([toBox(e, opts=opts) | e <- l], vs=0)];
 
-private list[Symbol] delabel(list[Symbol] syms) = [delabel(s) | s <- syms];
+    return V(cluster(lst), vs=1);
+}
+
+Box toClusterBox(&T* lst, FO opts=fo()) = toClusterBox([e | e <- lst], opts=opts);
+Box toClusterBox(&T+ lst, FO opts=fo()) = toClusterBox([e | e <- lst], opts=opts);
 
-private Symbol delabel(label(_, Symbol s)) = s;
-private default Symbol delabel(Symbol s) = s;
+@synopsis{Reusable way of dealing with large binary expression trees}
+@description{
+1. the default `toBox` will flatten nested binary expressions to U lists.
+2. the G box groups each operator with the following expression on the right hand-side,
+   * given an initial element (usually L("=") or L(":=")) for the assignment operators
+3. the entire list is indented in case the surrounding context needs more space
+4. the net result is usually in vertical mode:
+```
+    = operand1
+    + operand2
+    + operand3
+```
+or in horizontal mode:
+```
+= operand1 + operand2 + operand3
+```
+
+By default ((toExpBox)) wraps it result in a HOV context, but you can pass
+in a different `wrapper` if you like.
+}
+Box toExpBox(Box prefix, Tree expression, Box wrapper=HOV(), FO opts=fo())
+    = wrapper[boxes=[G(prefix, toBox(expression, opts=opts), gs=2, op=H())]];
+
+@synopsis{Reusable way of dealing with large binary expression trees}
+@description{
+1. the default `toBox` will flatten nested binary expressions to U lists.
+2. the G box groups each operator horizontally with the following expression on the right hand-side.
+4. the net result is usually in vertical mode:
+```
+    operand1 + operand2
+    + operand3
+```
+or in horizontal mode:
+```
+operand1 + operand2 + operand3
+```
+
+By default ((toExpBox)) wraps it result in a HV context, but you can pass
+in a different `wrapper` if you like.
+
+}
+Box toExpBox(Tree expression, Box wrapper=HV(), FO opts=fo())
+    = wrapper[boxes=[G(toBox(expression, opts=opts), gs=2, backwards=true, op=H())]];
+
+@synopsis{Private type alias for legibility's sake}
+private alias FO = FormattingOptions;
 
 @synopsis{This is a short-hand for legibility's sake}
-private FO fo() = formatOptions();
+private FO fo() = formattingOptions();
 
 @synopsis{Implements normalization of case-insensitive literals}
-private str ci(str word, toLower()) = toLowerCase(word);
-private str ci(str word, toUpper()) = toUpperCase(word);
-private str ci(str word, toCapitalized()) = capitalize(word);
-private str ci(str word, asIs())    = word;
-
-@synopsis{Split a text by the supported whitespace characters}
-private list[str] words(str text)
-    = [ x | // := text];
\ No newline at end of file
+private str ci(toLower(), str word) = toLowerCase(word);
+private str ci(toUpper(), str word) = toUpperCase(word);
+private str ci(toCapitalized(), str word) = capitalize(word);
+private str ci(asIs(), str word) = word;
+
+@synopsis{Removing production labels helps with case distinctions on ((Symbol)) kinds.}
+private Production delabel(prod(Symbol s, list[Symbol] syms, set[Attr] attrs)) = prod(delabel(s), [delabel(x) | x <- syms], attrs);
+private Production delabel(regular(Symbol s)) = regular(delabel(s));
+
+@synopsis{Removing symbol labels helps with case distinctions on ((Symbol)) kinds.}
+private Symbol delabel(label(_, Symbol s)) = delabel(s);
+private Symbol delabel(conditional(Symbol s, _)) = delabel(s);
+private default Symbol delabel(Symbol s) = s;
\ No newline at end of file
diff --git a/src/org/rascalmpl/library/lang/c90/syntax/C.rsc b/src/org/rascalmpl/library/lang/c90/syntax/C.rsc
index 556b7fcb9fa..b054d59a69b 100644
--- a/src/org/rascalmpl/library/lang/c90/syntax/C.rsc
+++ b/src/org/rascalmpl/library/lang/c90/syntax/C.rsc
@@ -87,8 +87,7 @@ syntax Expression
 	        | Expression "\>\>=" Expression 
 	        | Expression "&=" Expression 
 	        | Expression "^=" Expression 
-	        | Expression "
-	        | =" Expression
+	        | Expression "|=" Expression
 			)
 	> left commaExpression: Expression "," Expression
 	;
diff --git a/src/org/rascalmpl/library/lang/pico/format/Formatting.rsc b/src/org/rascalmpl/library/lang/pico/format/Formatting.rsc
index 20941f8a5f8..02649d8a481 100644
--- a/src/org/rascalmpl/library/lang/pico/format/Formatting.rsc
+++ b/src/org/rascalmpl/library/lang/pico/format/Formatting.rsc
@@ -47,18 +47,18 @@ list[TextEdit] formatPicoTree(start[Program] file) {
 
 @synopsis{Format while}
 Box toBox((Statement) `while  do <{Statement ";"}* block> od`, FO opts = fo())
-    = V([
-        H([L("while"), toBox(e, opts=opts), L("do")]),
-        I([toBox(block, opts=opts)]),
+    = V(
+        H(L("while"), HV(toBox(e, opts=opts)), L("do")),
+        I(toClusterBox(block, opts=opts)),
         L("od")
-    ]); 
+    ); 
 
 @synopsis{Format if-then-else }
 Box toBox((Statement) `if  then <{Statement ";"}* thenPart> else <{Statement ";"}* elsePart> fi`, FO opts = fo())
-    = V([
-        H([L("if"), toBox(e, opts=opts), L("then")]),
-            I([toBox(thenPart, opts=opts)]),
+    = V(
+        H(L("if"), HV(toBox(e, opts=opts)), L("then")),
+            I(toClusterBox(thenPart, opts=opts)),
         L("else"),
-            I([toBox(elsePart, opts=opts)]),
+            I(toClusterBox(elsePart, opts=opts)),
         L("fi")
-    ]); 
\ No newline at end of file
+    ); 
\ No newline at end of file
diff --git a/src/org/rascalmpl/library/lang/rascal/format/Grammar.rsc b/src/org/rascalmpl/library/lang/rascal/format/Grammar.rsc
index d29f1ad0e30..73e27142e60 100644
--- a/src/org/rascalmpl/library/lang/rascal/format/Grammar.rsc
+++ b/src/org/rascalmpl/library/lang/rascal/format/Grammar.rsc
@@ -10,6 +10,10 @@
 @contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl}
 @synopsis{Convert the Rascal internal grammar representation format (Grammar) to 
   a syntax definition in Rascal source code.}
+@pitfalls{
+This function does not use advanced formatting feature because it is a part of
+components early in Rascal's bootstrapping and standard library construction cycle.
+}
 module lang::rascal::format::Grammar
 
 import ParseTree;
diff --git a/src/org/rascalmpl/library/lang/rascal/format/Rascal.rsc b/src/org/rascalmpl/library/lang/rascal/format/Rascal.rsc
new file mode 100644
index 00000000000..1a984de90d8
--- /dev/null
+++ b/src/org/rascalmpl/library/lang/rascal/format/Rascal.rsc
@@ -0,0 +1,1172 @@
+@license{
+Copyright (c) 2022, NWO-I Centrum Wiskunde & Informatica (CWI) 
+All rights reserved. 
+  
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 
+  
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
+  
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 
+  
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+}
+@synopsis{Composes a default formatter for Rascal modules}
+@description{
+This module composes and describes a "standard" formatting style for Rascal.
+There could be other styles of course. Other styles can be build by 
+writing different `toBox` rules.
+}
+@bootstrapParser
+module lang::rascal::format::Rascal
+ 
+// by extending these modules we compose a `toBox` function
+// which handles all relevant constructs of Rascal
+extend lang::box::util::Tree2Box;
+extend lang::rascal::\syntax::Rascal;
+
+import IO;
+import ParseTree;
+import String;
+import analysis::diff::edits::ExecuteTextEdits;
+import analysis::diff::edits::HiFiLayoutDiff;
+import analysis::diff::edits::TextEdits;
+import lang::box::\syntax::Box;
+import lang::box::util::Box2Text;
+import util::Formatters;
+import util::Reflective;
+
+
+@synopsis{Format any Rascal module and dump the result as a string}
+void debugFormatRascalFile(loc \module, bool console=false, bool HTML=!console, FormattingOptions opts = formattingOptions(), bool dumpEdits=false) {
+    debugFileFormat(#start[Module], toBox, \module, console=console, HTML=HTML, opts=opts, dumpEdits=dumpEdits);
+}
+
+void testOnLibrary() {
+    debugFilesFormat(
+        #start[Module], 
+        toBox, 
+        |project://rascal/src/org/rascalmpl/library/|, 
+        "rsc", 
+        ansi=false, 
+        shadowFiles=true, 
+        appendFile=false, 
+        console=false);
+}
+
+void testOnCompiler() {
+    debugFilesFormat(
+        #start[Module], 
+        toBox, 
+        |project://rascal/src/org/rascalmpl/compiler/|, 
+        "rsc", 
+        ansi=true, 
+        shadowFiles=false, 
+        appendFile=true, 
+        console=false);
+}
+
+/* Modules */
+
+Box toBox(Toplevel* toplevels) = toClusterBox(toplevels);
+
+Box toBox((Module) ` module   `)
+    = V(V(toBox(tags),
+        H(L("module"), toBox(name))),
+        toClusterBox(imports),
+        toBox(body), vs=1);
+
+Box toBox(Import* imports) = toClusterBox(imports);
+
+Box toBox((Import) `import ;`)
+    = H(L("import"), H0(toBox(m), L(";")));
+
+Box toBox((Import) `extend ;`)
+    = H(L("extend"), H0(toBox(m), L(";")));
+    
+Box toBox((Visibility) ``) = NULL();
+
+/* Syntax definitions */
+
+Box toBox((SyntaxDefinition) ` syntax  = ;`)
+    = (production is \all || production is \first)
+        ? V(H(toBox(st), L("syntax"), toBox(defined)),
+            I(G(L("="), toBox(production), gs=2, op=H([])),
+                L(";")))
+        :  // single rule case
+          H(toBox(st), L("syntax"), toBox(defined), L("="), H0(toBox(production), L(";")))
+        ;
+
+Box toBox((SyntaxDefinition) `lexical  = ;`)
+    = (production is \all || production is \first)
+        ? V(H(L("lexical"), toBox(defined)),
+            I(G(L("="), toBox(production), gs=2, op=H([])),
+                L(";")))
+        :  // single rule case
+          H(L("lexical"), toBox(defined), L("="), H0(toBox(production), L(";")))
+        ;
+
+Box toBox((SyntaxDefinition) `keyword  = ;`)
+    = (production is \all || production is \first)
+        ? V(H(L("keyword"), toBox(defined)),
+            I(G(L("="), toBox(production), gs=2, op=H([])),
+                L(";")))
+        :  // single rule case
+          H(L("keyword"), toBox(defined), L("="), H0(toBox(production), L(";")))
+        ;
+
+Box toBox((SyntaxDefinition) ` layout  = ;`)
+    = (production is \all || production is \first)
+        ? V(H(toBox(v), L("layout"), toBox(defined)),
+            I(G(L("="), toBox(production), gs=2, op=H([])),
+                L(";")))
+        :  // single rule case
+          H(toBox(v), L("layout"), toBox(defined), L("="), H0(toBox(production), L(";")))
+        ;
+
+
+Box toBox((Prod) ` | `) 
+    = U(toBox(lhs), L("|"), toBox(rhs));
+
+Box toBox((Prod) ` \> `) 
+    = U(toBox(lhs), L("\>"), toBox(rhs));
+
+Box toBox((Prod) `:`) 
+    = H0(L(":"), toBox(n));
+
+Box toBox((Prod) `  : `)
+    = H([toBox(modifiers), H0(toBox(name), L(":")), *[toBox(s) | s <- syms]]);
+
+Box toBox((Prod) ` `)
+    = H([toBox(modifiers), *[toBox(s) | s <- syms]]);
+
+Box toBox((Prod) ` ()`)
+    = H(toBox(a), HOV(G(L("("), U(toBox(g)), L(")"), gs=2, op=H([]))));
+
+/* symbols */
+Box toBox((Sym) `{ }*`) = H0(L("{"), H1(toBox(e), toBox(sep)), L("}"), L("*"));
+Box toBox((Sym) `{ }+`) = H0(L("{"), H1(toBox(e), toBox(sep)), L("}"), L("+"));
+Box toBox((Sym) `*`) = H0(toBox(e), L("*"));
+Box toBox((Sym) `+`) = H0(toBox(e), L("+"));
+Box toBox((Sym) `?`) = H0(toBox(e), L("?"));
+Box toBox((Sym) `()`) = H0(L("("), L(")"));
+
+Box toBox((Sym) `( )`) 
+    = H0(L("("), H1([toBox(first), *[toBox(e) | Sym e <- sequence]]),L(")"));
+
+Box toBox((Sym) `start[]`) = H0(L("start"), L("["), toBox(s), L("]"));
+
+Box toBox((Sym) `( | <{Sym "|"}+ alternatives>)`) 
+    = H0(L("("), H1([toBox(first), *[L("|"), toBox(e) | Sym e <- alternatives]]),L(")"));
+
+Box toBox((Class) `[]`)
+    = H0([L("["), *[toBox(r) | r <- ranges], L("]")]);
+
+Box toBox((Range) ` - `)
+    = H0(toBox(s), L("-"), toBox(e));
+
+/* Declarations */
+
+Box toBox((QualifiedName) `<{Name "::"}+ names>`)
+    = L("");
+
+Box toBox((Tag) `@ `)
+    = H0(L("@"), toBox(n), toBox(contents));
+
+Box toBox((Tag) `@ = `)
+    = H0(L("@"), toBox(n), L("="), toBox(exp));
+
+Box toBox((Tag) `@`)
+    = H0(L("@"), toBox(n));
+
+Box toBox(QualifiedName n) = L("");
+
+Box toBox((Declaration) `  anno  @;`)
+    = V(
+        toBox(t),
+        H(toBox(v), L("anno"), toBox(annoType), H0(toBox(onType), L("@"),  toBox(n)))
+    );
+
+Box toBox((Declaration) `  alias   = ;`)
+    = V(toBox(t),
+        H(toBox(v), L("alias"), toBox(user), L("="), H0(toBox(base), L(";"))));
+
+Box toBox((Declaration) `  data  ;`)
+    = V(toBox(tg),
+        H(toBox(v), L("data"), H0(toBox(typ), toBox(ps), L(";"))));
+
+Box toBox((Declaration) `  data   = ;`)
+    = HV(V(toBox(tg),
+        H(toBox(v), L("data"), H0(toBox(typ)), toBox(ps))),
+        I(H(L("="), H0(toBox(va), L(";")))));
+
+Box toBox((Declaration) `  data   =  | <{Variant "|"}+ vs>;`)
+    = V(toBox(tg),
+        H(toBox(v), L("data"), H0(toBox(typ)), toBox(ps)),
+        I([G([
+                L("="),
+                toBox(va),
+                *[L("|"), toBox(vax) | Variant vax <- vs] // hoist the bars `|` up to the same level of `=`
+            ]), L(";")]));
+
+Box toBox((Declaration) `    = ;`)
+    = HV(
+        V(
+            toBox(tags), 
+            H1(toBox(visibility), toBox(typ), toBox(name))
+        ), 
+        I(H0(HOV(G(L("="), U([toBox(initial)]))), L(";"))));
+
+Box toBox((Declaration) `   , <{Variable ","}+ variables>;`)
+    = HV(V(toBox(tags), H1(toBox(visibility), toBox(typ))), I(HOV(H0(toBox(first), L(",")), SL([toBox(v) | v <- variables], L(",")))), L(";"));
+
+Box toBox((Declarator) ` `) 
+    = H1(toBox(typ), toBox(name));
+
+Box toBox((Declarator) `  = `) 
+    = HV(H(toBox(typ), toBox(name)), I(toExpBox(L("="), initial)));
+
+Box toBox((Declarator) ` , <{Variable ","}+ variables>`) 
+    = HV(I(HOV(H(toBox(typ), toBox(first)), L(","), SL([toBox(v) | v <- variables], L(",")))));
+
+Box toBox((CommonKeywordParameters) `(<{KeywordFormal ","}+ fs>)`)
+    = H0(L("("), HOV(toBox(fs)), L(")"));
+
+Box toBox((Variant) `(<{TypeArg ","}* args>, <{KeywordFormal ","}+ kws>)`)
+    = HV(
+        H0(toBox(n), L("(")),
+        HOV(
+            I(H0(toBox(args), L(","))),
+            I(toBox(kws)), hs=1),
+        L(")"), hs=0);
+
+Box toBox((Variant) `(<{TypeArg ","}* args>)`)
+    = HV(H0(toBox(n), L("(")),
+        I(toBox(args)),
+        L(")"), hs=0);
+
+Box toBox((Variant) `(<{TypeArg ","}* args>
+                    '<{KeywordFormal ","}+ kws>)`)
+    = HV(
+        H0(toBox(n), L("(")),
+        HOV(
+            I(H0(toBox(args))),
+            I(toBox(kws)), hs=1
+        ), 
+        L(")"), hs=0);
+
+Box toBox(FunctionModifier* modifiers) = H([toBox(b) | b <- modifiers]);
+
+Box toBox((Signature) `     throws <{Type ","}+ exs>`)
+    = H0(
+        HOV([
+            H(toBox(modifiers), toBox(typ), H0(toBox(name), L("("))),
+            G(toBox(parameters), gs=1, op=I())], hs=0),
+        H([L(")"), L("throws"), SL([toBox(e) | e <- exs], L(","))], hs=1));
+
+Box toBox((Signature) `    `)
+    = H0(HOV(
+        H(toBox(modifiers), toBox(typ), H0(toBox(name), L("("))),
+        G(toBox(parameters), gs=1, op=I())
+        hs=0), L(")"));
+
+Box toBox((FunctionDeclaration) `   ;`)
+    = V(
+        toBox(tags),
+        HOV(
+            toBox(vis), 
+            H0(toBox(sig), L(";"))
+        )
+    );
+
+Box toBox((FunctionDeclaration) `   = ;`)
+    = V(
+        toBox(tags),
+        H0(HOV(
+            toBox(vis), 
+            toBox(sig),
+            I(toExpBox(L("="), exp))),
+        L(";"))
+    ) 
+    when !(exp is \visit || exp is voidClosure || exp is closure);
+
+Box toBox((Expression) `  {  }`)
+    =   HOV(
+            toBox(typ), H0(L("("), HOV(G(toBox(parameters), gs=1, op=I())), H(L(")")), L("{")), 
+            I(V(toClusterBox(statements))),
+            L("}")
+        );
+
+Box toBox((Expression) ` {  }`)
+    =   HOV(
+            H0(L("("), HOV(G(toBox(parameters), gs=1, op=I())), H(L(")"), L("{"))), 
+            I(V(toClusterBox(statements))),
+            L("}")
+        );
+    
+Box toBox((FunctionDeclaration) `   =   {  };`)
+    = V(toBox(tags),
+        HOV(
+            toBox(vis), 
+            toBox(sig),
+            I(HOV(
+                H(L("="), H0(toBox(typ), L("("))),
+                G(toBox(parameters), gs=1, op=I()), 
+                H(L(")"), L("{"))
+            ))), 
+        I(V(toClusterBox(statements))),
+        H0(L("}"), L(";")));
+
+Box toBox((FunctionDeclaration) `   =  {  };`)
+    = V(toBox(tags),
+        HOV(
+            toBox(vis),
+            toBox(sig),
+            I(HOV(
+                H(L("="), L("(")),
+                G(toBox(parameters), gs=1, op=I()), 
+                H(L(")"), L("{"))
+            ))), 
+        I(V(toClusterBox(statements))),
+        H0(L("}"), L(";")));
+
+
+Box toBox((FunctionDeclaration) `   = <}>", htmlEscapes);
 
-  str rec(t:appl(prod(cilit(str l), _, _), _)) 
+  str rec(Tree t:appl(prod(lit(str l), _, _), _)) 
     = span("Keyword", l) when isKeyword(l);
 
-  str rec(t:appl(prod(_, _, {*_, \tag("category"(str cat))}), list[Tree] as))
-    = span(cat, "<}>");
+  str rec(Tree t:appl(prod(cilit(str l), _, _), list[Tree] as)) 
+    = span("Keyword", yield(as)) when isKeyword(l);
 
-  default str rec(appl(_, list[Tree] as))
-    = "<}>";
+  str rec(Tree t:appl(prod(_, _, {*_, \tag("category"(str cat))}), list[Tree] as))
+    = span(cat, yield(as));
 
-  str rec(amb({k, *_})) = rec(k);
+  str rec(amb({Tree k, *_})) = rec(k);
+  
+  default str rec(appl(Production p, list[Tree] as))
+    = "<}>";
 
   default str rec(Tree t:char(_)) = escape("", htmlEscapes);
 
   str span(str class, str src) = "\\"\>\";
 
-  return "\
\\\";
+  if (withStyle) {
+    return "\
+           '
+           '\
+           '\
\
+           '
+           '\\";
+  }
+  else {
+    return "\
\
+           '
+           '\\";
+  }
 }
 
 @synopsis{Yields the characters of a parse tree as the original input sentence but using macros to wrap to-be-highlighted areas.}
@@ -80,44 +100,6 @@ public str toLaTeX(Tree t) {
   return rec(t);
 } 
 
-@synopsis{Yields the characters of a parse tree as the original input sentence in a ... block, but with spans for highlighted segments in HTML}
-public str toHTML(Tree t) {
-  htmlEscapes = (
-	  "\<": "<",
-	  "\>": ">",
-	  "&" : "&"
-  );
-
-  str rec(t:appl(prod(lit(str l), _, _), _)) 
-    = wrapLink(span("Keyword", l), t)
-    when isKeyword(l);
-
-  str rec(t:appl(prod(cilit(str l), _, _), _)) 
-    = wrapLink(span("Keyword", l), t)
-    when isKeyword(l);
-
-  str rec(t:appl(prod(_, _, {*_, \tag("category"(str cat))}), list[Tree] as))
-    = wrapLink(span(cat, ( "" | it + rec(a) | a <- as )), t);
-
-  str rec(appl(prod(_, _, set[Attr] attrs), list[Tree] as))
-    = ( "" | it + rec(a) | a <- as )
-    when {*_, \tag("category"(str _))} !:= attrs;
-
-  str rec(appl(regular(_), list[Tree] as))
-    = ( "" | it + rec(a) | a <- as );
-
-  str rec(amb({k, *_})) = rec(k);
-
-  default str rec(Tree t) 
-    = wrapLink(escape(unparse(t), htmlEscapes), t);
-
-  str span(str class, str src) = "\\"\>\";
-
-  default str wrapLink(str text, Tree _) = text;
-
-  return "\
\\\";
-}
-
 @synopsis{Unparse a parse tree to unicode characters, wrapping certain substrings with ANSI codes for highlighting.}
 public str toANSI(Tree t, bool underlineAmbiguity=false, int tabSize=4) {
   str rec(Tree x:appl(prod(lit(str l), _, _), _))   = isKeyword(l) ? bold("") :  "";
@@ -143,8 +125,8 @@ public str toANSI(Tree t, bool underlineAmbiguity=false, int tabSize=4) {
   str underline(str s)  = "";
   str comment(str s)    = "";
 
-  str \map("Comment", text)         = comment(text);
-  str \map("Keyword", text)         = bold(text);
+  str \map(/[Cc]omment/, text)         = comment(text);
+  str \map(/[Kk]eyword/, text)         = bold(text);
   default str \map(str _, str text) = text;
 
   return rec(t);
diff --git a/src/org/rascalmpl/library/vis/Text.rsc b/src/org/rascalmpl/library/vis/Text.rsc
index 5299034149c..0960579402d 100644
--- a/src/org/rascalmpl/library/vis/Text.rsc
+++ b/src/org/rascalmpl/library/vis/Text.rsc
@@ -94,6 +94,11 @@ private str ppvalue_(value e, str(value) nodeLabel, lrel[str,value](value) edges
   str indented(str last, str other, bool doSpace) 
     = " <} else {><}> <}>";
     
-  return " <- kids) {>─→<}>
-         '";
+  return " <- kids) {>─→<}>
+  '";
 }
diff --git a/src/org/rascalmpl/runtime/$RascalModule.java b/src/org/rascalmpl/runtime/$RascalModule.java
index 83a27012654..52ff34e4342 100644
--- a/src/org/rascalmpl/runtime/$RascalModule.java
+++ b/src/org/rascalmpl/runtime/$RascalModule.java
@@ -302,41 +302,6 @@ public Set findResources(String fileName) {
         }
     }
     
-    private static void $usage(String module, String error, Type kwargs) {
-        PrintWriter $ERR = new PrintWriter(System.err);
-        
-        if (!error.isEmpty() && !error.equals("help")) {
-            $ERR.println(error);
-        }
-        
-        $ERR.println("Usage: ");
-        $ERR.println("java -cp ... " + module + " ");
-
-        if (kwargs.getArity() > 0) {
-            $ERR.println(" [options]\n\nOptions:\n");
-
-            for (String param : kwargs.getFieldNames()) {
-                $ERR.print("\t-");
-                $ERR.print(param);
-                if (kwargs.getFieldType(param).isSubtypeOf(TypeFactory.getInstance().boolType())) {
-                    $ERR.println("\t[arg]: one of nothing (true), \'1\', \'0\', \'true\' or \'false\';");
-                }
-                else {
-                    $ERR.println("\t[arg]: " + kwargs.getFieldType(param) + " argument;");
-                }
-            }
-        }
-        else {
-            $ERR.println('\n');
-        }
-        
-        $ERR.flush();
-        
-        if (!error.equals("help")) {
-            throw new IllegalArgumentException();
-        }
-    }
-
     protected static Map $parseCommandlineParameters(String module, String[] commandline, Type kwTypes) {
 		// reusing the same commandline parameter parser that the interpreter uses, based on the keyword parameter types
 		// of the function type of the `main` function.