Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for parsing input that forms a valid prefix #2053

Merged
merged 10 commits into from
Oct 15, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ module lang::rascal::tests::concrete::recovery::BasicRecoveryTests

import ParseTree;
import util::ErrorRecovery;
import util::Maybe;

import lang::rascal::tests::concrete::recovery::RecoveryTestSupport;

layout Layout = [\ ]* !>> [\ ];

Expand All @@ -26,32 +27,17 @@ syntax T = ABC End;
syntax ABC = 'a' 'b' 'c';
syntax End = "$";

private Tree parseS(str input, bool visualize=false)
= parser(#S, allowRecovery=true, allowAmbiguity=true)(input, |unknown:///?visualize=<"<visualize>">|);

test bool basicOk() {
return !hasErrors(parseS("a b c $"));
}
test bool basicOk() = checkRecovery(#S, "a b c $", []);

test bool abx() {
Tree t = parseS("a b x $");
return getErrorText(findBestError(t).val) == "x ";
}
test bool abx() = checkRecovery(#S, "a b x $", ["x "]);

test bool axc() {
Tree t = parseS("a x c $");
return getErrorText(findBestError(t).val) == "x c";
}
test bool axc() = checkRecovery(#S, "a x c $", ["x c"]);

test bool ax() {
test bool autoDisambiguation() {
str input = "a x $";

Tree t = parseS(input);
assert size(findAllErrors(t)) == 3;
assert getErrorText(findBestError(t).val) == "x ";
assert checkRecovery(#S, input, ["x "]);

Tree autoDisambiguated = parser(#S, allowRecovery=true, allowAmbiguity=false)(input, |unknown:///|);
assert size(findAllErrors(autoDisambiguated)) == 1;

return getErrorText(findFirstError(autoDisambiguated)) == getErrorText(findBestError(t).val);
return size(findAllErrors(autoDisambiguated)) == 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ module lang::rascal::tests::concrete::recovery::ListRecoveryTests
import ParseTree;
import util::ErrorRecovery;

import lang::rascal::tests::concrete::recovery::RecoveryTestSupport;

layout Layout = [\ ]* !>> [\ ];

syntax S = T End;
Expand All @@ -29,16 +31,8 @@ Tree parseList(str s, bool visualize=false) {
return parser(#S, allowRecovery=true, allowAmbiguity=true)(s, |unknown:///?visualize=<"<visualize>">|);
}

test bool listOk() {
return !hasErrors(parseList("a b , a b , a b $", visualize=true));
}
test bool listOk() = checkRecovery(#S, "a b , a b , a b $", []);

test bool listTypo() {
Tree t = parseList("a b, a x, ab $", visualize=true);
return hasErrors(t);
}
test bool listTypo() = checkRecovery(#S, "a b, a x, ab $", ["x"]);

test bool listTypoWs() {
Tree t = parseList("a b , a x , a b $", visualize=true);
return hasErrors(t);
}
test bool listTypoWs() = checkRecovery(#S, "a b , a x , a b $", ["x "]);
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@

module lang::rascal::tests::concrete::recovery::NestedRecoveryTests

import ParseTree;
import util::ErrorRecovery;
import util::Maybe;
import lang::rascal::tests::concrete::recovery::RecoveryTestSupport;

layout Layout = [\ ]* !>> [\ ];

Expand All @@ -28,14 +26,6 @@ syntax A = "a";
syntax B = "b" "b";
syntax C = "c";

private Tree parseS(str input, bool visualize=false)
= parser(#S, allowRecovery=true, allowAmbiguity=true)(input, |unknown:///?visualize=<"<visualize>">|);
test bool nestedOk() = checkRecovery(#S, "a b b c", []);

test bool nestedOk() {
return !hasErrors(parseS("a b b c"));
}

test bool nestedTypo() {
Tree t = parseS("a b x c");
return getErrorText(findBestError(t).val) == "x ";
}
test bool nestedTypo() = checkRecovery(#S, "a b x c", ["x "]);
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,12 @@ import lang::pico::\syntax::Main;

import ParseTree;
import util::ErrorRecovery;

import IO;
import String;
import util::Maybe;
import lang::rascal::tests::concrete::recovery::RecoveryTestSupport;

Tree parsePico(str input, bool visualize=false)
= parser(#Program, allowRecovery=true, allowAmbiguity=true)(input, |unknown:///?visualize=<"<visualize>">|);

bool checkError(Tree t, str expectedError) {
str bestError = getErrorText(findBestError(t).val);
//println("best error: <bestError>, expected: <expectedError>");
return size(bestError) == size(expectedError);
}

test bool picoOk() {
t = parsePico("begin declare input : natural,
test bool picoOk() = checkRecovery(#Program, "begin declare input : natural,
output : natural,
repnr : natural,
rep : natural;
Expand All @@ -48,12 +38,9 @@ test bool picoOk() {
od;
input := input - 1
od
end");
return !hasErrors(t);
}
end", []);

test bool picoTypo() {
t = parsePico("begin declare input : natural,
test bool picoTypo() = checkRecovery(#Program, "begin declare input : natural,
output : natural,
repnr : natural,
rep : natural;
Expand All @@ -68,13 +55,9 @@ test bool picoTypo() {
od;
input := input - 1
od
end");

return checkError(t, "output x rep");
}
end", ["output x rep"]);

test bool picoMissingSemi() {
t = parsePico("begin declare input : natural,
test bool picoMissingSemi() = checkRecovery(#Program, "begin declare input : natural,
output : natural,
repnr : natural,
rep : natural;
Expand All @@ -89,32 +72,24 @@ test bool picoMissingSemi() {
od
input := input - 1
od
end");
return checkError(t, "input := input - 1
od");
}
end", ["input := input - 1
od"]);

test bool picoTypoSmall() {
t = parsePico(
"begin declare;
test bool picoTypoSmall() = checkRecovery(#Program, "begin declare;
while input do
input x= 14;
output := 0
od
end");
end", ["x= 14"]);

return checkError(t, "x= 14");
}

test bool picoMissingSemiSmall() {
t = parsePico(
"begin declare;
test bool picoMissingSemiSmall() = checkRecovery(#Program, "begin declare;
while input do
input := 14
output := 0
od
end");
end", ["output := 0
od"]);

test bool picoEof() = checkRecovery(#Program, "begin declare; input := 0;", ["input := 0;"]);

return checkError(t, "output := 0
od");
}
test bool picoEofError() = checkRecovery(#Program, "begin declare x y; input := 0;", ["x y;", "input := 0;"]);
Original file line number Diff line number Diff line change
Expand Up @@ -21,113 +21,40 @@ import util::ErrorRecovery;
import IO;
import util::Maybe;

bool debugging = false;

Tree parseRascal(type[&T] t, str input, bool visualize=false) {
Tree result = parser(t, allowRecovery=true, allowAmbiguity=true)(input, |unknown:///?visualize=<"<visualize>">|);
if (debugging) {
list[Tree] errors = findAllErrors(result);
if (errors != []) {
println("Tree has <size(errors)> errors");
for (error <- errors) {
println("- <getErrorText(error)>");
}

println("Best error: <getErrorText(findBestError(result).val)>");
}
}

return result;
}

Tree parseRascal(str input, bool visualize=false) = parseRascal(#start[Module], input, visualize=visualize);
import lang::rascal::tests::concrete::recovery::RecoveryTestSupport;

Tree parseFunctionDeclaration(str input, bool visualize=false) = parseRascal(#FunctionDeclaration, input, visualize=visualize);

Tree parseStatement(str input, bool visualize=false) = parseRascal(#Statement, input, visualize=visualize);
bool debugging = false;

test bool rascalOk() {
Tree t = parseRascal("
test bool rascalOk() = checkRecovery(#start[Module], "
module A

int inc(int i) {
return i+1;
}
");
return !hasErrors(t);
}

test bool rascalFunctionDeclarationOk() {
Tree t = parseFunctionDeclaration("void f(){}");
return !hasErrors(t);
}
", []);

test bool rascalFunctionDeclarationOk() = checkRecovery(#FunctionDeclaration, "void f(){}", []);

test bool rascalModuleFollowedBySemi() {
Tree t = parseRascal("
test bool rascalModuleFollowedBySemi() = checkRecovery(#start[Module], "
module A
;
");

// There are a lot of productions in Rascal that have a ; as terminator.
// The parser assumes the user has only entered the ; on one of them,
// so the error list contains them all.
list[Tree] errors = findAllErrors(t);
assert size(errors) == 10;

return getErrorText(findFirstError(t)) == ";";
}
", [";"]);

test bool rascalOperatorTypo() {
Tree t = parseRascal("
test bool rascalOperatorTypo() = checkRecovery(#start[Module], "
module A

int f() = 1 x 1;
");
", ["x 1;"]);

return getErrorText(findFirstError(t)) == "x 1;";
}

test bool rascalIllegalStatement() {
Tree t = parseRascal("module A void f(){a}");
return getErrorText(findFirstError(t)) == "a}";
}

test bool rascalMissingCloseParen() {
Tree t = parseRascal("module A void f({} void g(){}");

assert getErrorText(findFirstError(t)) == "void g(";
assert getErrorText(findBestError(t).val) == "(";

return true;
}
test bool rascalIllegalStatement() = checkRecovery(#start[Module], "module A void f(){a}", ["a}"]);

test bool rascalFunctionDeclarationMissingCloseParen() {
Tree t = parseFunctionDeclaration("void f({} void g() {}");
test bool rascalMissingCloseParen() = checkRecovery(#start[Module], "module A void f({} void g(){}", ["("]);

assert getErrorText(findFirstError(t)) == "void g(";
test bool rascalFunctionDeclarationMissingCloseParen() = checkRecovery(#FunctionDeclaration, "void f({} void g() {}", ["("]);

Tree error = findBestError(t).val;
assert getErrorText(error) == "(";
loc location = getSkipped(error).src;
assert location.begin.column == 16 && location.length == 1;
test bool rascalIfMissingExpr() = checkRecovery(#FunctionDeclaration, "void f(){if(){1;}}", [")"]);

return true;
}

test bool rascalIfMissingExpr() {
Tree t = parseFunctionDeclaration("void f(){if(){1;}}", visualize=false);
return getErrorText(findBestError(t).val) == ")";
}

test bool rascalIfBodyEmpty() {
Tree t = parseRascal("module A void f(){1;} void g(){if(1){}} void h(){1;}");

println("error: <getErrorText(findFirstError(t))>");
assert getErrorText(findBestError(t).val) == "} void h(){1";

return true;
}
test bool rascalIfBodyEmpty() = checkRecovery(#start[Module], "module A void f(){1;} void g(){if(1){}} void h(){1;}", ["} void h(){1"]);

// Not working yet:
/*
Expand Down
Loading
Loading