Skip to content

Commit 5e9b821

Browse files
LoukasPapsylvestre
authored andcommitted
Set max addresses to 2 for a, i and = and make n_addr conditional, based on POSIX flag
- Update `get_cmd_spec()` and `get_verified_cmd_spec()` to accept a boolean POSIX param - Add unit and high level tests - Update README.md
1 parent 27da027 commit 5e9b821

7 files changed

Lines changed: 128 additions & 20 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ cargo test
8686
* The `a`, `c`, and `i` commands do not require an initial backslash,
8787
allow text to appear on the same line, and support escape sequences
8888
in the specified text.
89+
* The `a`, `i`, `=`, `l`, `q` and `r` commands support address range as an extension to POSIX.
8990
* The substitution command replacement group `\0` is a synonym for &.
9091
* A `Q` command (optionally followed by an exit code) quits immediately.
9192
* The `q` command can be optionally followed by an exit code.

src/sed/compiler.rs

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -285,14 +285,14 @@ fn compile_sequence(
285285
let mut cmd = Rc::new(RefCell::new(Command::at_position(lines, line)));
286286
let n_addr = compile_address_range(lines, line, &mut cmd, context)?;
287287
line.eat_spaces();
288-
let mut cmd_spec = get_verified_cmd_spec(lines, line, n_addr)?;
288+
let mut cmd_spec = get_verified_cmd_spec(lines, line, n_addr, context.posix)?;
289289

290290
// Compile the command according to its specification.
291291
let mut cmd_mut = cmd.borrow_mut();
292292
cmd_mut.code = line.current();
293293
match (cmd_spec.handler)(lines, line, &mut cmd_mut, context)? {
294294
CommandHandling::GetNext => {
295-
cmd_spec = get_verified_cmd_spec(lines, line, n_addr)?;
295+
cmd_spec = get_verified_cmd_spec(lines, line, n_addr, context.posix)?;
296296
cmd_mut.code = line.current();
297297
(cmd_spec.handler)(lines, line, &mut cmd_mut, context)?;
298298
}
@@ -1119,7 +1119,6 @@ fn compile_text_command(
11191119
context: &mut ProcessingContext,
11201120
) -> UResult<CommandHandling> {
11211121
line.advance(); // Skip the command character.
1122-
11231122
line.eat_spaces(); // Skip any leading whitespace.
11241123
if context.posix {
11251124
compile_text_command_posix(lines, line, cmd, context)
@@ -1251,13 +1250,14 @@ fn get_verified_cmd_spec(
12511250
lines: &ScriptLineProvider,
12521251
line: &ScriptCharProvider,
12531252
n_addr: usize,
1253+
posix: bool,
12541254
) -> UResult<CommandSpec> {
12551255
if line.eol() {
12561256
return compilation_error(lines, line, "command expected");
12571257
}
12581258

12591259
let ch = line.current();
1260-
let cmd_spec = get_cmd_spec(lines, line, ch)?;
1260+
let cmd_spec = get_cmd_spec(lines, line, ch, posix)?;
12611261

12621262
if n_addr > cmd_spec.n_addr {
12631263
return compilation_error(
@@ -1278,14 +1278,15 @@ fn get_cmd_spec(
12781278
lines: &ScriptLineProvider,
12791279
line: &ScriptCharProvider,
12801280
cmd_code: char,
1281+
posix: bool,
12811282
) -> UResult<CommandSpec> {
12821283
match cmd_code {
12831284
'!' => Ok(CommandSpec {
12841285
n_addr: 2,
12851286
handler: compile_negation_command,
12861287
}),
12871288
'=' => Ok(CommandSpec {
1288-
n_addr: 1,
1289+
n_addr: if posix { 1 } else { 2 },
12891290
handler: compile_empty_command,
12901291
}),
12911292
':' => Ok(CommandSpec {
@@ -1301,7 +1302,7 @@ fn get_cmd_spec(
13011302
handler: compile_end_group_command,
13021303
}),
13031304
'a' | 'i' => Ok(CommandSpec {
1304-
n_addr: 1,
1305+
n_addr: if posix { 1 } else { 2 },
13051306
handler: compile_text_command,
13061307
}),
13071308
'b' | 't' => Ok(CommandSpec {
@@ -1320,13 +1321,17 @@ fn get_cmd_spec(
13201321
n_addr: 2,
13211322
handler: compile_number_command,
13221323
}),
1324+
'q' => Ok(CommandSpec {
1325+
n_addr: if posix { 1 } else { 2 },
1326+
handler: compile_number_command,
1327+
}),
13231328
// Q is a GNU extension
1324-
'q' | 'Q' => Ok(CommandSpec {
1329+
'Q' => Ok(CommandSpec {
13251330
n_addr: 1,
13261331
handler: compile_number_command,
13271332
}),
13281333
'r' => Ok(CommandSpec {
1329-
n_addr: 1,
1334+
n_addr: if posix { 1 } else { 2 },
13301335
handler: compile_read_file_command,
13311336
}),
13321337
's' => Ok(CommandSpec {
@@ -1378,35 +1383,35 @@ mod tests {
13781383
#[test]
13791384
fn test_lookup_empty_command() {
13801385
let (lines, line) = make_providers("123abc");
1381-
let cmd = get_cmd_spec(&lines, &line, 'd').unwrap();
1386+
let cmd = get_cmd_spec(&lines, &line, 'd', false).unwrap();
13821387
assert_eq!(cmd.n_addr, 2);
13831388
}
13841389

13851390
#[test]
13861391
fn test_lookup_text_command() {
13871392
let (lines, line) = make_providers("123abc");
1388-
let cmd = get_cmd_spec(&lines, &line, 'a').unwrap();
1389-
assert_eq!(cmd.n_addr, 1);
1393+
let cmd = get_cmd_spec(&lines, &line, 'a', false).unwrap();
1394+
assert_eq!(cmd.n_addr, 2);
13901395
}
13911396

13921397
#[test]
13931398
fn test_lookup_nonselect_command() {
13941399
let (lines, line) = make_providers("123abc");
1395-
let cmd = get_cmd_spec(&lines, &line, '!').unwrap();
1400+
let cmd = get_cmd_spec(&lines, &line, '!', false).unwrap();
13961401
assert_eq!(cmd.n_addr, 2);
13971402
}
13981403

13991404
#[test]
14001405
fn test_lookup_endgroup_command() {
14011406
let (lines, line) = make_providers("123abc");
1402-
let cmd = get_cmd_spec(&lines, &line, '}').unwrap();
1407+
let cmd = get_cmd_spec(&lines, &line, '}', false).unwrap();
14031408
assert_eq!(cmd.n_addr, 0);
14041409
}
14051410

14061411
#[test]
14071412
fn test_lookup_invalid_command() {
14081413
let (lines, line) = make_providers("123abc");
1409-
let result = get_cmd_spec(&lines, &line, 'Z');
1414+
let result = get_cmd_spec(&lines, &line, 'Z', false);
14101415
assert!(result.is_err());
14111416
}
14121417

@@ -1458,7 +1463,7 @@ mod tests {
14581463
fn test_missing_command_character() {
14591464
let lines = ScriptLineProvider::with_active_state("test.sed", 1);
14601465
let line = char_provider_from("");
1461-
let result = get_verified_cmd_spec(&lines, &line, 0);
1466+
let result = get_verified_cmd_spec(&lines, &line, 0, ctx().posix);
14621467

14631468
assert!(result.is_err());
14641469
let msg = result.unwrap_err().to_string();
@@ -1469,7 +1474,7 @@ mod tests {
14691474
fn test_invalid_command_character() {
14701475
let lines = ScriptLineProvider::with_active_state("script.sed", 2);
14711476
let line = char_provider_from("@");
1472-
let result = get_verified_cmd_spec(&lines, &line, 0);
1477+
let result = get_verified_cmd_spec(&lines, &line, 0, ctx().posix);
14731478

14741479
assert!(result.is_err());
14751480
let msg = result.unwrap_err().to_string();
@@ -1480,7 +1485,7 @@ mod tests {
14801485
fn test_too_many_addresses() {
14811486
let lines = ScriptLineProvider::with_active_state("input.sed", 3);
14821487
let line = char_provider_from("q"); // q takes one address
1483-
let result = get_verified_cmd_spec(&lines, &line, 2);
1488+
let result = get_verified_cmd_spec(&lines, &line, 2, true);
14841489

14851490
assert!(result.is_err());
14861491
let msg = result.unwrap_err().to_string();
@@ -1493,11 +1498,22 @@ mod tests {
14931498
fn test_valid_command_spec() {
14941499
let lines = ScriptLineProvider::with_active_state("input.sed", 4);
14951500
let line = char_provider_from("a"); // valid command
1496-
let result = get_verified_cmd_spec(&lines, &line, 1);
1497-
1501+
let result = get_verified_cmd_spec(&lines, &line, 2, ctx().posix);
14981502
assert!(result.is_ok());
14991503
let spec = result.unwrap();
1500-
assert_eq!(spec.n_addr, 1);
1504+
assert_eq!(spec.n_addr, 2);
1505+
}
1506+
1507+
#[test]
1508+
fn test_invalid_address_range_posix() {
1509+
let lines = ScriptLineProvider::with_active_state("input.sed", 1);
1510+
let line = char_provider_from("i"); // valid command
1511+
let result = get_verified_cmd_spec(&lines, &line, 2, true);
1512+
assert!(result.is_err());
1513+
let msg = result.unwrap_err().to_string();
1514+
assert!(
1515+
msg.contains("input.sed:1:1: error: command i expects up to 1 address(es), found 2")
1516+
);
15011517
}
15021518

15031519
// parse_number

tests/by-util/test_sed.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,30 @@ tb"#,
594594

595595
// Check both POSIX and GNU parsing routines.
596596

597+
check_output!(
598+
text_simple_insert,
599+
[
600+
"-e",
601+
r#"
602+
2,4i\
603+
extra
604+
"#,
605+
LINES1
606+
]
607+
);
608+
609+
check_output!(
610+
text_simple_append,
611+
[
612+
"-e",
613+
r#"
614+
2,4a\
615+
extra
616+
"#,
617+
LINES1
618+
]
619+
);
620+
597621
check_output_posix!(
598622
text_insert_quit,
599623
[
@@ -785,6 +809,8 @@ fn write_two_files() -> std::io::Result<()> {
785809
// =, l commands
786810
check_output!(number_continuous, ["/l2_/=", LINES1, LINES2]);
787811
check_output!(number_separate, ["-s", "/l._8/=", LINES1, LINES2]);
812+
check_output!(number_range, ["-e", "10,12=", LINES1]);
813+
check_output!(number_range_out_of_bounds, ["-e", "47,60=", LINES1]);
788814

789815
check_output!(list_ascii, ["-n", "l 60", "input/ascii"]);
790816
check_output!(list_empty, ["-n", "l 60", "input/empty"]);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
l1_1
2+
l1_2
3+
l1_3
4+
l1_4
5+
l1_5
6+
l1_6
7+
l1_7
8+
l1_8
9+
l1_9
10+
10
11+
l1_10
12+
11
13+
l1_11
14+
12
15+
l1_12
16+
l1_13
17+
l1_14
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
l1_1
2+
l1_2
3+
l1_3
4+
l1_4
5+
l1_5
6+
l1_6
7+
l1_7
8+
l1_8
9+
l1_9
10+
l1_10
11+
l1_11
12+
l1_12
13+
l1_13
14+
l1_14
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
l1_1
2+
l1_2
3+
extra
4+
l1_3
5+
extra
6+
l1_4
7+
extra
8+
l1_5
9+
l1_6
10+
l1_7
11+
l1_8
12+
l1_9
13+
l1_10
14+
l1_11
15+
l1_12
16+
l1_13
17+
l1_14
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
l1_1
2+
extra
3+
l1_2
4+
extra
5+
l1_3
6+
extra
7+
l1_4
8+
l1_5
9+
l1_6
10+
l1_7
11+
l1_8
12+
l1_9
13+
l1_10
14+
l1_11
15+
l1_12
16+
l1_13
17+
l1_14

0 commit comments

Comments
 (0)