use unilang_parser :: *;
use unilang_parser ::error :: { ErrorKind, SourceLocation };
fn options_error_on_positional_after_named() -> UnilangParserOptions
{
UnilangParserOptions {
error_on_positional_after_named: true,
..Default ::default()
}
}
#[ test ]
fn error_invalid_escape_sequence_location_str()
{
let parser = Parser ::new(UnilangParserOptions ::default());
let input = r#"cmd arg1 "value with \x invalid escape""#;
let result = parser.parse_repl_input(input);
assert!(
result.is_ok(),
"parse_single_instruction unexpectedly failed for input: {input}"
);
let instruction = result.unwrap();
assert_eq!(instruction.positional_arguments[0].value, "arg1".to_string());
assert_eq!(
instruction.positional_arguments[1].value,
"value with \\x invalid escape".to_string()
);
}
#[ test ]
fn error_unexpected_delimiter_location_str()
{
let parser = Parser ::new(UnilangParserOptions ::default());
let input = r"cmd ::arg2";
let result = parser.parse_repl_input(input);
assert!(
result.is_ok(),
"Named-only args should parse successfully (spec.md:173), input: '{}', error: {:?}",
input,
result.err()
);
let inst = result.unwrap();
assert!( inst.command_path_slices.is_empty() );
assert!( inst.named_arguments.contains_key( "cmd" ) );
assert_eq!( inst.named_arguments.get( "cmd" ).unwrap()[0].value, "arg2" );
}
#[ test ]
fn empty_instruction_segment_double_semicolon()
{
let parser = Parser ::new(UnilangParserOptions ::default());
let input = "cmd1 ;;";
let result = parser.parse_multiple_instructions(input); assert!(
result.is_err(),
"Expected error for empty segment due to ';;', input: '{input}'"
);
let err = result.unwrap_err();
assert_eq!(
err.kind,
ErrorKind ::TrailingDelimiter,
"Expected TrailingDelimiter error, but got: {:?}",
err.kind
); assert_eq!(err.location, Some(SourceLocation ::StrSpan { start: 5, end: 7 }));
}
#[ test ]
fn empty_instruction_segment_trailing_semicolon()
{
let parser = Parser ::new(UnilangParserOptions ::default());
let input = "cmd1 ;; ";
let result = parser.parse_multiple_instructions(input);
assert!(
result.is_err(),
"Expected error for empty segment due to trailing ';;', input: '{input}'"
);
let err = result.unwrap_err();
assert_eq!(
err.kind,
ErrorKind ::TrailingDelimiter,
"Expected TrailingDelimiter error, but got: {:?}",
err.kind
);
assert_eq!(err.location, Some(SourceLocation ::StrSpan { start: 5, end: 7 }));
}
#[ test ]
fn empty_instruction_segment_only_semicolon()
{
let parser = Parser ::new(UnilangParserOptions ::default());
let input = ";;";
let result = parser.parse_multiple_instructions(input);
assert!(
result.is_err(),
"Expected error for input being only ';;', input: '{input}'"
);
let err = result.unwrap_err();
assert_eq!(
err.kind,
ErrorKind ::EmptyInstructionSegment,
"Expected EmptyInstructionSegment error, but got: {:?}",
err.kind
);
assert_eq!(err.location, Some(SourceLocation ::StrSpan { start: 0, end: 2 }));
}
#[ test ]
fn missing_value_for_named_arg()
{
let parser = Parser ::new(UnilangParserOptions ::default());
let input = "cmd name :: ";
let result = parser.parse_repl_input(input);
assert!(
result.is_err(),
"Expected error for missing value for named arg, input: '{input}'"
);
let err = result.unwrap_err();
match err.kind
{
ErrorKind ::Syntax(s) => assert!(
s.contains("Expected value for named argument 'name' but found end of instruction"),
"Msg: {s}"
),
_ => panic!("Expected Syntax error, but got: {:?}", err.kind),
}
assert_eq!(err.location, Some(SourceLocation ::StrSpan { start: 4, end: 8 }));
}
#[ test ]
fn unexpected_colon_colon_no_name()
{
let parser = Parser ::new(UnilangParserOptions ::default());
let input = "cmd ::value";
let result = parser.parse_repl_input(input);
assert!(
result.is_ok(),
"Named-only args should parse successfully (spec.md:173), input: '{}', error: {:?}",
input,
result.err()
);
let inst = result.unwrap();
assert!( inst.command_path_slices.is_empty() );
assert!( inst.named_arguments.contains_key( "cmd" ) );
assert_eq!( inst.named_arguments.get( "cmd" ).unwrap()[0].value, "value" );
}
#[ test ]
fn unexpected_colon_colon_after_value()
{
let parser = Parser ::new(UnilangParserOptions ::default());
let input = "cmd name ::val1 ::val2";
let result = parser.parse_repl_input(input);
assert!(result.is_err(), "Expected error for 'name ::val1 ::val2', input: '{input}'");
let err = result.unwrap_err();
assert_eq!(
err.kind,
ErrorKind ::Syntax("Named argument operator '::' cannot appear by itself".to_string()),
"ErrorKind mismatch: {:?}",
err.kind
);
assert_eq!(err.location, Some(SourceLocation ::StrSpan { start: 16, end: 18 }));
}
#[ test ]
fn positional_after_named_error()
{
let parser = Parser ::new(options_error_on_positional_after_named());
let input = "cmd name ::val pos1";
let result = parser.parse_repl_input(input);
assert!(
result.is_err(),
"Expected error for positional after named, input: '{input}'"
);
let err = result.unwrap_err();
match err.kind
{
ErrorKind ::Syntax(s) => assert!(s.contains("Positional argument after named argument"), "Msg: {s}"), _ => panic!("Expected Syntax error, but got: {:?}", err.kind),
}
assert_eq!(err.location, Some(SourceLocation ::StrSpan { start: 15, end: 19 }));
}
#[ test ]
fn unexpected_help_operator_middle()
{
let parser = Parser ::new(UnilangParserOptions ::default());
let input = "cmd ? arg1";
let result = parser.parse_repl_input(input);
assert!(result.is_err(), "Expected error for '?' in middle, input: '{input}'");
let err = result.unwrap_err();
assert_eq!(
err.kind,
ErrorKind ::Syntax("Help operator '?' must be the last token".to_string()),
"ErrorKind mismatch: {:?}",
err.kind
);
assert_eq!(err.location, Some(SourceLocation ::StrSpan { start: 4, end: 5 })); }
#[ test ]
fn unexpected_token_in_args()
{
let parser = Parser ::new(UnilangParserOptions ::default());
let input = "cmd arg1 ! badchar";
let result = parser.parse_repl_input(input);
assert!(
result.is_err(),
"Expected error for unexpected token '!', input: '{}', got: {:?}",
input,
result.ok()
);
if result.is_ok()
{
return;
}
let err = result.unwrap_err();
assert_eq!(
err.kind,
ErrorKind ::Syntax("Unexpected token '!' in arguments".to_string()),
"ErrorKind mismatch: {:?}",
err.kind
);
assert_eq!(err.location, Some(SourceLocation ::StrSpan { start: 9, end: 10 }));
}