mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-06 08:18:58 +00:00
Support unquoted bare string values in DOT parser (e.g., gpt-5.2)
Add a bare_string parser that accepts values containing hyphens and dots like gpt-5.2-codex-spark and gemini-3-flash-preview. These are common in kilroy DOT files for model names but were previously rejected by arc's strict identifier parser. All 14 kilroy DOT files now parse successfully. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6d869aa8da
commit
77b7bf7481
2 changed files with 71 additions and 22 deletions
|
|
@ -238,7 +238,27 @@ pub mod combinators {
|
|||
Ok((rest, AstValue::Str(format!("{num}{unit}"))))
|
||||
}
|
||||
|
||||
/// Parse an AST value: duration, float, integer, boolean, quoted string, or bare identifier.
|
||||
/// Parse a bare string value containing hyphens and dots (e.g., `gpt-5.2-codex`).
|
||||
///
|
||||
/// Must start with an alpha/underscore character, then may continue with
|
||||
/// alphanumeric, underscore, hyphen, or dot characters. Must contain at
|
||||
/// least one hyphen or dot (otherwise `identifier` handles it).
|
||||
pub fn bare_string(input: &str) -> IResult<&str, String> {
|
||||
let (rest, raw) = recognize(pair(
|
||||
take_while1(|c: char| c.is_ascii_alphabetic() || c == '_'),
|
||||
take_while(|c: char| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '.'),
|
||||
))(input)?;
|
||||
if !raw.contains('-') && !raw.contains('.') {
|
||||
return Err(nom::Err::Error(nom::error::Error::new(
|
||||
input,
|
||||
nom::error::ErrorKind::Verify,
|
||||
)));
|
||||
}
|
||||
Ok((rest, raw.to_string()))
|
||||
}
|
||||
|
||||
/// Parse an AST value: duration, float, integer, boolean, quoted string, bare identifier,
|
||||
/// or bare string (e.g., `gpt-5.2-codex`).
|
||||
pub fn value(input: &str) -> IResult<&str, AstValue> {
|
||||
let input = input.trim_start();
|
||||
alt((
|
||||
|
|
@ -247,6 +267,7 @@ pub mod combinators {
|
|||
map(float_value, AstValue::Float),
|
||||
map(integer_value, AstValue::Int),
|
||||
map(boolean, AstValue::Bool),
|
||||
map(bare_string, AstValue::Str),
|
||||
map(identifier, |s: &str| AstValue::Ident(s.to_string())),
|
||||
))(input)
|
||||
}
|
||||
|
|
@ -403,4 +424,39 @@ mod tests {
|
|||
assert_eq!(value("true"), Ok(("", AstValue::Bool(true))));
|
||||
assert_eq!(value("LR"), Ok(("", AstValue::Ident("LR".into()))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_bare_string_with_hyphens_and_dots() {
|
||||
assert_eq!(
|
||||
bare_string("gpt-5.2 rest"),
|
||||
Ok((" rest", "gpt-5.2".into()))
|
||||
);
|
||||
assert_eq!(
|
||||
bare_string("gpt-5.2-codex"),
|
||||
Ok(("", "gpt-5.2-codex".into()))
|
||||
);
|
||||
assert_eq!(
|
||||
bare_string("gpt-5.3-codex-spark"),
|
||||
Ok(("", "gpt-5.3-codex-spark".into()))
|
||||
);
|
||||
assert_eq!(
|
||||
bare_string("gemini-3-flash-preview"),
|
||||
Ok(("", "gemini-3-flash-preview".into()))
|
||||
);
|
||||
// Plain identifier without hyphens/dots should fail (identifier handles it)
|
||||
assert!(bare_string("LR").is_err());
|
||||
assert!(bare_string("openai").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_value_bare_string() {
|
||||
assert_eq!(
|
||||
value("gpt-5.2"),
|
||||
Ok(("", AstValue::Str("gpt-5.2".into())))
|
||||
);
|
||||
assert_eq!(
|
||||
value("gpt-5.2-codex"),
|
||||
Ok(("", AstValue::Str("gpt-5.2-codex".into())))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,34 +26,27 @@ fn parse_kilroy_simple_example() {
|
|||
assert!(graph.find_exit_node().is_some());
|
||||
}
|
||||
|
||||
// The batch_*.dot files use unquoted values with hyphens/dots (e.g., `llm_model=gpt-5.2`)
|
||||
// which is valid in kilroy's more lenient Go parser but not in arc's strict DOT parser.
|
||||
// These tests document the parser gap: arc requires quoting such values.
|
||||
#[test]
|
||||
fn parse_kilroy_batch_clean_requires_quoted_model_values() {
|
||||
let err = parse_kilroy_dot("batch_clean.dot").unwrap_err();
|
||||
assert!(
|
||||
err.contains("grammar error"),
|
||||
"expected grammar error for unquoted `gpt-5.2`, got: {err}"
|
||||
);
|
||||
fn parse_kilroy_batch_clean() {
|
||||
let graph = parse_kilroy_dot("batch_clean.dot").unwrap();
|
||||
assert_eq!(graph.name, "G");
|
||||
assert_eq!(graph.nodes.len(), 3);
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_batch_has_errors_requires_quoted_model_values() {
|
||||
let err = parse_kilroy_dot("batch_has_errors.dot").unwrap_err();
|
||||
assert!(
|
||||
err.contains("grammar error"),
|
||||
"expected grammar error for unquoted `gpt-5.2`, got: {err}"
|
||||
);
|
||||
fn parse_kilroy_batch_has_errors() {
|
||||
// This file is intentionally missing llm_provider on the work node.
|
||||
// It should still parse successfully — validation is separate from parsing.
|
||||
let graph = parse_kilroy_dot("batch_has_errors.dot").unwrap();
|
||||
assert_eq!(graph.nodes.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_batch_warnings_only_requires_quoted_model_values() {
|
||||
let err = parse_kilroy_dot("batch_warnings_only.dot").unwrap_err();
|
||||
assert!(
|
||||
err.contains("grammar error"),
|
||||
"expected grammar error for unquoted `gpt-5.2`, got: {err}"
|
||||
);
|
||||
fn parse_kilroy_batch_warnings_only() {
|
||||
let graph = parse_kilroy_dot("batch_warnings_only.dot").unwrap();
|
||||
assert_eq!(graph.nodes.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue