Skip to content

Commit 0ba2cd5

Browse files
committed
added support for comma-separated point annotations for other plugins
1 parent 10a25b1 commit 0ba2cd5

File tree

8 files changed

+88
-67
lines changed

8 files changed

+88
-67
lines changed

plugins/csharp/src/plugin.rs

+16-11
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use tmc_langs_framework::{
1313
CommandError, ExerciseDesc, Language, LanguagePlugin, RunResult, RunStatus,
1414
StyleValidationResult, StyleValidationStrategy, TestDesc, TestResult, TmcCommand, TmcError,
1515
};
16-
use tmc_langs_util::{file_util, FileError};
16+
use tmc_langs_util::{file_util, parse_util, FileError};
1717
use walkdir::WalkDir;
1818
use zip::ZipArchive;
1919

@@ -344,26 +344,28 @@ impl LanguagePlugin for CSharpPlugin {
344344
combinator::map(
345345
sequence::delimited(
346346
sequence::tuple((
347-
bytes::complete::tag("@"),
347+
character::complete::char('@'),
348348
character::complete::multispace0,
349349
bytes::complete::tag_no_case("points"),
350350
character::complete::multispace0,
351351
character::complete::char('('),
352352
character::complete::multispace0,
353353
)),
354-
sequence::delimited(
355-
character::complete::char('"'),
356-
bytes::complete::is_not("\""),
357-
character::complete::char('"'),
358-
),
354+
parse_util::comma_separated_strings,
359355
sequence::tuple((
360356
character::complete::multispace0,
361357
character::complete::char(')'),
362358
)),
363359
),
364-
str::trim,
360+
// splits each point by whitespace
361+
|points| {
362+
points
363+
.into_iter()
364+
.map(|p| p.split_whitespace())
365+
.flatten()
366+
.collect()
367+
},
365368
)(i)
366-
.map(|(a, b)| (a, vec![b]))
367369
}
368370
}
369371

@@ -680,10 +682,13 @@ mod test {
680682
assert!(res.is_err());
681683

682684
let res = CSharpPlugin::points_parser("@Points(\"1\")").unwrap();
683-
assert_eq!(res.1[0], "1");
685+
assert_eq!(res.1, &["1"]);
684686

685687
let res = CSharpPlugin::points_parser("@ pOiNtS ( \" 1 \" ) ").unwrap();
686-
assert_eq!(res.1[0], "1");
688+
assert_eq!(res.1, &["1"]);
689+
690+
let res = CSharpPlugin::points_parser("@Points(\"1\", \"2\" , \"3\")").unwrap();
691+
assert_eq!(res.1, &["1", "2", "3"]);
687692
}
688693

689694
#[test]

plugins/java/src/ant_plugin.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ impl LanguagePlugin for AntPlugin {
117117
}
118118

119119
fn points_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
120-
Self::java_points_parser(i).map(|(a, b)| (a, vec![b]))
120+
Self::java_points_parser(i)
121121
}
122122

123123
fn get_default_student_file_paths() -> Vec<PathBuf> {

plugins/java/src/java_plugin.rs

+21-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use tmc_langs_framework::{
1212
ExerciseDesc, Language, LanguagePlugin, RunResult, RunStatus, StyleValidationResult, TestDesc,
1313
TestResult, TmcCommand,
1414
};
15-
use tmc_langs_util::file_util;
15+
use tmc_langs_util::{file_util, parse_util};
1616
use walkdir::WalkDir;
1717

1818
pub(crate) trait JavaPlugin: LanguagePlugin {
@@ -253,28 +253,31 @@ pub(crate) trait JavaPlugin: LanguagePlugin {
253253
}
254254

255255
/// Parses @Points("1.1") point annotations.
256-
fn java_points_parser(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
256+
fn java_points_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
257257
combinator::map(
258258
sequence::delimited(
259259
sequence::tuple((
260-
bytes::complete::tag("@"),
260+
character::complete::char('@'),
261261
character::complete::multispace0,
262262
bytes::complete::tag_no_case("points"),
263263
character::complete::multispace0,
264264
character::complete::char('('),
265265
character::complete::multispace0,
266266
)),
267-
sequence::delimited(
268-
character::complete::char('"'),
269-
bytes::complete::is_not("\""),
270-
character::complete::char('"'),
271-
),
267+
parse_util::comma_separated_strings,
272268
sequence::tuple((
273269
character::complete::multispace0,
274270
character::complete::char(')'),
275271
)),
276272
),
277-
str::trim,
273+
// splits each point by whitespace
274+
|points| {
275+
points
276+
.into_iter()
277+
.map(|p| p.split_whitespace())
278+
.flatten()
279+
.collect()
280+
},
278281
)(i)
279282
}
280283
}
@@ -361,7 +364,7 @@ mod test {
361364
}
362365

363366
fn points_parser(i: &str) -> IResult<&str, Vec<&str>, nom::error::VerboseError<&str>> {
364-
Self::java_points_parser(i).map(|(a, b)| (a, vec![b]))
367+
Self::java_points_parser(i)
365368
}
366369
}
367370

@@ -569,13 +572,19 @@ openjdk version "1.8.0_252"S
569572

570573
assert_eq!(
571574
Stub::java_points_parser(r#"@points("point")"#).unwrap().1,
572-
"point"
575+
&["point"]
573576
);
574577
assert_eq!(
575578
Stub::java_points_parser(r#"@ PoInTs ( " another point " ) "#)
576579
.unwrap()
577580
.1,
578-
"another point"
581+
&["another", "point"]
582+
);
583+
assert_eq!(
584+
Stub::java_points_parser(r#"@points("point", "another point" , "asd")"#)
585+
.unwrap()
586+
.1,
587+
&["point", "another", "point", "asd"]
579588
);
580589
}
581590
}

plugins/java/src/maven_plugin.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ impl LanguagePlugin for MavenPlugin {
124124
}
125125

126126
fn points_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
127-
Self::java_points_parser(i).map(|(a, b)| (a, vec![b]))
127+
Self::java_points_parser(i)
128128
}
129129
}
130130

plugins/notests/src/plugin.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,9 @@ impl LanguagePlugin for NoTestsPlugin {
9595
vec![PathBuf::from("test")]
9696
}
9797

98-
fn points_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
99-
// does not match any characters
100-
nom::combinator::value("", nom::character::complete::one_of(""))(i)
101-
.map(|(a, b)| (a, vec![b]))
98+
fn points_parser(_: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
99+
// never parses anything
100+
Err(nom::Err::Error(VerboseError { errors: vec![] }))
102101
}
103102
}
104103

plugins/python3/src/plugin.rs

+24-22
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ use std::io::BufReader;
1313
use std::path::{Path, PathBuf};
1414
use std::time::Duration;
1515
use tmc_langs_framework::{
16-
nom::{branch, bytes, character, combinator, error::VerboseError, sequence, IResult},
16+
nom::{bytes, character, combinator, error::VerboseError, sequence, IResult},
1717
CommandError, ExerciseDesc, LanguagePlugin, Output, RunResult, RunStatus, TestDesc, TestResult,
1818
TmcCommand, TmcError, TmcProjectYml,
1919
};
2020
use tmc_langs_util::{
2121
file_util,
2222
notification_reporter::{self, Notification},
23+
parse_util,
2324
};
2425
use walkdir::WalkDir;
2526

@@ -382,33 +383,28 @@ impl LanguagePlugin for Python3Plugin {
382383
combinator::map(
383384
sequence::delimited(
384385
sequence::tuple((
385-
bytes::complete::tag("@"),
386+
character::complete::char('@'),
386387
character::complete::multispace0,
387388
bytes::complete::tag_no_case("points"),
388389
character::complete::multispace0,
389390
character::complete::char('('),
390391
character::complete::multispace0,
391392
)),
392-
branch::alt((
393-
sequence::delimited(
394-
character::complete::char('"'),
395-
bytes::complete::is_not("\""),
396-
character::complete::char('"'),
397-
),
398-
sequence::delimited(
399-
character::complete::char('\''),
400-
bytes::complete::is_not("'"),
401-
character::complete::char('\''),
402-
),
403-
)),
393+
parse_util::comma_separated_strings_either,
404394
sequence::tuple((
405395
character::complete::multispace0,
406396
character::complete::char(')'),
407397
)),
408398
),
409-
str::trim,
399+
// splits each point by whitespace
400+
|points| {
401+
points
402+
.into_iter()
403+
.map(|p| p.split_whitespace())
404+
.flatten()
405+
.collect()
406+
},
410407
)(i)
411-
.map(|(a, b)| (a, vec![b]))
412408
}
413409
}
414410

@@ -723,18 +719,24 @@ class TestErroring(unittest.TestCase):
723719
#[test]
724720
fn parses_points() {
725721
assert_eq!(
726-
Python3Plugin::points_parser("@points('p1')").unwrap().1[0],
727-
"p1"
722+
Python3Plugin::points_parser("@points('p1')").unwrap().1,
723+
&["p1"]
728724
);
729725
assert_eq!(
730726
Python3Plugin::points_parser("@ pOiNtS ( ' p2 ' ) ")
731727
.unwrap()
732-
.1[0],
733-
"p2"
728+
.1,
729+
&["p2"]
730+
);
731+
assert_eq!(
732+
Python3Plugin::points_parser(r#"@points("p3")"#).unwrap().1,
733+
&["p3"]
734734
);
735735
assert_eq!(
736-
Python3Plugin::points_parser(r#"@points("p3")"#).unwrap().1[0],
737-
"p3"
736+
Python3Plugin::points_parser(r#"@points("p3", 'p4', "p5", "p6 p7")"#)
737+
.unwrap()
738+
.1,
739+
&["p3", "p4", "p5", "p6", "p7"]
738740
);
739741
assert!(Python3Plugin::points_parser(r#"@points("p3')"#).is_err());
740742
}

plugins/r/src/plugin.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ impl LanguagePlugin for RPlugin {
154154
character::complete::char(','),
155155
character::complete::multispace0,
156156
)),
157-
c_parser,
157+
list_parser,
158158
);
159159
let points_for_all_tests_parser = sequence::preceded(
160160
sequence::tuple((
@@ -163,10 +163,10 @@ impl LanguagePlugin for RPlugin {
163163
character::complete::char('('),
164164
character::complete::multispace0,
165165
)),
166-
c_parser,
166+
list_parser,
167167
);
168168

169-
fn c_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
169+
fn list_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
170170
sequence::delimited(
171171
sequence::tuple((
172172
character::complete::char('c'),
@@ -522,13 +522,13 @@ etc
522522
"tests/testthat/testExercise.R",
523523
r#"
524524
something
525-
test("some test", c("r1", "r2", "r3"))
525+
test("some test", c("r1", "r2", "r3", "r4 r5"))
526526
etc
527527
"#,
528528
);
529529

530530
let points = RPlugin::get_available_points(temp.path()).unwrap();
531-
assert_eq!(points, &["r1", "r2", "r3"]);
531+
assert_eq!(points, &["r1", "r2", "r3", "r4", "r5"]);
532532
}
533533

534534
#[test]

tmc-langs-util/src/parse_util.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
//! Contains parse functions that may be convenient for implementing language plugins.
2-
use nom::{branch, bytes, character, error::VerboseError, multi, sequence, IResult};
2+
use nom::{branch, bytes, character, combinator, error::VerboseError, multi, sequence, IResult};
33

4-
/// Parses a string delimited by double quotes.
4+
/// Parses a string delimited by double quotes. Trims.
55
pub fn string(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
6-
sequence::delimited(
7-
character::complete::char('"'),
8-
bytes::complete::is_not("\""),
9-
character::complete::char('"'),
6+
combinator::map(
7+
sequence::delimited(
8+
character::complete::char('"'),
9+
bytes::complete::is_not("\""),
10+
character::complete::char('"'),
11+
),
12+
str::trim,
1013
)(i)
1114
}
1215

13-
/// Parses a string delimited by single quotes.
16+
/// Parses a string delimited by single quotes. Trims.
1417
pub fn string_single(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
15-
sequence::delimited(
16-
character::complete::char('\''),
17-
bytes::complete::is_not("'"),
18-
character::complete::char('\''),
18+
combinator::map(
19+
sequence::delimited(
20+
character::complete::char('\''),
21+
bytes::complete::is_not("'"),
22+
character::complete::char('\''),
23+
),
24+
str::trim,
1925
)(i)
2026
}
2127

0 commit comments

Comments
 (0)