Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@ bool isValidRascalTplVersion(str version)

str getCurrentRascalTplVersion() = currentRascalTplVersion;

str currentRascalTplVersion = "2.0.0";
str currentRascalTplVersion = "3.0.0";

data TModel (
str rascalTplVersion = "2.0.0"
str rascalTplVersion = "3.0.0"
);

// Define alias for TypePalConfig
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,5 @@ int nextClosure(){
void resetClosureCounter(){
closureCounter = 0;
}

str normalizedMD5Hash(str s) = md5Hash(removeWhitespace(s));
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ void dataDeclaration(Tags tags, Declaration current, list[Variant] variants, Col
dt = isEmpty(typeParameters) ? defType(aadt(adtName, [], dataSyntax()))
: defType(typeParameters, AType(Solver s) { return aadt(adtName, [ s.getType(tp)[closed=true] | tp <- typeParameters], dataSyntax()); });

dt.md5 = md5Hash("<adtName><dataCounter>");
dt.md5 = normalizedMD5Hash("<adtName><dataCounter>");
dataCounter += 1;
if(!isEmpty(commonKeywordParameterList)) dt.commonKeywordFields = commonKeywordParameterList;
c.define(adtName, dataId(), current, dt);
Expand Down Expand Up @@ -141,7 +141,7 @@ void collect(current:(Variant) `<Name name> ( <{TypeArg ","}* arguments> <Keywor
declaredFieldNames += fieldName;
fieldType = ta.\type;
dt = defType([fieldType], makeFieldType(fieldName, fieldType));
dt.md5 = md5Hash("<currentModuleName><adtName><name><unparseNoLayout(current)>");
dt.md5 = normalizedMD5Hash("<currentModuleName><adtName><name><current>");
c.define(fieldName, fieldId(), ta.name, dt);
}
}
Expand All @@ -152,7 +152,7 @@ void collect(current:(Variant) `<Name name> ( <{TypeArg ","}* arguments> <Keywor
declaredFieldNames += fieldName;
kwfType = kwf.\type;
dt = defType([kwfType], makeKeywordFieldType(fieldName, kwf));
dt.md5 = md5Hash("<currentModuleName><adtName><dataCounter><name><consArity><kwfType><fieldName>");
dt.md5 = normalizedMD5Hash("<currentModuleName><adtName><dataCounter><name><consArity><kwfType><fieldName>");
c.define(fieldName, keywordFieldId(), kwf.name, dt);
}

Expand All @@ -166,7 +166,7 @@ void collect(current:(Variant) `<Name name> ( <{TypeArg ","}* arguments> <Keywor
kwFormalTypes = [kwField(s.getType(kwf.\type)[alabel=prettyPrintName(kwf.name)], prettyPrintName(kwf.name), currentModuleName, kwf.expression) | kwf <- kwFormals /*+ commonKwFormals*/];
formalTypes = [f is named ? s.getType(f)[alabel=prettyPrintName(f.name)] : s.getType(f) | f <- formals];
return acons(adtType, formalTypes, kwFormalTypes)[alabel=asUnqualifiedName(prettyPrintName(name))];
})[md5 = md5Hash(md5Contrib)]);
})[md5 = normalizedMD5Hash(md5Contrib)]);
c.fact(current, name);
beginUseTypeParameters(c, closed=false);
// The standard rules would declare arguments and kwFormals as variableId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ void collect(current: (Declaration) `<Tags tags> <Visibility visibility> <Type v
c.enterLubScope(var);
dt = defType([varType], makeGetSyntaxType(varType));
dt.vis = getVis(current.visibility, privateVis());
dt.md5 = md5Hash("<md5Contrib4Tags(tags)><visibility><varType><var.name>");
dt.md5 = normalizedMD5Hash("<md5Contrib4Tags(tags)><visibility><varType><var.name>");
if(!isEmpty(tagsMap)) dt.tags = tagsMap;
vname = prettyPrintName(var.name);
if(isWildCard(vname)){
Expand Down Expand Up @@ -217,7 +217,7 @@ void collect(current: (Declaration) `<Tags tags> <Visibility visibility> anno <T

dt = defType([annoType, onType], AType(Solver s) { return aanno(pname, s.getType(onType), s.getType(annoType)); });
dt.vis = getVis(current.visibility, publicVis());
dt.md5 = md5Hash("<md5Contrib4Tags(tags)><visibility><annoType><onType><name>");
dt.md5 = normalizedMD5Hash("<md5Contrib4Tags(tags)><visibility><annoType><onType><name>");
if(!isEmpty(tagsMap)) dt.tags = tagsMap;
// if(isWildCard(pname)){
// c.report(error(name, "Annotation names starting with `_` are deprecated; only allowed to suppress warning on unused variables"));
Expand All @@ -236,7 +236,7 @@ void collect(current: (KeywordFormal) `<Type kwType> <Name name> = <Expression e
} catch TypeUnavailable(): {
dt = defType([kwType], makeFieldType(kwformalName, kwType));
}
dt.md5 = md5Hash(unparseNoLayout(current));
dt.md5 = normalizedMD5Hash("<current>");
c.define(kwformalName, keywordFormalId(), current, dt);
c.calculate("keyword formal", current, [kwType, expression],
AType(Solver s){
Expand Down Expand Up @@ -419,7 +419,7 @@ void collect(current: (FunctionDeclaration) `<FunctionDeclaration decl>`, Collec

endUseBoundedTypeParameters(c);

dt.md5 = md5Hash(md5Contrib);
dt.md5 = normalizedMD5Hash(md5Contrib);
c.defineInScope(parentScope, prettyPrintName(fname), functionId(), current, dt);
c.leaveScope(decl);
c.pop(currentFunction);
Expand Down Expand Up @@ -719,7 +719,7 @@ void collect (current: (Declaration) `<Tags tags> <Visibility visibility> alias
// c.report(warning(name, "Alias names starting with `_` are deprecated; only allowed to suppress warning on unused variables"));
// }

c.define(aliasName, aliasId(), current, defType([base], AType(Solver s) { return s.getType(base); })[md5 = md5Hash("<md5Contrib4Tags(tags)><visibility><name><base>")]);
c.define(aliasName, aliasId(), current, defType([base], AType(Solver s) { return s.getType(base); })[md5 = normalizedMD5Hash("<md5Contrib4Tags(tags)><visibility><name><base>")]);
c.enterScope(current);
collect(tags, base, c);
c.leaveScope(current);
Expand Down Expand Up @@ -754,7 +754,7 @@ void collect (current: (Declaration) `<Tags tags> <Visibility visibility> alias
}

return aalias(aliasName, params, s.getType(base));
})[md5 = md5Hash("<md5Contrib4Tags(tags)><visibility><name><parameters><base>")]);
})[md5 = normalizedMD5Hash("<md5Contrib4Tags(tags)><visibility><name><parameters><base>")]);

collect(tags, c);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ void declareSyntax(SyntaxDefinition current, SyntaxRole syntaxRole, IdRole idRol

dt = defType(/*current is language && current.\start is present ? \start(nonterminalType) : */nonterminalType);
dt.vis = vis;
dt.md5 = md5Hash("<current is language ? "<current.\start>" : ""><adtName><syndefCounter><unparseNoLayout(defined)>");
dt.md5 = normalizedMD5Hash("<current is language ? "<current.\start>" : ""><adtName><syndefCounter><defined>");
syndefCounter += 1;

// Define the syntax symbol itself and all labelled alternatives as constructors
Expand Down Expand Up @@ -199,7 +199,7 @@ void collect(current: (Prod) `<ProdModifier* modifiers> <Name name> : <Sym* syms
//def = \start(sdef) := def ? sdef : unset(def, "alabel");
return acons(def, fields, [], alabel=unescape("<name>"));
} else throw "Unexpected type of production: <ptype>";
})[md5=md5Hash("<adt><unparseNoLayout(current)>")]);
})[md5=normalizedMD5Hash("<adt><current>")]);
beginUseTypeParameters(c,closed=true);
c.push(currentAlternative, <adt, "<name>", syms>);
collect(symbols, c);
Expand Down Expand Up @@ -271,7 +271,7 @@ void collect(current: (Prod) `<Prod lhs> | <Prod rhs>`, Collector c){
c.pop(inAlternative);
if(isEmpty(c.getStack(inAlternative))){
nalternatives += 1;
c.define("alternative-<nalternatives>", nonterminalId(), current, defType(current)[md5=md5Hash(unparseNoLayout(current))]);
c.define("alternative-<nalternatives>", nonterminalId(), current, defType(current)[md5=normalizedMD5Hash("<current>")]);
}
} else {
throw "collect alt: currentAdt not found";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -570,16 +570,11 @@ void collect(current:(Sym) `start [ <Nonterminal n> ]`, Collector c){
collect(n, c);
}

str unparseNoLayout(Tree t){
s = "<t>";
return "<for(int i <- [0..size(s)]){><s[i] in {" ", "\t", "\n"} ? "" : s[i]><}>";
}

void collect(current:(Sym) `<Sym symbol> <NonterminalLabel n>`, Collector c){
un = unescape("<n>");
md5Contrib = "";
if(!isEmpty(c.getStack(currentAlternative)) && <SyntaxDefinition adt, str cname, syms> := c.top(currentAlternative)){
md5Contrib += "<adt.defined><cname><unparseNoLayout(syms)>";
md5Contrib += "<adt.defined><cname><syms>";
} else {
throw "Cannot compute md5 for <current>";
}
Expand All @@ -589,7 +584,7 @@ void collect(current:(Sym) `<Sym symbol> <NonterminalLabel n>`, Collector c){
AType(Solver s){
res = s.getType(symbol)[alabel=un];
return res;
})[md5=md5Hash("<md5Contrib><unparseNoLayout(current)>")]);
})[md5=normalizedMD5Hash("<md5Contrib><current>")]);

c.fact(current, n);
collect(symbol, c);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ TModel check(str mname, RascalCompilerConfig cfg){

// --- Tests for source libraries --------------------------------------------

@ignore{Loads TModel with version 2.0.0 while it is 3.0.0 since a22dcd4416. TODO Make this test more robust.}
test bool importSimpleSourceModuleWithRascalAsLib(){
libName = "test-lib";
lib =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,12 @@ test bool consFieldLayoutChanged1()
test bool consFieldLayoutChanged2()
= expectEqual("data D = d(int n);", "data D = d (int n);");

test bool consDifferentNewlineCount()
= expectEqual("data A = a(list[A] children\n\n);", "data A = a(list[A] children\n\n\n);");

test bool consDifferentNewlineChars()
= expectEqual("data A = a(list[A] children\n);", "data A = a(list[A] children\r\n);");


// Keyword fields n and m generate separate locs, therefore we filter on constructors
test bool consKwFieldChanged()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
@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.
}

module lang::rascalcore::compile::Examples::CompareTPLs

import IO;
import List;
import Location;
import Set;
import ValueIO;
import util::FileSystem;
import util::Monitor;

import analysis::typepal::TModel;
import lang::rascalcore::check::LogicalLocations;

str JOB = "Comparing TPLs";

void main() {
compareTPLs();
}

@synopsis{Compare locations in two TPLs two verify only expected (OS-specific newline offset) differences.}
rel[loc, loc, loc] compareTPLs() = job(JOB, rel[loc, loc, loc](void(str, int) step) {
/*
Preconditions
1. Make sure the right Rascal release JAR is present in the Maven repository.
2. Compile the same Rascal release locally and copy the JARs to the local target folder.
*/
loc localTarget = |home:///swat/projects/Rascal/rascal/targetBackup/relocatedClasses|;
loc remoteTarget = |mvn://org.rascalmpl--rascal--0.41.3-RC8|;

rel[loc, loc, loc] differentLocations = {};
step("Finding local TPLs", 1);
allTPLs = sort(find(localTarget, "tpl"), byPathLength);
jobTodo(JOB, work=size(allTPLs));

for (tpl <- allTPLs) {
relTplPath = relativize(localTarget, tpl);
step("Comparing <relTplPath>", 1);
differentLocations += toSet(compareTPL(relTplPath, localTarget, remoteTarget));
}

step("Computing statistics", 1);

set[str] filesWithDiffs = {l.parent.path | l <- differentLocations<0>};
set[loc] defs = differentLocations<0>;

println("Number of tested TPLs: <size(allTPLs)>");
println("Found <size(defs)> different locations in <size(filesWithDiffs)> files.");;

print("Kinds of different locations: ");
iprintln({l.scheme | l <- differentLocations<0>});

return differentLocations;
}, totalWork=2);

bool byPathLength(loc a, loc b) = a.path < b.path;

lrel[loc, loc, loc] compareTPL(loc relTplPath, loc localTargetDir, loc unixTargetDir) {
loc localTplPath = resolve(localTargetDir, relTplPath);
loc unixTplPath = resolve(unixTargetDir, relTplPath);

if (!exists(localTplPath)) {
throw "Local TPL <localTplPath> does not exist";
}
if (!exists(unixTplPath)) {
throw "Unix TPL <unixTplPath> does not exist";
}

localTpl = readBinaryValueFile(#TModel, localTplPath);
unixTpl = readBinaryValueFile(#TModel, unixTplPath);

differentDefs = [<ll, ul, localTpl.logical2physical[ll]> | <ll, ul> <- difference(localTpl.defines.defined, unixTpl.defines.defined)];
if ([_, *_] := differentDefs) {
println("Differences in defs of <relTplPath> (\<Local logical, Unix logical, local physical\>): ");
iprintln(differentDefs);
println();
}

return differentDefs;
}

lrel[loc, loc] difference(set[loc] lLocs, set[loc] uLocs) =
[<l, u> | <l, u> <- pairs, !isEqualModuloNewlines(l, u)]
when lrel[loc, loc] pairs := zip2(sort(lLocs, lessThan), sort(uLocs, lessThan));

bool isEqualModuloNewlines(loc localLoc, loc unixLoc) = isRascalLogicalLoc(localLoc)
? isEqualLogicalModuloNewlines(localLoc, unixLoc)
: isEqualPhysicalModuloNewlines(localLoc, unixLoc);

bool isEqualLogicalModuloNewlines(loc localLoc, loc unixLoc) = localLoc == unixLoc;

bool isEqualPhysicalModuloNewlines(loc localLoc, loc unixLoc) {
if (localLoc.uri != unixLoc.uri) {
throw "URIs not equal: <localLoc> vs. <unixLoc>";
}

if (!localLoc.begin?) {
// Cannot say anything sensible about newlines without line information
return true;
}

if (localLoc.begin.line == localLoc.end.line) {
// Single line
return localLoc.length == unixLoc.length
&& localLoc.begin == unixLoc.begin
&& localLoc.end == unixLoc.end;
}

// Multi line
return localLoc.begin == unixLoc.begin
&& localLoc.end == unixLoc.end;
}

bool lessThan(loc a, loc b) = a.offset? && a.uri == b.uri
? a.offset < b.offset
: a.uri < b.uri;

bool lessThan(tuple[&A, &B] a, tuple[&A, &B] b) = a<0> != b<0>
? lessThan(a<0>, b<0>)
: lessThan(a<1>, b<1>);
26 changes: 26 additions & 0 deletions src/org/rascalmpl/library/Prelude.java
Original file line number Diff line number Diff line change
Expand Up @@ -3486,6 +3486,32 @@ private boolean match(IString subject, int i, IString pattern){
return true;
}

private boolean isUnicodeWhitespace(Integer cp) {
return Character.isSpaceChar(cp)
// Check for characters not included in 'space chars', but considered white space
|| cp == 0x0009 // \t
|| cp == 0x000A // \n
|| cp == 0x000B // VT
|| cp == 0x000C // FF
|| cp == 0x000D // \r
|| cp == 0x0085;// NEL
}

public IString removeWhitespace(IString str) {
StringBuilder b = new StringBuilder(str.length());
var iter = str.iterator();

while (iter.hasNext()) {
var codepoint = iter.next();
// Character.isWhitespace does not cover the complete range of Unicode whitespace
if (!isUnicodeWhitespace(codepoint)) {
b.appendCodePoint(codepoint);
}
}

return values.string(b.toString());
}

public IValue replaceAll(IString str, IString find, IString replacement){
int fLength = find.length();
if(fLength == 0){
Expand Down
15 changes: 15 additions & 0 deletions src/org/rascalmpl/library/String.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,21 @@ public str left(str s, int n, str 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`.
Expand Down
Loading
Loading