Skip to content

Commit 9a78f2f

Browse files
authored
Merge pull request #604 from ratmice/grmtools_section_array_values
Grmtools section array values
2 parents 4e68283 + 57717e3 commit 9a78f2f

File tree

19 files changed

+380
-224
lines changed

19 files changed

+380
-224
lines changed

.buildbot.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,35 @@ echo "2 + 3 * 4" | cargo run | grep "Result: 14"
5656
touch src/main.rs && CACHE_EXPECTED=y cargo build
5757
cd $root/lrpar/examples/calc_actions
5858
echo "2 + 3 * 4" | cargo run --package nimbleparse -- src/calc.l src/calc.y -
59+
# Invoke `%grmtools{test_files}`
60+
cargo run --package nimbleparse -- src/calc.l src/calc.y
5961
echo "2 + 3 * 4" | cargo run | grep "Result: 14"
6062
touch src/main.rs && CACHE_EXPECTED=y cargo build
6163
cd $root/lrpar/examples/calc_ast
6264
echo "2 + 3 * 4" | cargo run --package nimbleparse -- src/calc.l src/calc.y -
65+
# Invoke `%grmtools{test_files}`
66+
cargo run --package nimbleparse -- src/calc.l src/calc.y
6367
echo "2 + 3 * 4" | cargo run | grep "Result: 14"
6468
cd $root/lrpar/examples/calc_ast_arena
6569
echo "2 + 3 * 4" | cargo run --package nimbleparse -- src/calc.l src/calc.y -
70+
# Invoke `%grmtools{test_files}`
71+
cargo run --package nimbleparse -- src/calc.l src/calc.y
6672
echo "2 + 3 * 4" | cargo run | grep "Result: 14"
6773
touch src/main.rs && CACHE_EXPECTED=y cargo build
6874
cd $root/lrpar/examples/calc_parsetree
6975
echo "2 + 3 * 4" | cargo run --package nimbleparse -- src/calc.l src/calc.y -
76+
# Invoke `%grmtools{test_files}`
77+
cargo run --package nimbleparse -- src/calc.l src/calc.y
7078
echo "2 + 3 * 4" | cargo run | grep "Result: 14"
7179
touch src/main.rs && CACHE_EXPECTED=y cargo build
7280
cd $root/lrpar/examples/clone_param
7381
echo "1+++" | cargo run --package nimbleparse -- src/param.l src/param.y -
82+
# Invoke `%grmtools{test_files}`
83+
cargo run --package nimbleparse -- src/param.l src/param.y
7484
cd $root/lrpar/examples/start_states
7585
echo "/* /* commented out */ */ uncommented text /* */" | cargo run --package nimbleparse -- src/comment.l src/comment.y -
86+
# Invoke `%grmtools{test_files}`
87+
cargo run --package nimbleparse -- src/comment.l src/comment.y
7688
cd $root
7789

7890
RUSTDOCFLAGS="-Dwarnings" cargo doc --no-deps

cfgrammar/src/lib/header.rs

Lines changed: 131 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ pub enum Setting<T> {
137137
},
138138
Num(u64, T),
139139
String(String, T),
140+
// The two `T` values are for the spans of the open and close brackets `[`, and `]`.
141+
Array(Vec<Setting<T>>, T, T),
140142
}
141143

142144
/// Parser for the `%grmtools` section
@@ -157,92 +159,54 @@ pub enum Value<T> {
157159
Setting(Setting<T>),
158160
}
159161

160-
impl From<Value<Span>> for Value<Location> {
161-
fn from(v: Value<Span>) -> Value<Location> {
162-
match v {
163-
Value::Flag(flag, u) => Value::Flag(flag, u.into()),
164-
Value::Setting(s) => Value::Setting(match s {
165-
Setting::Unitary(Namespaced {
166-
namespace,
167-
member: (m, ml),
168-
}) => Setting::Unitary(Namespaced {
169-
namespace: namespace.map(|(n, nl)| (n, nl.into())),
170-
member: (m, ml.into()),
171-
}),
172-
Setting::Constructor {
173-
ctor:
174-
Namespaced {
175-
namespace: ctor_ns,
176-
member: (ctor_m, ctor_ml),
177-
},
178-
arg:
179-
Namespaced {
180-
namespace: arg_ns,
181-
member: (arg_m, arg_ml),
182-
},
183-
} => Setting::Constructor {
184-
ctor: Namespaced {
185-
namespace: ctor_ns.map(|(ns, ns_l)| (ns, ns_l.into())),
186-
member: (ctor_m, ctor_ml.into()),
187-
},
188-
arg: Namespaced {
189-
namespace: arg_ns.map(|(ns, ns_l)| (ns, ns_l.into())),
190-
member: (arg_m, arg_ml.into()),
191-
},
192-
},
193-
Setting::Num(num, num_loc) => Setting::Num(num, num_loc.into()),
194-
Setting::String(s, str_loc) => Setting::String(s, str_loc.into()),
195-
}),
196-
}
197-
}
198-
}
199-
200-
impl<T> Value<T> {
201-
pub fn expect_string_with_context(&self, ctxt: &str) -> Result<&str, Box<dyn Error>> {
202-
let found = match self {
203-
Value::Flag(_, _) => "bool".to_string(),
204-
Value::Setting(Setting::String(s, _)) => {
205-
return Ok(s);
206-
}
207-
Value::Setting(Setting::Num(_, _)) => "numeric".to_string(),
208-
Value::Setting(Setting::Unitary(Namespaced {
162+
impl From<Setting<Span>> for Setting<Location> {
163+
fn from(s: Setting<Span>) -> Setting<Location> {
164+
match s {
165+
Setting::Unitary(Namespaced {
209166
namespace,
210-
member: (member, _),
211-
})) => {
212-
if let Some((ns, _)) = namespace {
213-
format!("'{ns}::{member}'")
214-
} else {
215-
format!("'{member}'")
216-
}
217-
}
218-
Value::Setting(Setting::Constructor {
167+
member: (m, ml),
168+
}) => Setting::Unitary(Namespaced {
169+
namespace: namespace.map(|(n, nl)| (n, nl.into())),
170+
member: (m, ml.into()),
171+
}),
172+
Setting::Constructor {
219173
ctor:
220174
Namespaced {
221175
namespace: ctor_ns,
222-
member: (ctor_memb, _),
176+
member: (ctor_m, ctor_ml),
223177
},
224178
arg:
225179
Namespaced {
226180
namespace: arg_ns,
227-
member: (arg_memb, _),
181+
member: (arg_m, arg_ml),
228182
},
229-
}) => {
230-
format!(
231-
"'{}({})'",
232-
if let Some((ns, _)) = ctor_ns {
233-
format!("{ns}::{ctor_memb}")
234-
} else {
235-
arg_memb.to_string()
236-
},
237-
if let Some((ns, _)) = arg_ns {
238-
format!("{ns}::{arg_memb}")
239-
} else {
240-
arg_memb.to_string()
241-
}
242-
)
243-
}
244-
};
245-
Err(format!("Expected 'String' value, found {}, at {ctxt}", found).into())
183+
} => Setting::Constructor {
184+
ctor: Namespaced {
185+
namespace: ctor_ns.map(|(ns, ns_l)| (ns, ns_l.into())),
186+
member: (ctor_m, ctor_ml.into()),
187+
},
188+
arg: Namespaced {
189+
namespace: arg_ns.map(|(ns, ns_l)| (ns, ns_l.into())),
190+
member: (arg_m, arg_ml.into()),
191+
},
192+
},
193+
Setting::Num(num, num_loc) => Setting::Num(num, num_loc.into()),
194+
Setting::String(s, str_loc) => Setting::String(s, str_loc.into()),
195+
Setting::Array(mut xs, arr_open_loc, arr_close_loc) => Setting::Array(
196+
xs.drain(..).map(|x| x.into()).collect(),
197+
arr_open_loc.into(),
198+
arr_close_loc.into(),
199+
),
200+
}
201+
}
202+
}
203+
204+
impl From<Value<Span>> for Value<Location> {
205+
fn from(v: Value<Span>) -> Value<Location> {
206+
match v {
207+
Value::Flag(flag, u) => Value::Flag(flag, u.into()),
208+
Value::Setting(s) => Value::Setting(s.into()),
209+
}
246210
}
247211
}
248212

@@ -281,7 +245,85 @@ fn add_duplicate_occurrence<T: Eq + PartialEq + Clone>(
281245
}
282246

283247
impl<'input> GrmtoolsSectionParser<'input> {
284-
pub fn parse_value(
248+
fn parse_setting(&'_ self, mut i: usize) -> Result<(Setting<Span>, usize), HeaderError<Span>> {
249+
i = self.parse_ws(i);
250+
match RE_DIGITS.find(&self.src[i..]) {
251+
Some(m) => {
252+
let num_span = Span::new(i + m.start(), i + m.end());
253+
let num_str = &self.src[num_span.start()..num_span.end()];
254+
// If the above regex matches we expect this to succeed.
255+
let num = str::parse::<u64>(num_str).unwrap();
256+
let val = Setting::Num(num, num_span);
257+
i = self.parse_ws(num_span.end());
258+
Ok((val, i))
259+
}
260+
None => match RE_STRING.find(&self.src[i..]) {
261+
Some(m) => {
262+
let end = i + m.end();
263+
// Trim the leading and trailing quotes.
264+
let str_span = Span::new(i + m.start() + 1, end - 1);
265+
let str = &self.src[str_span.start()..str_span.end()];
266+
let setting = Setting::String(str.to_string(), str_span);
267+
// After the trailing quotes.
268+
i = self.parse_ws(end);
269+
Ok((setting, i))
270+
}
271+
None => {
272+
if let Some(mut j) = self.lookahead_is("[", i) {
273+
let mut vals = Vec::new();
274+
let open_pos = j;
275+
276+
loop {
277+
j = self.parse_ws(j);
278+
if let Some(end_pos) = self.lookahead_is("]", j) {
279+
return Ok((
280+
Setting::Array(
281+
vals,
282+
Span::new(i, open_pos),
283+
Span::new(j, end_pos),
284+
),
285+
end_pos,
286+
));
287+
}
288+
if let Ok((val, k)) = self.parse_setting(j) {
289+
vals.push(val);
290+
j = self.parse_ws(k);
291+
}
292+
if let Some(k) = self.lookahead_is(",", j) {
293+
j = k
294+
}
295+
}
296+
} else {
297+
let (path_val, j) = self.parse_namespaced(i)?;
298+
i = self.parse_ws(j);
299+
if let Some(j) = self.lookahead_is("(", i) {
300+
let (arg, j) = self.parse_namespaced(j)?;
301+
i = self.parse_ws(j);
302+
if let Some(j) = self.lookahead_is(")", i) {
303+
i = self.parse_ws(j);
304+
Ok((
305+
Setting::Constructor {
306+
ctor: path_val,
307+
arg,
308+
},
309+
i,
310+
))
311+
} else {
312+
Err(HeaderError {
313+
kind: HeaderErrorKind::ExpectedToken(')'),
314+
locations: vec![Span::new(i, i)],
315+
})
316+
}
317+
} else {
318+
Ok((Setting::Unitary(path_val), i))
319+
}
320+
}
321+
}
322+
},
323+
}
324+
}
325+
326+
pub fn parse_key_value(
285327
&'_ self,
286328
mut i: usize,
287329
) -> Result<(String, Span, Value<Span>, usize), HeaderError<Span>> {
@@ -298,62 +340,8 @@ impl<'input> GrmtoolsSectionParser<'input> {
298340
let key_span = Span::new(i, j);
299341
i = self.parse_ws(j);
300342
if let Some(j) = self.lookahead_is(":", i) {
301-
i = self.parse_ws(j);
302-
match RE_DIGITS.find(&self.src[i..]) {
303-
Some(m) => {
304-
let num_span = Span::new(i + m.start(), i + m.end());
305-
let num_str = &self.src[num_span.start()..num_span.end()];
306-
// If the above regex matches we expect this to succeed.
307-
let num = str::parse::<u64>(num_str).unwrap();
308-
let val = Setting::Num(num, num_span);
309-
i = self.parse_ws(num_span.end());
310-
Ok((key_name, key_span, Value::Setting(val), i))
311-
}
312-
None => match RE_STRING.find(&self.src[i..]) {
313-
Some(m) => {
314-
let end = i + m.end();
315-
// Trim the leading and trailing quotes.
316-
let str_span = Span::new(i + m.start() + 1, end - 1);
317-
let str = &self.src[str_span.start()..str_span.end()];
318-
let setting = Setting::String(str.to_string(), str_span);
319-
// After the trailing quotes.
320-
i = self.parse_ws(end);
321-
Ok((key_name, key_span, Value::Setting(setting), i))
322-
}
323-
None => {
324-
let (path_val, j) = self.parse_namespaced(i)?;
325-
i = self.parse_ws(j);
326-
if let Some(j) = self.lookahead_is("(", i) {
327-
let (arg, j) = self.parse_namespaced(j)?;
328-
i = self.parse_ws(j);
329-
if let Some(j) = self.lookahead_is(")", i) {
330-
i = self.parse_ws(j);
331-
Ok((
332-
key_name,
333-
key_span,
334-
Value::Setting(Setting::Constructor {
335-
ctor: path_val,
336-
arg,
337-
}),
338-
i,
339-
))
340-
} else {
341-
Err(HeaderError {
342-
kind: HeaderErrorKind::ExpectedToken(')'),
343-
locations: vec![Span::new(i, i)],
344-
})
345-
}
346-
} else {
347-
Ok((
348-
key_name,
349-
key_span,
350-
Value::Setting(Setting::Unitary(path_val)),
351-
i,
352-
))
353-
}
354-
}
355-
},
356-
}
343+
let (val, j) = self.parse_setting(j)?;
344+
Ok((key_name, key_span, Value::Setting(val), j))
357345
} else {
358346
Ok((key_name, key_span, Value::Flag(true, key_span), i))
359347
}
@@ -414,7 +402,7 @@ impl<'input> GrmtoolsSectionParser<'input> {
414402
if let Some(j) = self.lookahead_is("{", i) {
415403
i = self.parse_ws(j);
416404
while self.lookahead_is("}", i).is_none() && i < self.src.len() {
417-
let (key, key_loc, val, j) = match self.parse_value(i) {
405+
let (key, key_loc, val, j) = match self.parse_key_value(i) {
418406
Ok((key, key_loc, val, pos)) => (key, key_loc, val, pos),
419407
Err(e) => {
420408
errs.push(e);
@@ -439,7 +427,7 @@ impl<'input> GrmtoolsSectionParser<'input> {
439427
i = self.parse_ws(j);
440428
continue;
441429
} else {
442-
i = j;
430+
i = self.parse_ws(j);
443431
break;
444432
}
445433
}
@@ -587,6 +575,13 @@ impl<T: Clone> TryFrom<&Value<T>> for YaccKind {
587575
),
588576
locations: vec![loc.clone()],
589577
}),
578+
Value::Setting(Setting::Array(_, loc, _)) => Err(HeaderError {
579+
kind: HeaderErrorKind::ConversionError(
580+
"From<YaccKind>",
581+
"Cannot convert array to YaccKind",
582+
),
583+
locations: vec![loc.clone()],
584+
}),
590585
Value::Setting(Setting::String(_, loc)) => Err(HeaderError {
591586
kind: HeaderErrorKind::ConversionError(
592587
"From<YaccKind>",

doc/src/yaccextensions.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ But a default can be set or forced by using a `YaccKindResolver`.
77
|------------------|-------------------------------------------------|--------------|
88
| `yacckind` | [YaccKind](yacccompatibility.md#yacckinds) | &checkmark; |
99
| `recoverykind` | [RecoveryKind](errorrecovery.md#recoverykinds) | &cross; |
10-
| `test_files`[^] | String | &cross; |
10+
| `test_files`[^] | Array of string values | &cross; |
1111

12-
[^]: String containing a glob relative to the yacc `.y` source file, experimental.
12+
[^]: Strings containing globs are resolved relative to the yacc `.y` source file.
13+
`test_files` is currently experimental.
1314

1415
## Example
1516

0 commit comments

Comments
 (0)