Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
8c385c4
Implement clean-room ANTLR Rust runtime
tinovyatkin May 17, 2026
455f1d8
Add ANTLR runtime testsuite harness
tinovyatkin May 17, 2026
f9414cc
Improve lexer runtime-testsuite conformance
tinovyatkin May 17, 2026
045fd3e
Run parser runtime-testsuite smoke descriptors
tinovyatkin May 17, 2026
6561448
Improve parser runtime-testsuite conformance
tinovyatkin May 17, 2026
ab5f34e
Expand parser runtime testsuite coverage
tinovyatkin May 18, 2026
1cdc314
Add lexer target action support
tinovyatkin May 18, 2026
4a206a5
Support parser token label text actions
tinovyatkin May 18, 2026
3dec6da
Support parser after stdout actions
tinovyatkin May 18, 2026
906b560
Build nested trees for parser actions
tinovyatkin May 18, 2026
574a45c
Support labeled parse tree after actions
tinovyatkin May 18, 2026
2189e07
Shape left-recursive parse trees
tinovyatkin May 18, 2026
e34456e
Support parser runtime helper actions
tinovyatkin May 18, 2026
11634c6
Support parser no-op target templates
tinovyatkin May 18, 2026
1856b80
Support parser member no-op templates
tinovyatkin May 18, 2026
ace3320
Report parser mismatch diagnostics
tinovyatkin May 18, 2026
8072b64
Support single-token parser recovery
tinovyatkin May 18, 2026
661ae48
Render target token actions and lexer predicates
tinovyatkin May 18, 2026
b5d4a2e
Render parser expected-token init actions
tinovyatkin May 18, 2026
5336c22
Render alt-numbered parse trees
tinovyatkin May 18, 2026
96cb00a
Support position-adjusting lexer template
tinovyatkin May 18, 2026
fada78d
Render non-greedy if-else text actions
tinovyatkin May 18, 2026
7c1a701
Evaluate parser lookahead predicates
tinovyatkin May 19, 2026
d57e6f5
Admit extraneous input diagnostics
tinovyatkin May 19, 2026
d7def9b
Recover mismatched parse-tree tokens
tinovyatkin May 19, 2026
1f5e291
Honor parser decision order for actions
tinovyatkin May 19, 2026
7df5b0f
Admit ordered parser predicates
tinovyatkin May 19, 2026
0db15c5
Support parser LTEquals predicates
tinovyatkin May 19, 2026
e3ac4e3
Support AppendStr token text actions
tinovyatkin May 19, 2026
2e976f9
Support runtime listener templates
tinovyatkin May 19, 2026
f6d41ca
Support left recursion return value actions
tinovyatkin May 19, 2026
7a3a1fb
Admit common label compile-check templates
tinovyatkin May 19, 2026
f37d2da
Support selected semantic predicate templates
tinovyatkin May 19, 2026
0c7359b
Support parser rule argument predicates
tinovyatkin May 19, 2026
79fb57e
Support parser member target templates
tinovyatkin May 19, 2026
8f9e0ea
Admit multi-token deletion before loop diagnostic
tinovyatkin May 19, 2026
0c9b827
Preserve inherited recovery expectations at loop decisions
tinovyatkin May 19, 2026
458acaf
Recover by deleting tokens at decision contexts
tinovyatkin May 19, 2026
f62189f
Recover through repeated loop token deletion
tinovyatkin May 19, 2026
585d3e7
Buffer lexer diagnostics for parser recovery order
tinovyatkin May 19, 2026
619b787
Report no viable LL parser alternatives
tinovyatkin May 19, 2026
422cf75
Handle empty complement parser diagnostics
tinovyatkin May 19, 2026
c12944e
Report no viable failed predicates
tinovyatkin May 19, 2026
0ad5e71
Run basic composite grammar descriptors
tinovyatkin May 19, 2026
4bf9d38
Filter mixed grammar action templates
tinovyatkin May 19, 2026
4833fe3
Handle composite parser import actions
tinovyatkin May 19, 2026
ea8f1fb
Capture parser rule return actions
tinovyatkin May 19, 2026
4e789bd
Respect lexer action ownership
tinovyatkin May 19, 2026
e063753
Admit LL prediction mode
tinovyatkin May 19, 2026
ec18ecc
Support parser ambiguity diagnostics
tinovyatkin May 19, 2026
39b766a
Evaluate boolean member predicates
tinovyatkin May 19, 2026
d15decd
Record lexer DFA dumps
tinovyatkin May 19, 2026
1149df8
Support lexer column predicates
tinovyatkin May 20, 2026
2819e12
Support SLL prediction mode
tinovyatkin May 20, 2026
2555d06
Complete semantic lexer DFA fixtures
tinovyatkin May 20, 2026
28cf079
Recover failed child parser rules
tinovyatkin May 20, 2026
64e0bd4
Handle EOF unwind recovery
tinovyatkin May 20, 2026
083e0d2
Support predicate fail options
tinovyatkin May 20, 2026
23bd14d
Enable full context runtime cases
tinovyatkin May 20, 2026
695bef7
Default runtime testsuite inputs
tinovyatkin May 20, 2026
50add82
Prepare crate publication
tinovyatkin May 20, 2026
435f4ee
Use BSD license and crate name
tinovyatkin May 20, 2026
086e198
Fix README JSON example
tinovyatkin May 20, 2026
1cec68a
Adopt stricter Clippy policy
tinovyatkin May 20, 2026
c3f39ac
Add GitHub Actions workflows
tinovyatkin May 20, 2026
6510a2b
Add copy paste detection workflow
tinovyatkin May 20, 2026
4855190
Extract shared generator helpers
tinovyatkin May 20, 2026
410e955
Address PR review feedback
tinovyatkin May 20, 2026
42bd95e
Document antlr-ng metadata workflow
tinovyatkin May 20, 2026
1a98bd3
Address PR review findings
tinovyatkin May 20, 2026
c128f43
Address follow-up review findings
tinovyatkin May 20, 2026
0b66e33
Address parser review follow-ups
tinovyatkin May 20, 2026
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
105 changes: 105 additions & 0 deletions .clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Complexity thresholds
allowed-idents-below-min-chars = ["..", "id", "x", "y", "z", "i", "j", "k", "n", "m"]
cognitive-complexity-threshold = 15
excessive-nesting-threshold = 8
min-ident-chars-threshold = 2
single-char-binding-names-threshold = 3
too-many-arguments-threshold = 6
# The generator and runtime-testsuite harness still contain a few deliberately
# dense functions. Keep this inherited strict-policy threshold explicit so new
# jumbo helpers fail Clippy without forcing premature splits in those modules.
too-many-lines-threshold = 476
Comment thread
tinovyatkin marked this conversation as resolved.
trivial-copy-size-limit = 16
type-complexity-threshold = 300

avoid-breaking-exported-api = false

# Testing allowances
allow-dbg-in-tests = true
allow-expect-in-tests = true
allow-print-in-tests = true
allow-unwrap-in-tests = true
suppress-restriction-lint-in-const = true

# Trait implementations
allow-renamed-params-for = ["core::fmt::Debug", "core::fmt::Display", "futures_sink::Sink", "serde::de::Visitor", ".."]

# Documentation
doc-valid-idents = [
"..",
"ANTLR",
"CodeQL",
"CPython",
"FastAPI",
"IPython",
"LangChain",
"LibCST",
"McCabe",
"NumPy",
"SCREAMING_SNAKE_CASE",
"SQLAlchemy",
"StackOverflow",
"PyCharm",
"SNMPv1",
"SNMPv2",
"SNMPv3",
"PyFlakes",
"GraphQL",
"gRPC",
"WebSocket",
"PostgreSQL",
"MySQL",
"SQLite",
"MongoDB",
"Redis",
"Kubernetes",
"GitHub",
"GitLab",
]

disallowed-names = ["foo", "bar", "baz", "tmp", "qux", "temp", "test", "dummy"]

disallowed-types = [
{ path = "std::collections::HashMap", reason = "Non-deterministic iter - use indexmap::IndexMap instead" },
{ path = "std::collections::HashSet", reason = "Non-deterministic iter - use indexmap::IndexSet instead" },
{ path = "std::sync::Once", reason = "Use std::sync::OnceLock for lazy initialization (available since 1.70)" },
{ path = "rand::rngs::ThreadRng", reason = "ThreadRng is inherently non-deterministic; use a fixed-seed or hash-based approach instead." },
]

disallowed-methods = [
{ path = "rand::random", reason = "Use a deterministic hash (e.g. via a cryptographic hasher like Sha256) instead of `rand::random`." },
{ path = "rand::Rng::gen", reason = "Use a deterministic hash (e.g. via Sha256) rather than generating random bytes at runtime.", allow-invalid = true },

{ path = "str::to_ascii_lowercase", reason = "Avoid hidden allocation; use an explicit conversion helper." },
{ path = "str::to_ascii_uppercase", reason = "Avoid hidden allocation; use an explicit conversion helper." },
{ path = "str::to_lowercase", reason = "Avoid hidden allocation; use an explicit conversion helper." },
{ path = "str::to_uppercase", reason = "Avoid hidden allocation; use an explicit conversion helper." },
{ path = "str::replace", reason = "Avoid hidden allocation in hot paths." },
{ path = "str::replacen", reason = "Avoid hidden allocation in hot paths." },

{ path = "std::mem::forget", reason = "future::scope is unsafe when used with forget" },
{ path = "futures::executor::block_on", reason = "block_on can cause deadlock easily" },
{ path = "async_std::task::block_on", reason = "block_on can cause deadlock easily" },
{ path = "pollster::block_on", reason = "block_on can cause deadlock easily" },

{ path = "std::time::Instant::now", reason = "Do not use current date/time in code that must be deterministic" },
{ path = "namada_core::time::DateTimeUtc::now", reason = "Do not use current date/time in code that must be deterministic" },
{ path = "wasmtimer::std::Instant", reason = "Do not use current date/time in code that must be deterministic" },

{ path = "std::iter::Iterator::for_each", reason = "prefer `for` for side-effects" },

{ path = "std::option::Option::unwrap", reason = "use `expect` with descriptive message or handle None case" },
{ path = "std::result::Result::unwrap", reason = "use `expect` with descriptive message or handle error case" },
{ path = "std::panic::catch_unwind", reason = "panics are not for control flow - use Result" },
{ path = "std::process::exit", reason = "return Result from main() instead of exiting" },
{ path = "std::thread::sleep", reason = "use async sleep in async contexts, or document why blocking is needed" },
{ path = "std::mem::transmute", reason = "use safe alternatives like bytemuck or zerocopy" },
{ path = "std::mem::uninitialized", reason = "deprecated - use MaybeUninit" },
{ path = "std::mem::zeroed", reason = "use MaybeUninit::zeroed or Default::default" },

{ path = "tokio::task::spawn_blocking", reason = "document why blocking is necessary - consider async alternatives" },
]

ignore-interior-mutability = []

allowed-duplicate-crates = []
53 changes: 53 additions & 0 deletions .github/workflows/antlr-runtime-testsuite.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: ANTLR Runtime Testsuite

on:
pull_request:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read

env:
ANTLR_VERSION: 4.13.2
ANTLR4_JAR: /tmp/antlr-cleanroom/tools/antlr-4.13.2-complete.jar
ANTLR4_RUNTIME_TESTSUITE: /tmp/antlr-cleanroom/antlr4-upstream/runtime-testsuite
CARGO_TERM_COLOR: always

jobs:
runtime-testsuite:
name: Runtime Testsuite
runs-on: ubuntu-latest
timeout-minutes: 90

steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false

- name: Install stable Rust
run: |
rustup toolchain install stable --profile minimal --no-self-update
rustup default stable

- name: Install Java
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
with:
distribution: temurin
java-version: "21"

- name: Prepare ANTLR runtime testsuite
run: |
set -euxo pipefail
mkdir -p /tmp/antlr-cleanroom/tools
curl --fail --location --output "${ANTLR4_JAR}" \
"https://www.antlr.org/download/antlr-${ANTLR_VERSION}-complete.jar"
git clone --depth 1 --branch "${ANTLR_VERSION}" \
https://github.com/antlr/antlr4.git \
/tmp/antlr-cleanroom/antlr4-upstream

- name: Run ANTLR runtime testsuite
run: cargo run --locked --quiet --bin antlr4-runtime-testsuite
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

env:
CARGO_TERM_COLOR: always

jobs:
check:
name: Clippy and Unit Tests
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false

- name: Install stable Rust
run: |
rustup toolchain install stable --profile minimal --component clippy --no-self-update
rustup default stable

- name: Run Clippy
run: cargo clippy --locked --all-targets --all-features -- -D warnings

- name: Run unit tests
run: cargo test --locked
178 changes: 178 additions & 0 deletions .github/workflows/cpd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
name: Copy/Paste Detection

on:
pull_request:
branches:
- main
paths:
- "**/*.rs"
- "!target/**"
- ".github/workflows/cpd.yml"

permissions:
contents: read
pull-requests: write

concurrency:
group: cpd-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

env:
PMD_VERSION: 7.20.0
CPD_TOKENS: "100"
COMMENT_MARKER: <!-- cpd-report -->

jobs:
cpd:
name: Copy/Paste Detection
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
persist-credentials: false

Comment thread
coderabbitai[bot] marked this conversation as resolved.
- name: Determine changed Rust files
id: changed
shell: bash
run: |
set -euo pipefail
BASE_SHA=$(git merge-base "origin/${GITHUB_BASE_REF}" HEAD)

git diff --name-only --diff-filter=d "$BASE_SHA" HEAD -- "*.rs" \
| grep -Ev "^target/" \
> changed-files.txt || true

COUNT=$(wc -l < changed-files.txt | tr -d " ")
echo "count=$COUNT" >> "$GITHUB_OUTPUT"
echo "Changed Rust files ($COUNT):"
cat changed-files.txt || true

- name: Setup Java
if: steps.changed.outputs.count != '0'
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
with:
distribution: temurin
java-version: "21"

- name: Setup PMD
if: steps.changed.outputs.count != '0'
shell: bash
run: |
set -euo pipefail
curl -fL "https://github.com/pmd/pmd/releases/download/pmd_releases%2F${PMD_VERSION}/pmd-dist-${PMD_VERSION}-bin.zip" -o pmd.zip
unzip -q pmd.zip
rm pmd.zip

- name: Run CPD
id: cpd
shell: bash
run: |
set -uo pipefail

if [ "${{ steps.changed.outputs.count }}" = "0" ]; then
: > cpd-report.md
echo "duplications=0" >> "$GITHUB_OUTPUT"
echo "No changed Rust files."
exit 0
fi

# PMD CPD exit codes:
# 0 - no duplications
# 4 - duplications found
# 5 - recoverable errors, for example a file failed to lex
set +e
"pmd-bin-${PMD_VERSION}/bin/pmd" cpd \
--language rust \
--minimum-tokens "${CPD_TOKENS}" \
--file-list changed-files.txt \
--format markdown \
> cpd-report.md 2> cpd-stderr.log
STATUS=$?
set -e

if [ "$STATUS" -ne 0 ] && [ "$STATUS" -ne 4 ] && [ "$STATUS" -ne 5 ]; then
echo "PMD CPD errored (status $STATUS):"
cat cpd-stderr.log
exit "$STATUS"
fi

if [ -s cpd-stderr.log ]; then
echo "=== PMD stderr ==="
cat cpd-stderr.log
fi

sed -i "s|${GITHUB_WORKSPACE}/||g" cpd-report.md

awk '
BEGIN { open = 0 }
/^```$/ {
if (open == 0) { print "```rust"; open = 1 }
else { print "```"; open = 0 }
next
}
{ print }
' cpd-report.md > cpd-report.tagged.md
mv cpd-report.tagged.md cpd-report.md

DUP_COUNT=$(grep -c "^Found a " cpd-report.md || true)
echo "duplications=${DUP_COUNT:-0}" >> "$GITHUB_OUTPUT"

echo "=== Report ==="
cat cpd-report.md

- name: Build comment body
shell: bash
env:
CHANGED_COUNT: ${{ steps.changed.outputs.count }}
DUP_COUNT: ${{ steps.cpd.outputs.duplications }}
run: |
set -euo pipefail
{
echo "${COMMENT_MARKER}"
echo "## Copy/Paste Detection"
echo ""
if [ "${DUP_COUNT:-0}" = "0" ]; then
echo "No duplications found in ${CHANGED_COUNT} changed Rust file(s) (threshold: ${CPD_TOKENS} tokens)."
else
echo "Found **${DUP_COUNT}** duplication(s) across ${CHANGED_COUNT} changed Rust file(s) (threshold: ${CPD_TOKENS} tokens)."
echo ""
echo "<details>"
echo "<summary>Show duplications</summary>"
echo ""
cat cpd-report.md
echo ""
echo "</details>"
fi
} > comment-body.md

SIZE=$(wc -c < comment-body.md)
if [ "$SIZE" -gt 60000 ]; then
head -c 60000 comment-body.md > comment-body.trunc.md
printf "\n\n_(report truncated; full output in workflow logs)_\n" >> comment-body.trunc.md
mv comment-body.trunc.md comment-body.md
fi

cat comment-body.md

- name: Post sticky PR comment
if: github.event.pull_request.head.repo.full_name == github.repository
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
shell: bash
run: |
set -euo pipefail
BODY=$(cat comment-body.md)

COMMENT_ID=$(gh api "repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" \
--paginate -q ".[] | select(.body | startswith(\"${COMMENT_MARKER}\")) | .id" | head -1)

if [ -n "$COMMENT_ID" ]; then
gh api "repos/${{ github.repository }}/issues/comments/${COMMENT_ID}" \
-X PATCH -f body="$BODY"
else
gh pr comment "$PR_NUMBER" --body "$BODY"
fi
Loading
Loading