Skip to content

Commit 56cef18

Browse files
authored
fix: incorrect col calculate for pair.line_col method, and add integration test for cover it. (#768)
* Add integration test for cover `pair.line_col` and `position.line_col`. * Fix incorrect `col` calculate for `pair.line_col` method.
1 parent 174aae8 commit 56cef18

File tree

5 files changed

+158
-25
lines changed

5 files changed

+158
-25
lines changed

grammars/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pest_derive = { path = "../derive", version = "2.5.3" }
1919

2020
[dev-dependencies]
2121
criterion = "0.3"
22+
pretty_assertions = "1.3.0"
2223

2324
[[bench]]
2425
name = "json"

grammars/tests/examples.line-col.txt

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
(2:3) "JSON Test Pattern pass1"
2+
(3:4) "object with 1 member"
3+
(3:28) "array with 1 element"
4+
(4:3) {}
5+
(5:3) []
6+
(6:3) -42
7+
(7:3) true
8+
(8:3) false
9+
(9:3) null
10+
(11:5) "integer"
11+
(11:16) 1234567890
12+
(12:5) "real"
13+
(12:13) -9876.543210
14+
(13:5) "e"
15+
(13:10) 0.123456789e-12
16+
(14:5) "E"
17+
(14:10) 1.234567890E+34
18+
(15:5) ""
19+
(15:10) 23456789012E66
20+
(16:5) "zero"
21+
(16:13) 0
22+
(17:5) "one"
23+
(17:12) 1
24+
(18:5) "space"
25+
(18:14) " "
26+
(19:5) "quote"
27+
(19:14) "\""
28+
(20:5) "backslash"
29+
(20:18) "\\"
30+
(21:5) "controls"
31+
(21:17) "\b\f\n\r\t"
32+
(22:5) "slash"
33+
(22:14) "/ & \/"
34+
(23:5) "alpha"
35+
(23:14) "abcdefghijklmnopqrstuvwyz"
36+
(24:5) "ALPHA"
37+
(24:14) "ABCDEFGHIJKLMNOPQRSTUVWYZ"
38+
(25:5) "digit"
39+
(25:14) "0123456789"
40+
(26:5) "0123456789"
41+
(26:19) "digit"
42+
(27:5) "special"
43+
(27:16) "`1~!@#$%^&*()_+-={':[,]}|;.</>?"
44+
(28:5) "hex"
45+
(28:12) "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A"
46+
(29:5) "true"
47+
(29:13) true
48+
(30:5) "false"
49+
(30:14) false
50+
(31:5) "null"
51+
(31:13) null
52+
(32:5) "array"
53+
(32:13) [ ]
54+
(33:5) "object"
55+
(33:14) { }
56+
(34:5) "address"
57+
(34:16) "50 St. James Street"
58+
(35:5) "url"
59+
(35:12) "http://www.JSON.org/"
60+
(36:5) "comment"
61+
(36:16) "// /* <!-- --"
62+
(37:5) "# -- --> */"
63+
(37:20) " "
64+
(38:5) " s p a c e d "
65+
(38:23) 1
66+
(38:25) 2
67+
(38:29) 3
68+
(42:7) 4
69+
(42:11) 5
70+
(42:31) 6
71+
(42:44) 7
72+
(42:55) "compact"
73+
(42:66) 1
74+
(42:68) 2
75+
(42:70) 3
76+
(42:72) 4
77+
(42:74) 5
78+
(42:76) 6
79+
(42:78) 7
80+
(43:5) "jsontext"
81+
(43:17) "{\"object with 1 member\":[\"array with 1 element\"]}"
82+
(44:5) "quotes"
83+
(44:15) "&#34; \u0022 %22 0x22 034 &#x22;"
84+
(45:5) "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
85+
(46:7) "A key can be any string"
86+
(48:3) 0.5
87+
(48:8) 98.6
88+
(50:3) 99.44
89+
(53:3) 1066
90+
(54:3) 1e1
91+
(55:3) 0.1e1
92+
(56:3) 1e-1
93+
(57:3) 1e00
94+
(57:8) 2e+00
95+
(57:14) 2e-00
96+
(58:2) "rosebud"
97+
(59:1)

grammars/tests/json.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@
1111
extern crate pest;
1212
extern crate pest_grammars;
1313

14-
use std::fs::File;
15-
use std::io::Read;
16-
1714
use pest::Parser;
18-
1915
use pest_grammars::json::*;
16+
use pretty_assertions::assert_eq;
2017

2118
#[test]
2219
fn null() {
@@ -164,10 +161,50 @@ fn object() {
164161

165162
#[test]
166163
fn examples() {
167-
let mut file = File::open("tests/examples.json").unwrap();
168-
let mut data = String::new();
164+
let raw = include_str!("examples.json");
165+
let pairs = JsonParser::parse(Rule::json, raw).unwrap();
166+
167+
let expected = include_str!("examples.line-col.txt");
168+
169+
// Test for flatten iter, and use span.start_pos().line_col()
170+
let mut out = String::new();
171+
for pair in pairs.clone().flatten() {
172+
let sub_pairs = pair.clone().into_inner();
173+
if sub_pairs.count() == 0 {
174+
let span = pair.as_span();
175+
out.push_str(&build_line_col(span.start_pos().line_col(), span.as_str()));
176+
}
177+
}
178+
assert_eq!(expected.trim(), out.trim());
179+
180+
// Test for nested iter, use pair.line_col()
181+
let mut out = String::new();
182+
for pair in pairs {
183+
out.push_str(&build_result_for_pair(pair.clone()));
184+
}
185+
186+
assert_eq!(expected.trim(), out.trim());
187+
}
169188

170-
file.read_to_string(&mut data).unwrap();
189+
fn build_line_col(line_col: (usize, usize), str: &str) -> String {
190+
format!(
191+
"({}:{}) {}\n",
192+
line_col.0,
193+
line_col.1,
194+
str.replace('\n', "\\n")
195+
)
196+
}
171197

172-
JsonParser::parse(Rule::json, &data).unwrap();
198+
fn build_result_for_pair(pair: pest::iterators::Pair<Rule>) -> String {
199+
let mut out = String::new();
200+
201+
let sub_pairs = pair.clone().into_inner();
202+
if sub_pairs.clone().count() == 0 {
203+
out.push_str(&build_line_col(pair.line_col(), pair.as_str()));
204+
} else {
205+
for sub_pair in sub_pairs {
206+
out.push_str(&build_result_for_pair(sub_pair));
207+
}
208+
}
209+
out
173210
}

pest/src/iterators/pairs.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,7 @@ impl<'i, R: RuleType> Pairs<'i, R> {
249249
let (prev_line, prev_col) = (self.cursor.line, self.cursor.col);
250250

251251
let part = &input[self.cursor.end..end];
252-
let (l, c) = position::line_col(part, part.len());
253-
254-
// Because the `original_line_col` returns (line, col) is start from 1
255-
let l = l - 1;
256-
let mut c = c - 1;
257-
if c < 1 {
258-
c = 1
259-
}
252+
let (l, c) = position::line_col(part, part.len(), (0, 0));
260253

261254
self.cursor.line += l;
262255
// Has new line

pest/src/position.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ impl<'i> Position<'i> {
139139
panic!("position out of bounds");
140140
}
141141

142-
line_col(self.input, self.pos)
142+
line_col(self.input, self.pos, (1, 1))
143143
}
144144

145145
/// Returns the entire line of the input that contains this `Position`.
@@ -452,25 +452,30 @@ impl<'i> Hash for Position<'i> {
452452
}
453453
}
454454

455-
pub(crate) fn line_col(input: &str, pos: usize) -> (usize, usize) {
455+
/// Returns the line and column of the given `pos` in `input`.
456+
pub(crate) fn line_col(input: &str, pos: usize, start: (usize, usize)) -> (usize, usize) {
456457
#[cfg(feature = "fast-line-col")]
457458
{
458-
fast_line_col(input, pos)
459+
fast_line_col(input, pos, start)
459460
}
460461
#[cfg(not(feature = "fast-line-col"))]
461462
{
462-
original_line_col(input, pos)
463+
original_line_col(input, pos, start)
463464
}
464465
}
465466

466467
#[inline]
467468
#[cfg(not(feature = "fast-line-col"))]
468-
fn original_line_col(input: &str, mut pos: usize) -> (usize, usize) {
469+
pub(crate) fn original_line_col(
470+
input: &str,
471+
mut pos: usize,
472+
start: (usize, usize),
473+
) -> (usize, usize) {
469474
// Position's pos is always a UTF-8 border.
470475
let slice = &input[..pos];
471476
let mut chars = slice.chars().peekable();
472477

473-
let mut line_col = (1, 1);
478+
let mut line_col = start;
474479

475480
while pos != 0 {
476481
match chars.next() {
@@ -507,16 +512,16 @@ fn original_line_col(input: &str, mut pos: usize) -> (usize, usize) {
507512

508513
#[inline]
509514
#[cfg(feature = "fast-line-col")]
510-
fn fast_line_col(input: &str, pos: usize) -> (usize, usize) {
515+
fn fast_line_col(input: &str, pos: usize, start: (usize, usize)) -> (usize, usize) {
511516
// Position's pos is always a UTF-8 border.
512517
let slice = &input[..pos];
513518

514519
let prec_ln = memchr::memrchr(b'\n', slice.as_bytes());
515520
if let Some(prec_nl_pos) = prec_ln {
516-
let lines = bytecount::count(slice[..=prec_nl_pos].as_bytes(), b'\n') + 1;
521+
let lines = bytecount::count(slice[..=prec_nl_pos].as_bytes(), b'\n') + start.0;
517522
(lines, slice[prec_nl_pos..].chars().count())
518523
} else {
519-
(1, slice.chars().count() + 1)
524+
(start.0, slice.chars().count() + start.1)
520525
}
521526
}
522527

0 commit comments

Comments
 (0)