Skip to content

Commit 1ecb35f

Browse files
committed
added support for comma separated points in R
1 parent 78b02d9 commit 1ecb35f

File tree

9 files changed

+111
-59
lines changed

9 files changed

+111
-59
lines changed

plugins/csharp/src/plugin.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ impl LanguagePlugin for CSharpPlugin {
340340
vec![PathBuf::from("test")]
341341
}
342342

343-
fn points_parser(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
343+
fn points_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
344344
combinator::map(
345345
sequence::delimited(
346346
sequence::tuple((
@@ -363,6 +363,7 @@ impl LanguagePlugin for CSharpPlugin {
363363
),
364364
str::trim,
365365
)(i)
366+
.map(|(a, b)| (a, vec![b]))
366367
}
367368
}
368369

@@ -679,14 +680,13 @@ mod test {
679680
assert!(res.is_err());
680681

681682
let res = CSharpPlugin::points_parser("@Points(\"1\")").unwrap();
682-
assert_eq!(res.1, "1");
683+
assert_eq!(res.1[0], "1");
683684

684685
let res = CSharpPlugin::points_parser("@ pOiNtS ( \" 1 \" ) ").unwrap();
685-
assert_eq!(res.1, "1");
686+
assert_eq!(res.1[0], "1");
686687
}
687688

688689
#[test]
689-
// #[ignore = "requires newer version of C# runner that always includes all points in the tests"]
690690
fn doesnt_give_points_unless_all_relevant_exercises_pass() {
691691
init();
692692

plugins/java/src/ant_plugin.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ impl LanguagePlugin for AntPlugin {
116116
Ok(())
117117
}
118118

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

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

plugins/java/src/java_plugin.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,8 +360,8 @@ mod test {
360360
unimplemented!()
361361
}
362362

363-
fn points_parser(i: &str) -> IResult<&str, &str, nom::error::VerboseError<&str>> {
364-
Self::java_points_parser(i)
363+
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]))
365365
}
366366
}
367367

plugins/java/src/maven_plugin.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ impl LanguagePlugin for MavenPlugin {
123123
vec![PathBuf::from("src/test")]
124124
}
125125

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

plugins/make/src/plugin.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,8 @@ impl LanguagePlugin for MakePlugin {
337337
vec![PathBuf::from("test")]
338338
}
339339

340-
fn points_parser(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
341-
fn tmc_register_test_parser(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
340+
fn points_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
341+
fn tmc_register_test_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
342342
sequence::delimited(
343343
sequence::tuple((
344344
bytes::complete::tag("tmc_register_test"),
@@ -354,6 +354,7 @@ impl LanguagePlugin for MakePlugin {
354354
character::complete::char(')'),
355355
)),
356356
)(i)
357+
.map(|(a, b)| (a, vec![b]))
357358
}
358359

359360
// todo: currently cannot handle function calls with multiple parameters, probably not a problem
@@ -613,7 +614,7 @@ test [invalid] point6
613614
"tmc_register_test(s, test_insertion_empty_list, \"dlink_insert\");",
614615
)
615616
.unwrap()
616-
.1,
617+
.1[0],
617618
"dlink_insert"
618619
);
619620
}

plugins/notests/src/plugin.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,10 @@ impl LanguagePlugin for NoTestsPlugin {
9595
vec![PathBuf::from("test")]
9696
}
9797

98-
fn points_parser(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
98+
fn points_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
9999
// does not match any characters
100100
nom::combinator::value("", nom::character::complete::one_of(""))(i)
101+
.map(|(a, b)| (a, vec![b]))
101102
}
102103
}
103104

plugins/python3/src/plugin.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ impl LanguagePlugin for Python3Plugin {
378378
vec![PathBuf::from("test"), PathBuf::from("tmc")]
379379
}
380380

381-
fn points_parser(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
381+
fn points_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
382382
combinator::map(
383383
sequence::delimited(
384384
sequence::tuple((
@@ -408,6 +408,7 @@ impl LanguagePlugin for Python3Plugin {
408408
),
409409
str::trim,
410410
)(i)
411+
.map(|(a, b)| (a, vec![b]))
411412
}
412413
}
413414

@@ -722,17 +723,17 @@ class TestErroring(unittest.TestCase):
722723
#[test]
723724
fn parses_points() {
724725
assert_eq!(
725-
Python3Plugin::points_parser("@points('p1')").unwrap().1,
726+
Python3Plugin::points_parser("@points('p1')").unwrap().1[0],
726727
"p1"
727728
);
728729
assert_eq!(
729730
Python3Plugin::points_parser("@ pOiNtS ( ' p2 ' ) ")
730731
.unwrap()
731-
.1,
732+
.1[0],
732733
"p2"
733734
);
734735
assert_eq!(
735-
Python3Plugin::points_parser(r#"@points("p3")"#).unwrap().1,
736+
Python3Plugin::points_parser(r#"@points("p3")"#).unwrap().1[0],
736737
"p3"
737738
);
738739
assert!(Python3Plugin::points_parser(r#"@points("p3')"#).is_err());

plugins/r/src/plugin.rs

Lines changed: 75 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::io::{Read, Seek};
99
use std::path::{Path, PathBuf};
1010
use std::time::Duration;
1111
use tmc_langs_framework::{
12-
nom::{branch, bytes, character, combinator, error::VerboseError, sequence, IResult},
12+
nom::{branch, bytes, character, combinator, error::VerboseError, multi, sequence, IResult},
1313
LanguagePlugin, TmcCommand, TmcError, {ExerciseDesc, RunResult, TestDesc},
1414
};
1515
use tmc_langs_util::file_util;
@@ -142,46 +142,73 @@ impl LanguagePlugin for RPlugin {
142142
vec![PathBuf::from("tests")]
143143
}
144144

145-
fn points_parser(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
146-
let test_parser = sequence::delimited(
145+
fn points_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
146+
let mut test_parser = sequence::preceded(
147147
sequence::tuple((
148148
bytes::complete::tag("test"),
149149
character::complete::multispace0,
150150
character::complete::char('('),
151-
bytes::complete::take_until(","),
152-
bytes::complete::take_until("\""),
153-
)),
154-
sequence::delimited(
155-
character::complete::char('"'),
156-
bytes::complete::is_not("\""),
157-
character::complete::char('"'),
158-
),
159-
sequence::tuple((
160151
character::complete::multispace0,
161-
character::complete::char(')'),
152+
arg_parser,
162153
)),
154+
c_parser,
163155
);
164-
let points_for_all_tests_parser = sequence::delimited(
156+
let points_for_all_tests_parser = sequence::preceded(
165157
sequence::tuple((
166158
bytes::complete::tag("points_for_all_tests"),
167159
character::complete::multispace0,
168160
character::complete::char('('),
169-
bytes::complete::take_until("\""),
170-
)),
171-
sequence::delimited(
172-
character::complete::char('"'),
173-
bytes::complete::is_not("\""),
174-
character::complete::char('"'),
175-
),
176-
sequence::tuple((
177161
character::complete::multispace0,
178-
character::complete::char(')'),
179162
)),
163+
c_parser,
180164
);
181-
combinator::map(
182-
branch::alt((test_parser, points_for_all_tests_parser)),
183-
str::trim,
184-
)(i)
165+
166+
// todo: currently cannot handle function calls with multiple parameters, probably not a problem
167+
fn arg_parser(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
168+
combinator::value(
169+
"",
170+
sequence::tuple((
171+
bytes::complete::take_till(|c: char| c == ','),
172+
character::complete::char(','),
173+
character::complete::multispace0,
174+
)),
175+
)(i)
176+
}
177+
178+
fn c_parser(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
179+
combinator::map(
180+
sequence::tuple((
181+
character::complete::char('c'),
182+
character::complete::multispace0,
183+
character::complete::char('('),
184+
character::complete::multispace0,
185+
multi::separated_list1(
186+
sequence::tuple((
187+
character::complete::multispace0,
188+
character::complete::char(','),
189+
character::complete::multispace0,
190+
)),
191+
string_parser,
192+
),
193+
character::complete::multispace0,
194+
character::complete::char(')'),
195+
)),
196+
|t| t.4,
197+
)(i)
198+
}
199+
200+
fn string_parser(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
201+
combinator::map(
202+
sequence::tuple((
203+
character::complete::char('"'),
204+
bytes::complete::is_not("\""),
205+
character::complete::char('"'),
206+
)),
207+
|r| str::trim(r.1),
208+
)(i)
209+
}
210+
211+
branch::alt((test_parser, points_for_all_tests_parser))(i)
185212
}
186213
}
187214

@@ -466,14 +493,14 @@ test("sample", c("r1.1"), {
466493
expect_true(areEqual(res, res_correct))
467494
})
468495
"#;
469-
assert_eq!(RPlugin::points_parser(target).unwrap().1, "W1A.1.2");
496+
assert_eq!(RPlugin::points_parser(target).unwrap().1[0], "W1A.1.2");
470497

471498
let target = r#"test ( "1d and 1e are solved correctly", c ( " W1A.1.2 " ) , {
472499
expect_equivalent(z, z_correct, tolerance=1e-5)
473500
expect_true(areEqual(res, res_correct))
474501
})
475502
"#;
476-
assert_eq!(RPlugin::points_parser(target).unwrap().1, "W1A.1.2");
503+
assert_eq!(RPlugin::points_parser(target).unwrap().1[0], "W1A.1.2");
477504
}
478505

479506
#[test]
@@ -510,4 +537,23 @@ etc
510537
let points = RPlugin::get_available_points(temp.path()).unwrap();
511538
assert_eq!(points, &["r1"]);
512539
}
540+
541+
#[test]
542+
fn parses_multiple_points() {
543+
init();
544+
545+
let temp = tempfile::tempdir().unwrap();
546+
file_to(
547+
&temp,
548+
"tests/testthat/testExercise.R",
549+
r#"
550+
something
551+
test("some test", c("r1", "r2", "r3"))
552+
etc
553+
"#,
554+
);
555+
556+
let points = RPlugin::get_available_points(temp.path()).unwrap();
557+
assert_eq!(points, &["r1", "r2", "r3"]);
558+
}
513559
}

tmc-langs-framework/src/plugin.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ pub trait LanguagePlugin {
407407

408408
fn get_default_exercise_file_paths() -> Vec<PathBuf>;
409409

410-
/// Parses exercise files using Self::LINE_COMMENT and Self::BLOCK_COMMENt to filter out comments and Self::points_parser to parse points from the actual code.
410+
/// Parses exercise files using Self::LINE_COMMENT and Self::BLOCK_COMMENT to filter out comments and Self::points_parser to parse points from the actual code.
411411
fn get_available_points(exercise_path: &Path) -> Result<Vec<String>, TmcError> {
412412
let config = TmcProjectYml::load_or_default(exercise_path)?;
413413
let config = Self::get_exercise_packaging_configuration(config)?;
@@ -458,8 +458,9 @@ pub trait LanguagePlugin {
458458
};
459459

460460
// reads a points annotation
461-
let points_parser =
462-
combinator::map(Self::points_parser, |p| Parse::Points(p.to_string()));
461+
let points_parser = combinator::map(Self::points_parser, |p| {
462+
Parse::Points(p.into_iter().map(|s| s.to_string()).collect())
463+
});
463464

464465
// try to apply the interesting parsers, else read a character with the etc parser. repeat until the input ends
465466
let mut parser = multi::many0(branch::alt((
@@ -474,10 +475,12 @@ pub trait LanguagePlugin {
474475
Ok((_, parsed)) => {
475476
for parse in parsed {
476477
if let Parse::Points(parsed) = parse {
477-
// a single points annotation can contain multiple whitespace separated points
478-
let split_points =
479-
parsed.split_whitespace().map(str::to_string);
480-
points.extend(split_points);
478+
for point in parsed {
479+
// a single points annotation can contain multiple whitespace separated points
480+
let split_points =
481+
point.split_whitespace().map(str::to_string);
482+
points.extend(split_points);
483+
}
481484
}
482485
}
483486
}
@@ -501,17 +504,17 @@ pub trait LanguagePlugin {
501504
Ok(points)
502505
}
503506

504-
/// A nom parser that recognizes a points annotation and returns the inner points value.
507+
/// A nom parser that recognizes a points annotation and returns the inner points value(s).
505508
///
506509
/// For example implementations, see the existing language plugins.
507-
fn points_parser(i: &str) -> IResult<&str, &str, nom::error::VerboseError<&str>>;
510+
fn points_parser(i: &str) -> IResult<&str, Vec<&str>, nom::error::VerboseError<&str>>;
508511
}
509512

510513
#[derive(Debug, Clone)]
511514
enum Parse {
512515
LineComment,
513516
BlockComment,
514-
Points(String),
517+
Points(Vec<String>),
515518
Other,
516519
}
517520

@@ -634,7 +637,7 @@ mod test {
634637
Ok(())
635638
}
636639

637-
fn points_parser(i: &str) -> IResult<&str, &str, nom::error::VerboseError<&str>> {
640+
fn points_parser(i: &str) -> IResult<&str, Vec<&str>, nom::error::VerboseError<&str>> {
638641
combinator::map(
639642
sequence::delimited(
640643
sequence::tuple((
@@ -662,7 +665,7 @@ mod test {
662665
character::complete::char(')'),
663666
)),
664667
),
665-
str::trim,
668+
|s: &str| vec![s.trim()],
666669
)(i)
667670
}
668671

0 commit comments

Comments
 (0)