Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
2,440 changes: 2,244 additions & 196 deletions Cargo.lock

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,21 @@ its source spelling and a colliding native helper is exposed as `context_*`.
Labeled alternatives currently materialize `_localctx` through the base rule
context, so alternative-only children remain outside this compatibility surface.

The CLI also discovers the current grammars-v4 C and Java
`Rust/transformGrammar.py` files next to grammar roots. It fingerprints the
`Rust/` folder and requires explicit trust before running the transform with
bundled RustPython in a child over a disposable grammar-tree copy. Interactive
choices can trust once, the exact revision, or the repository; non-interactive
invocations fail with the exact `--trust-rust-support sha256:...` argument.
This trusted-source path does not claim to sandbox the host. It stages inputs
to preserve the checkout and automatically emits transformed grammars and
shipped Rust support modules under `antlr-rust-support/`, plus a
`rust-support.json` audit manifest.

Python compatibility is intentionally pinned to the imports and filesystem
operations used by those two transforms. Other or future sibling scripts must
be evaluated explicitly before they are considered supported.

### Decision Tiers and `--fixed-lookahead`

ANTLR always generates an adaptive `ALL(*)` recognizer: every decision point
Expand Down
14 changes: 14 additions & 0 deletions crates/antlr-rust-codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ name = "antlr4-rust-testrig"
path = "src/bin/antlr4-rust-testrig.rs"

[dependencies]
anstream = "1"
anstyle = "1"
antlr-rust-g4-parser.workspace = true
antlr-rust-rs-parser.workspace = true
antlr-rust-runtime.workspace = true
Expand All @@ -46,7 +48,19 @@ icu_properties.workspace = true
intl = { version = "0.6.0", default-features = false, features = ["full", "normalization"] }
miette = { version = "7.6.0", default-features = false, features = ["fancy"] }
petgraph = { version = "0.8.3", default-features = false, features = ["std"] }
rustpython-pylib = { version = "0.5.0", features = ["freeze-stdlib"] }
rustpython-vm = { version = "0.5.0", default-features = false, features = [
"compiler",
"freeze-stdlib",
"gc",
"host_env",
"stdio",
] }
serde = { version = "1", features = ["derive"] }
sha2 = "0.10"
tempfile = "3"
thiserror.workspace = true
toml = "0.9"

[dev-dependencies]
hmac-sha256 = "1"
Expand Down
40 changes: 40 additions & 0 deletions crates/antlr-rust-codegen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,46 @@ Or install the command:
cargo install antlr-rust-codegen --bin antlr4-rust-gen
```

## Trusted grammars-v4 Rust support

`antlr4-rust-gen` recognizes a sibling `Rust/transformGrammar.py` for the
current C and Java target-support folders in `antlr/grammars-v4`. Before
executing one, an interactive terminal shows its repository, revision, and
content fingerprint and offers four choices: trust once, trust the exact
revision, trust the repository, or abort.

The approved transform runs with bundled RustPython in a child process over a
disposable copy of the complete grammar directory. This is a trusted-source
workflow, not a security sandbox. The original checkout is never modified.
The child has a 30-second deadline and bounded output capture; use
`--rust-support-timeout SECONDS` for a slower trusted transform.
Transformed grammars, shipped `.rs` support files, and `rust-support.json` are
emitted with the generated sources so the inputs remain inspectable. Embedded
actions, strict semantics, generated-only parser coverage, and `superClass`
acknowledgement are selected automatically for those staged sources.
Shipped helpers are available through the generated recognizer module's stable
`rust_support` re-export, for example
`c_parser::rust_support::c_parser_base::reset_symbol_table()`.

Non-interactive use fails before execution and prints the exact opt-in:

```bash
antlr4-rust-gen JavaLexer.g4 JavaParser.g4 \
--trust-rust-support sha256:<fingerprint> \
--out-dir src/generated
```

Persisted choices live in
`$XDG_CONFIG_HOME/antlr4-rust/trusted-support.toml` (or the platform config
directory). Set `ANTLR4_RUST_TRUST_STORE` to use a different file. Automatic
transform execution is currently an `antlr4-rust-gen` CLI feature; the
`Builder` API does not execute sibling scripts.

The bundled Python compatibility target is deliberately limited to the imports
and filesystem operations used by the two existing grammars-v4 transforms
(`c/Rust` and `java/java/Rust`). A future transform with additional Python
requirements is a new compatibility decision, not implicitly supported.

The package also provides `antlr4-rust-testrig` for running a grammar directly
against UTF-8 files or stdin. A grammar-only project needs no `Cargo.toml`,
generated Rust sources, or runtime dependency; only a Rust toolchain is
Expand Down
2 changes: 2 additions & 0 deletions crates/antlr-rust-codegen/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::config::CompilerConfig;
use crate::driver;
use crate::error::Error;
use crate::parser::MAX_FIXED_LOOKAHEAD_FLAG;
use crate::rust_support::RustSupportOptions;
use crate::semantics::{
SemPatternFile, SemUnknownPolicy, load_sem_patterns, normalize_option_hook,
};
Expand Down Expand Up @@ -240,6 +241,7 @@ impl Builder {
optimize_precedence_ladders: self.optimize_precedence_ladders,
report_precedence_ladders: self.report_precedence_ladders,
test_rig: None,
rust_support: RustSupportOptions::disabled(),
})
}
}
Expand Down
54 changes: 50 additions & 4 deletions crates/antlr-rust-codegen/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::io::{self, Write as _};
use std::io::{self, IsTerminal as _, Write as _};
use std::path::PathBuf;
use std::time::Duration;

use clap::{ArgAction, Parser};
use miette::{Context as _, IntoDiagnostic as _};
Expand All @@ -8,16 +9,36 @@ use crate::builder::{ActionMode, UnknownSemanticPolicy};
use crate::config::CompilerConfig;
use crate::driver::generate;
use crate::parser::MAX_FIXED_LOOKAHEAD_FLAG;
use crate::rust_support::{RustSupportOptions, parse_trust_fingerprint, run_transform_child};
use crate::semantics::{SemPatternFile, load_sem_patterns, normalize_option_hook};

pub(crate) fn run_cli() -> miette::Result<()> {
let args = CliArgs::parse().into_config()?;
let mut stderr = io::stderr().lock();
generate(&args, |message| writeln!(stderr, "{message}"))
if let Some(staging_directory) = transform_child_argument()? {
return run_transform_child(&staging_directory);
}
let args = CliArgs::parse();
let config = args.into_config()?;
generate(&config, |message| writeln!(io::stderr(), "{message}"))
.map_err(crate::cli_report::codegen_error)?;
Ok(())
}

fn transform_child_argument() -> miette::Result<Option<PathBuf>> {
let mut arguments = std::env::args_os().skip(1);
if arguments.next().as_deref() != Some(std::ffi::OsStr::new("--__antlr-rust-transform")) {
return Ok(None);
}
let directory = arguments.next().map(PathBuf::from).ok_or_else(|| {
miette::miette!("--__antlr-rust-transform requires a staged grammar directory")
})?;
if arguments.next().is_some() {
return Err(miette::miette!(
"--__antlr-rust-transform accepts exactly one staged grammar directory"
));
}
Ok(Some(directory))
}

#[derive(Debug, Parser)]
#[command(
name = "antlr4-rust-gen",
Expand Down Expand Up @@ -127,6 +148,23 @@ struct CliArgs {
/// Dry-run precedence-ladder analysis and emit only optimizations.json.
#[arg(long, conflicts_with = "optimize_precedence_ladders")]
report_precedence_ladders: bool,

/// Trust one exact sibling Rust support bundle fingerprint.
#[arg(
long = "trust-rust-support",
value_name = "SHA256",
value_parser = parse_trust_fingerprint
)]
trusted_rust_support: Vec<String>,

/// Maximum time allowed for a trusted Rust support transform.
#[arg(
long = "rust-support-timeout",
value_name = "SECONDS",
default_value_t = 30,
value_parser = clap::value_parser!(u64).range(1..=3600)
)]
rust_support_timeout: u64,
}

impl CliArgs {
Expand Down Expand Up @@ -158,6 +196,14 @@ impl CliArgs {
optimize_precedence_ladders: self.optimize_precedence_ladders,
report_precedence_ladders: self.report_precedence_ladders,
test_rig: None,
rust_support: RustSupportOptions {
enabled: true,
trusted_fingerprints: self.trusted_rust_support.into_iter().collect(),
interactive: io::stdin().is_terminal() && io::stderr().is_terminal(),
child_executable: Some(std::env::current_exe().into_diagnostic()?),
trust_store_path: std::env::var_os("ANTLR4_RUST_TRUST_STORE").map(PathBuf::from),
transform_timeout: Duration::from_secs(self.rust_support_timeout),
},
Comment thread
tinovyatkin marked this conversation as resolved.
Resolved
})
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/antlr-rust-codegen/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::BTreeSet;
use std::path::PathBuf;

use crate::rust_support::RustSupportOptions;
use crate::semantics::{SemPatternFile, SemUnknownPolicy};

#[derive(Debug)]
Expand Down Expand Up @@ -38,4 +39,6 @@ pub(crate) struct CompilerConfig {
pub(crate) report_precedence_ladders: bool,
/// Emit a temporary-crate entry point for `antlr4-rust-testrig`.
pub(crate) test_rig: Option<TestRigConfig>,
/// Trusted sibling `Rust/transformGrammar.py` execution.
pub(crate) rust_support: RustSupportOptions,
}
Loading
Loading