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
76 changes: 43 additions & 33 deletions tools/aprender-crux-judge/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,49 +196,59 @@ fn sandbox_report(probed: Result<&sandbox::Sandbox, &str>) -> (String, u8) {
}
}

fn main() -> ExitCode {
let mut argv = Vec::new();
for a in std::env::args_os().skip(1) {
match a.into_string() {
Ok(s) => argv.push(s),
Err(a) => {
return ExitCode::from(usage_error(&format!(
"argument is not UTF-8: {}",
a.to_string_lossy()
)));
}
}
}
match argv.first().map(String::as_str) {
Some("eval" | "extract" | "lint") => return ExitCode::from(oracles::cli(&argv)),
Some("certify" | "check") => return ExitCode::from(certify::cli(&argv)),
_ => {}
}
if argv.first().map(String::as_str) == Some("sandbox") {
if let Some(extra) = argv.get(1) {
return ExitCode::from(usage_error(&format!("unrecognized arguments: {extra}")));
}
let (line, rc) = sandbox_report(sandbox::sandbox());
println!("{line}");
return ExitCode::from(rc);
/// The arguments after the program name, or the usage exit code for the first one that is not UTF-8.
fn utf8_args() -> Result<Vec<String>, u8> {
std::env::args_os()
.skip(1)
.map(|a| {
a.into_string().map_err(|a| {
usage_error(&format!("argument is not UTF-8: {}", a.to_string_lossy()))
})
})
.collect()
}

/// `sandbox`: print the probe's one line; it takes no arguments.
fn sandbox_cmd(argv: &[String]) -> u8 {
if let Some(extra) = argv.get(1) {
return usage_error(&format!("unrecognized arguments: {extra}"));
}
let args = match parse(&argv) {
Parsed::Run(args) => args,
Parsed::Exit(rc) => return ExitCode::from(rc),
};
match collect::collect(&args) {
Ok(rc) => ExitCode::from(u8::try_from(rc).unwrap_or(1)),
let (line, rc) = sandbox_report(sandbox::sandbox());
println!("{line}");
rc
}

/// The default command: collect, with a decline exiting 2 and a crash 1.
fn collect_cmd(args: &collect::Args) -> u8 {
match collect::collect(args) {
Ok(rc) => u8::try_from(rc).unwrap_or(1),
Err(e) if e.declines() => {
eprintln!("decline: {}: {e}", e.kind);
ExitCode::from(2)
2
}
Err(e) => {
eprintln!("crash: {}: {e}", e.kind);
ExitCode::from(1)
1
}
}
}

fn run(argv: &[String]) -> u8 {
match argv.first().map(String::as_str) {
Some("eval" | "extract" | "lint") => oracles::cli(argv),
Some("certify" | "check") => certify::cli(argv),
Some("sandbox") => sandbox_cmd(argv),
_ => match parse(argv) {
Parsed::Run(args) => collect_cmd(&args),
Parsed::Exit(rc) => rc,
},
}
}

fn main() -> ExitCode {
ExitCode::from(utf8_args().map_or_else(|rc| rc, |argv| run(&argv)))
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
40 changes: 33 additions & 7 deletions tools/aprender-crux-judge/src/pyjson.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ enum ScanErr {

type Scan<T> = Result<T, ScanErr>;

/// The value of one of `Doc::scan_literal`'s bare words.
fn literal_value(lit: &str) -> Val {
match lit {
"true" => Val::Bool(true),
"false" => Val::Bool(false),
"NaN" => Val::Float(f64::NAN),
"Infinity" => Val::Float(f64::INFINITY),
"-Infinity" => Val::Float(f64::NEG_INFINITY),
_ => Val::None,
}
}

/// Where a container scan goes after an item.
enum Step {
/// At the closing bracket.
Expand Down Expand Up @@ -56,16 +68,20 @@ impl Doc<'_> {
'"' => self.scanstring(idx + 1).map(|(v, e)| (Val::Str(v), e)),
'{' => self.parse_object(idx + 1),
'[' => self.parse_array(idx + 1),
'n' if self.starts(idx, "null") => Ok((Val::None, idx + 4)),
't' if self.starts(idx, "true") => Ok((Val::Bool(true), idx + 4)),
'f' if self.starts(idx, "false") => Ok((Val::Bool(false), idx + 5)),
'N' if self.starts(idx, "NaN") => Ok((Val::Float(f64::NAN), idx + 3)),
'I' if self.starts(idx, "Infinity") => Ok((Val::Float(f64::INFINITY), idx + 8)),
'-' if self.starts(idx, "-Infinity") => Ok((Val::Float(f64::NEG_INFINITY), idx + 9)),
_ => self.match_number(idx),
_ => self
.scan_literal(idx)
.map_or_else(|| self.match_number(idx), Ok),
}
}

/// The bare-word constants Python's scanner accepts, tried before a number so `-Infinity` is not read as `-`.
fn scan_literal(&self, idx: usize) -> Option<(Val, usize)> {
["null", "true", "false", "NaN", "Infinity", "-Infinity"]
.into_iter()
.find(|lit| self.starts(idx, lit))
.map(|lit| (literal_value(lit), idx + lit.len()))
}

fn is_at(&self, i: usize, c: char) -> bool {
i < self.s.len() && self.s[i] == c
}
Expand Down Expand Up @@ -537,6 +553,16 @@ mod tests {
}
}

#[test]
fn bare_word_literals_decode() {
let v = loads("[null, true, false, NaN, Infinity, -Infinity, -1, -1.5]")
.expect("every bare word decodes");
assert_eq!(
dumps(&v, true).expect("the literals dump"),
"[null, true, false, NaN, Infinity, -Infinity, -1, -1.5]"
);
}

#[test]
fn surrogate_pair_joins() {
let v = loads("\"\\ud83d\\ude00x\"").expect("a surrogate pair decodes");
Expand Down
Loading