Skip to content
Merged
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
104 changes: 104 additions & 0 deletions scripts/AntlrBatchTestRig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import org.antlr.v4.runtime.*;
import java.io.*;
import java.nio.file.*;

public class AntlrBatchTestRig {
public static void main(String[] args) throws Exception {
if (args.length < 1) {
System.err.println("Usage: AntlrBatchTestRig <file1> [file2] ...");
System.exit(1);
}

int exitCode = 0;
for (String filePath: args) {
try {
if (!testFile(filePath))
exitCode = 1;
} catch (Exception e) {
System.err.println("ERROR:" + filePath + ":" + e.getMessage());
exitCode = 1;
}
}
System.exit(exitCode);
}

private static boolean testFile(String filePath) throws Exception {
// Detect mode based on file extension
String mode = filePath.endsWith(".yul") ? "yul" : "sol";
String content = new String(Files.readAllBytes(Paths.get(filePath)));

// Check if file expects parser error
boolean expectsError = content.contains("// ParserError");

CharStream input;
if (mode.equals("sol")) {
// Remove ExternalSource lines for Solidity files
content = content.replaceAll("(?m)^==== ExternalSource:.*$", "");
input = CharStreams.fromString(content);
} else {
// Wrap Yul in assembly statement
input = CharStreams.fromString("assembly " + content);
}

SolidityLexer lexer = new SolidityLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
SolidityParser parser = new SolidityParser(tokens);

// Remove default error listeners and add custom one
parser.removeErrorListeners();
lexer.removeErrorListeners();

ErrorCollector errorCollector = new ErrorCollector();
parser.addErrorListener(errorCollector);
lexer.addErrorListener(errorCollector);

// Parse based on mode
if (mode.equals("sol")) {
parser.sourceUnit();
} else {
parser.assemblyStatement();
}

boolean hasErrors = errorCollector.hasErrors();

// Output result
if (expectsError) {
if (hasErrors) {
System.out.println("PASS:" + filePath + ":FAILED_AS_EXPECTED");
} else {
System.out.println("FAIL:" + filePath + ":SUCCEEDED_DESPITE_PARSER_ERROR");
return false;
}
} else {
if (!hasErrors) {
System.out.println("PASS:" + filePath + ":OK");
} else {
System.out.println("FAIL:" + filePath + ":" + errorCollector.getErrors());
return false;
}
}
return true;
}

static class ErrorCollector extends BaseErrorListener {
private StringBuilder errors = new StringBuilder();
private boolean hasErrors = false;

@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol,
int line, int charPositionInLine,
String msg, RecognitionException e) {
hasErrors = true;
errors.append("line ").append(line).append(":").append(charPositionInLine)
.append(" ").append(msg).append("\n");
}

public boolean hasErrors() {
return hasErrors;
}

public String getErrors() {
return errors.toString();
}
}
}
118 changes: 51 additions & 67 deletions scripts/test_antlr_grammar.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@ ROOT_DIR=$(${READLINK} -f "$(dirname "$0")"/..)
WORKDIR="${ROOT_DIR}/build/antlr"
ANTLR_JAR="${ROOT_DIR}/build/deps/antlr4.jar"
ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.8-complete.jar"
BATCH_SIZE=100 # Process 100 files per JVM invocation

SGR_RESET="\033[0m"
SGR_BOLD="\033[1m"
SGR_GREEN="\033[32m"
SGR_RED="\033[31m"
SGR_BLUE="\033[34m"

vt_cursor_up() { echo -ne "\033[A"; }
vt_cursor_begin_of_line() { echo -ne "\r"; }

function download_antlr4
{
if [[ ! -e "$ANTLR_JAR" ]]
Expand Down Expand Up @@ -47,61 +45,10 @@ java -jar "${ANTLR_JAR}" SolidityParser.g4 SolidityLexer.g4 -o "${WORKDIR}/src/"

# Compile lexer/parser sources
javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/"
)

# Run tests
failed_count=0
function test_file
{
local SOL_FILE
SOL_FILE="$(${READLINK} -m "${1}")"
local cur=${2}
local max=${3}
local solOrYul=${4}

echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ..."
local output
if [[ "${solOrYul}" == "sol" ]]; then
output=$(
grep -v "^==== ExternalSource:" "${SOL_FILE}" | java \
-classpath "${ANTLR_JAR}:${WORKDIR}/target/" \
"org.antlr.v4.gui.TestRig" \
Solidity \
sourceUnit 2>&1
)
else
output=$(
echo "assembly $(cat "${SOL_FILE}")" | java \
-classpath "${ANTLR_JAR}:${WORKDIR}/target/" \
"org.antlr.v4.gui.TestRig" \
Solidity \
assemblyStatement 2>&1
)
fi
vt_cursor_up
vt_cursor_begin_of_line
if grep -qE "^\/\/ ParserError" "${SOL_FILE}"; then
if [[ "${output}" != "" ]]
then
echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}FAILED AS EXPECTED${SGR_RESET}"
else
echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}SUCCEEDED DESPITE PARSER ERROR${SGR_RESET}"
echo "${output}"
failed_count=$((failed_count + 1))
exit 1
fi
else
if [[ "${output}" == "" ]]
then
echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}"
else
echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}"
echo "${output}"
failed_count=$((failed_count + 1))
exit 1
fi
fi
}
# Compile AntlrBatchTestRig
javac -classpath "${ANTLR_JAR}:${WORKDIR}/target/" "${ROOT_DIR}/scripts/AntlrBatchTestRig.java" -d "${WORKDIR}/target/"
)

# we only want to use files that do not contain excluded parser errors, analysis errors or multi-source files.
SOL_FILES=()
Expand Down Expand Up @@ -154,17 +101,54 @@ done < <(
"${ROOT_DIR}/test/libyul/yulOptimizerTests"
)

num_tests=$((${#SOL_FILES[*]} + ${#YUL_FILES[*]}))
# Combine all files into one array
ALL_FILES=("${SOL_FILES[@]}" "${YUL_FILES[@]}")
num_tests=${#ALL_FILES[@]}
failed_count=0
test_count=0
for SOL_FILE in "${SOL_FILES[@]}"
do
test_count=$((test_count + 1))
test_file "${SOL_FILE}" ${test_count} $num_tests "sol"
done
for YUL_FILE in "${YUL_FILES[@]}"
do
test_count=$((test_count + 1))
test_file "${YUL_FILE}" ${test_count} $num_tests "yul"

echo "Testing $num_tests files..."

# Process all files in batches
for ((i=0; i<${#ALL_FILES[@]}; i+=BATCH_SIZE)); do
batch=("${ALL_FILES[@]:i:BATCH_SIZE}")
batch_size=${#batch[@]}

echo "Processing batch $((i/BATCH_SIZE + 1)) (${batch_size} files)..."

# Run batch
output=$(java -classpath "${ANTLR_JAR}:${WORKDIR}/target/" AntlrBatchTestRig "${batch[@]}" 2>&1) || true

# Parse output
while IFS= read -r line; do
if [[ -z "$line" ]]; then
continue
fi

test_count=$((test_count + 1))

if [[ "$line" =~ ^PASS:(.+):(.*) ]]; then
file="${BASH_REMATCH[1]}"
status="${BASH_REMATCH[2]}"
if [[ "$status" == "FAILED_AS_EXPECTED" ]]; then
echo -e "${SGR_BLUE}[${test_count}/${num_tests}] ${file}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}FAILED AS EXPECTED${SGR_RESET}"
else
echo -e "${SGR_BLUE}[${test_count}/${num_tests}] ${file}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}"
fi
elif [[ "$line" =~ ^FAIL:(.+):(.*) ]]; then
file="${BASH_REMATCH[1]}"
error="${BASH_REMATCH[2]}"
echo -e "${SGR_BLUE}[${test_count}/${num_tests}] ${file}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}"
echo "$error"
failed_count=$((failed_count + 1))
elif [[ "$line" =~ ^ERROR:(.+):(.*) ]]; then
file="${BASH_REMATCH[1]}"
error="${BASH_REMATCH[2]}"
echo -e "${SGR_BLUE}[${test_count}/${num_tests}] ${file}${SGR_RESET} ${SGR_BOLD}${SGR_RED}ERROR${SGR_RESET}"
echo "$error"
failed_count=$((failed_count + 1))
fi
done <<< "$output"
done

echo "Summary: ${failed_count} of $num_tests sources failed."
Expand Down