Skip to content

Commit 4e68283

Browse files
authored
Merge pull request #603 from ratmice/cttest_extra_files
Add initial support for extra_files to cttests
2 parents e534b83 + 0028126 commit 4e68283

File tree

5 files changed

+172
-54
lines changed

5 files changed

+172
-54
lines changed

lrlex/src/lib/ctbuilder.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ where
226226
LexerTypesT::StorageT: Debug + Eq + Hash + ToTokens,
227227
usize: num_traits::AsPrimitive<LexerTypesT::StorageT>,
228228
{
229-
lrpar_config: Option<Box<dyn Fn(CTParserBuilder<LexerTypesT>) -> CTParserBuilder<LexerTypesT>>>,
229+
lrpar_config:
230+
Option<Box<dyn Fn(CTParserBuilder<LexerTypesT>) -> CTParserBuilder<LexerTypesT> + 'a>>,
230231
lexer_path: Option<PathBuf>,
231232
output_path: Option<PathBuf>,
232233
lexerkind: Option<LexerKind>,
@@ -313,9 +314,9 @@ where
313314
/// .lexer_in_src_dir("calc.l")?
314315
/// .build()?;
315316
/// ```
316-
pub fn lrpar_config<F>(mut self, config_func: F) -> Self
317+
pub fn lrpar_config<F: 'a>(mut self, config_func: F) -> Self
317318
where
318-
F: 'static + Fn(CTParserBuilder<LexerTypesT>) -> CTParserBuilder<LexerTypesT>,
319+
F: Fn(CTParserBuilder<LexerTypesT>) -> CTParserBuilder<LexerTypesT>,
319320
{
320321
self.lrpar_config = Some(Box::new(config_func));
321322
self

lrpar/cttests/src/calc_input.test

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Test with calculator input from %grmtools{test_files}
2+
grammar: |
3+
%grmtools {
4+
yacckind: Original(YaccOriginalActionKind::UserAction),
5+
recoverer: RecoveryKind::None,
6+
test_files: "*.calc_input"
7+
}
8+
%start Expr
9+
%actiontype Result<u64, ()>
10+
%avoid_insert 'INT'
11+
%%
12+
Expr: Expr '+' Term { Ok($1? + $3?) }
13+
| Term { $1 }
14+
;
15+
16+
Term: Term '*' Factor { Ok($1? * $3?) }
17+
| Factor { $1 }
18+
;
19+
20+
Factor: '(' Expr ')' { $2 }
21+
| 'INT' {
22+
let l = $1.map_err(|_| ())?;
23+
match $lexer.span_str(l.span()).parse::<u64>() {
24+
Ok(v) => Ok(v),
25+
Err(_) => {
26+
let ((_, col), _) = $lexer.line_col(l.span());
27+
eprintln!("Error at column {}: '{}' cannot be represented as a u64",
28+
col,
29+
$lexer.span_str(l.span()));
30+
Err(())
31+
}
32+
}
33+
}
34+
;
35+
36+
lexer: |
37+
%%
38+
[0-9]+ "INT"
39+
\+ "+"
40+
\* "*"
41+
\( "("
42+
\) ")"
43+
[\t\n ]+ ;
44+
extra_files:
45+
input1.calc_input: |
46+
1 + 2 * 3
47+
input2.calc_input: |
48+
(1 + 2) * 3
49+

lrpar/cttests/src/cgen_helper.rs

Lines changed: 59 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use cfgrammar::yacc::{YaccKind, YaccOriginalActionKind};
2-
use lrlex::{CTLexerBuilder, DefaultLexerTypes};
3-
use lrpar::{CTParserBuilder, RecoveryKind};
2+
use lrlex::CTLexerBuilder;
3+
use lrpar::RecoveryKind;
44
use std::{
55
env, fs,
66
path::{Path, PathBuf},
@@ -10,11 +10,10 @@ use yaml_rust2::YamlLoader;
1010
#[allow(dead_code)]
1111
pub(crate) fn run_test_path<P: AsRef<Path>>(path: P) -> Result<(), Box<dyn std::error::Error>> {
1212
let out_dir = env::var("OUT_DIR").unwrap();
13-
let path = path.as_ref();
14-
if path.is_file() {
15-
println!("cargo::rerun-if-changed={}", path.display());
13+
if path.as_ref().is_file() {
14+
println!("cargo::rerun-if-changed={}", path.as_ref().display());
1615
// Parse test file
17-
let s = fs::read_to_string(path).unwrap();
16+
let s = fs::read_to_string(path.as_ref()).unwrap();
1817
let docs = YamlLoader::load_from_str(&s).unwrap();
1918
let grm = &docs[0]["grammar"].as_str().unwrap();
2019
let lex = &docs[0]["lexer"].as_str().unwrap();
@@ -37,26 +36,6 @@ pub(crate) fn run_test_path<P: AsRef<Path>>(path: P) -> Result<(), Box<dyn std::
3736
Some("RecoveryKind::None") => Some(RecoveryKind::None),
3837
_ => None,
3938
};
40-
let (negative_yacc_flags, positive_yacc_flags) = &docs[0]["yacc_flags"]
41-
.as_vec()
42-
.map(|flags_vec| {
43-
flags_vec
44-
.iter()
45-
.partition(|flag| flag.as_str().unwrap().starts_with('!'))
46-
})
47-
.unwrap_or_else(|| (Vec::new(), Vec::new()));
48-
let positive_yacc_flags = positive_yacc_flags
49-
.iter()
50-
.map(|flag| flag.as_str().unwrap())
51-
.collect::<Vec<_>>();
52-
let negative_yacc_flags = negative_yacc_flags
53-
.iter()
54-
.map(|flag| {
55-
let flag = flag.as_str().unwrap();
56-
flag.strip_prefix('!').unwrap()
57-
})
58-
.collect::<Vec<_>>();
59-
let yacc_flags = (&positive_yacc_flags, &negative_yacc_flags);
6039
let (negative_lex_flags, positive_lex_flags) = &docs[0]["lex_flags"]
6140
.as_vec()
6241
.map(|flags_vec| {
@@ -82,44 +61,73 @@ pub(crate) fn run_test_path<P: AsRef<Path>>(path: P) -> Result<(), Box<dyn std::
8261
// filename conventions. If those change, this code will also have to change.
8362

8463
// Create grammar files
85-
let base = path.file_stem().unwrap().to_str().unwrap();
64+
let base = path.as_ref().file_stem().unwrap().to_str().unwrap();
8665
let mut pg = PathBuf::from(&out_dir);
8766
pg.push(format!("{}.test.y", base));
8867
fs::write(&pg, grm).unwrap();
8968
let mut pl = PathBuf::from(&out_dir);
9069
pl.push(format!("{}.test.l", base));
9170
fs::write(&pl, lex).unwrap();
9271

93-
// Build parser and lexer
94-
let mut outp = PathBuf::from(&out_dir);
95-
outp.push(format!("{}.y.rs", base));
96-
outp.set_extension("rs");
97-
let mut cp_build = CTParserBuilder::<DefaultLexerTypes<u32>>::new();
98-
if let Some(yacckind) = yacckind {
99-
cp_build = cp_build.yacckind(yacckind);
100-
}
101-
if let Some(recoverer) = recoverer {
102-
cp_build = cp_build.recoverer(recoverer)
103-
}
104-
cp_build = cp_build
105-
.grammar_path(pg.to_str().unwrap())
106-
.output_path(&outp);
107-
if let Some(flag) = check_flag(yacc_flags, "error_on_conflicts") {
108-
cp_build = cp_build.error_on_conflicts(flag)
109-
}
110-
if let Some(flag) = check_flag(yacc_flags, "warnings_are_errors") {
111-
cp_build = cp_build.warnings_are_errors(flag)
72+
if let Some(extra_files) = docs[0]["extra_files"].as_hash() {
73+
for (filename, contents) in extra_files.iter() {
74+
let mut out_file = PathBuf::from(&out_dir);
75+
let filename = filename.as_str().unwrap();
76+
out_file.push(filename);
77+
let contents = contents.as_str().unwrap();
78+
fs::write(&out_file, contents).unwrap();
79+
}
11280
}
113-
if let Some(flag) = check_flag(yacc_flags, "show_warnings") {
114-
cp_build = cp_build.show_warnings(flag)
115-
};
116-
let cp = cp_build.build()?;
11781

82+
// Build parser and lexer
11883
let mut outl = PathBuf::from(&out_dir);
11984
outl.push(format!("{}.l.rs", base));
12085
outl.set_extension("rs");
12186
let mut cl_build = CTLexerBuilder::new()
122-
.rule_ids_map(cp.token_map())
87+
.lrpar_config(|mut cp_build| {
88+
let mut outp = PathBuf::from(&out_dir);
89+
outp.push(format!("{}.y.rs", base));
90+
outp.set_extension("rs");
91+
let (negative_yacc_flags, positive_yacc_flags) = &docs[0]["yacc_flags"]
92+
.as_vec()
93+
.map(|flags_vec| {
94+
flags_vec
95+
.iter()
96+
.partition(|flag| flag.as_str().unwrap().starts_with('!'))
97+
})
98+
.unwrap_or_else(|| (Vec::new(), Vec::new()));
99+
let positive_yacc_flags = positive_yacc_flags
100+
.iter()
101+
.map(|flag| flag.as_str().unwrap())
102+
.collect::<Vec<_>>();
103+
let negative_yacc_flags = negative_yacc_flags
104+
.iter()
105+
.map(|flag| {
106+
let flag = flag.as_str().unwrap();
107+
flag.strip_prefix('!').unwrap()
108+
})
109+
.collect::<Vec<_>>();
110+
let yacc_flags = (&positive_yacc_flags, &negative_yacc_flags);
111+
if let Some(yacckind) = yacckind {
112+
cp_build = cp_build.yacckind(yacckind);
113+
}
114+
if let Some(recoverer) = recoverer {
115+
cp_build = cp_build.recoverer(recoverer)
116+
}
117+
cp_build = cp_build
118+
.grammar_path(pg.to_str().unwrap())
119+
.output_path(&outp);
120+
if let Some(flag) = check_flag(yacc_flags, "error_on_conflicts") {
121+
cp_build = cp_build.error_on_conflicts(flag)
122+
}
123+
if let Some(flag) = check_flag(yacc_flags, "warnings_are_errors") {
124+
cp_build = cp_build.warnings_are_errors(flag)
125+
}
126+
if let Some(flag) = check_flag(yacc_flags, "show_warnings") {
127+
cp_build = cp_build.show_warnings(flag)
128+
};
129+
cp_build
130+
})
123131
.lexer_path(pl.to_str().unwrap())
124132
.output_path(&outl);
125133
if let Some(flag) = check_flag(lex_flags, "allow_missing_terms_in_lexer") {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Test calculator with malformed input from %grmtools{test_files}
2+
grammar: |
3+
%grmtools {
4+
yacckind: Original(YaccOriginalActionKind::UserAction),
5+
recoverer: RecoveryKind::None,
6+
test_files: "*.bad_input"
7+
}
8+
%start Expr
9+
%actiontype Result<u64, ()>
10+
%avoid_insert 'INT'
11+
%%
12+
Expr: Expr '+' Term { Ok($1? + $3?) }
13+
| Term { $1 }
14+
;
15+
16+
Term: Term '*' Factor { Ok($1? * $3?) }
17+
| Factor { $1 }
18+
;
19+
20+
Factor: '(' Expr ')' { $2 }
21+
| 'INT' {
22+
let l = $1.map_err(|_| ())?;
23+
match $lexer.span_str(l.span()).parse::<u64>() {
24+
Ok(v) => Ok(v),
25+
Err(_) => {
26+
let ((_, col), _) = $lexer.line_col(l.span());
27+
eprintln!("Error at column {}: '{}' cannot be represented as a u64",
28+
col,
29+
$lexer.span_str(l.span()));
30+
Err(())
31+
}
32+
}
33+
}
34+
;
35+
36+
lexer: |
37+
%%
38+
[0-9]+ "INT"
39+
\+ "+"
40+
\* "*"
41+
\( "("
42+
\) ")"
43+
[\t\n ]+ ;
44+
extra_files:
45+
input1.bad_input: |
46+
(1 + 2 * 3
47+

lrpar/cttests/src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ lrpar_mod!("calc_nodefault_yacckind.y");
2929
lrlex_mod!("calc_unsafeaction.l");
3030
lrpar_mod!("calc_unsafeaction.y");
3131

32+
lrlex_mod!("calc_input.l");
33+
lrpar_mod!("calc_input.y");
34+
3235
lrlex_mod!("expect.l");
3336
lrpar_mod!("expect.y");
3437

@@ -103,6 +106,16 @@ fn test_basic_actions() {
103106
}
104107
}
105108

109+
#[test]
110+
fn test_calc_input() {
111+
let lexerdef = calc_input_l::lexerdef();
112+
let lexer = lexerdef.lexer("2+3");
113+
match calc_input_y::parse(&lexer) {
114+
(Some(Ok(5)), ref errs) if errs.is_empty() => (),
115+
_ => unreachable!(),
116+
}
117+
}
118+
106119
#[test]
107120
fn test_nodefault_yacckind() {
108121
let lexerdef = calc_nodefault_yacckind_l::lexerdef();

0 commit comments

Comments
 (0)