Skip to content

Commit b8c860b

Browse files
authored
Merge pull request #47 from forkeith/bug_fixes
Fix some parsing bugs
2 parents 82b5140 + ef3b99b commit b8c860b

File tree

3 files changed

+175
-3
lines changed

3 files changed

+175
-3
lines changed

src/parsing/parser.rs

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ impl ParseState {
312312
ops.push((index, ScopeStackOp::Pop(v.len())));
313313
}
314314

315-
if initial && cur_context.clear_scopes != None {
315+
if !initial && cur_context.clear_scopes != None {
316316
ops.push((index, ScopeStackOp::Restore));
317317
}
318318
}
@@ -483,4 +483,96 @@ mod tests {
483483

484484
// assert!(false);
485485
}
486+
487+
fn expect_scope_stacks(line: &str, expect: &[&str]) {
488+
// check that each expected scope stack appears at least once while parsing the given test line
489+
490+
//let syntax = SyntaxSet::load_syntax_file("testdata/parser_tests.sublime-syntax", true).unwrap();
491+
use std::fs::File;
492+
use std::io::Read;
493+
let mut f = File::open("testdata/parser_tests.sublime-syntax").unwrap();
494+
let mut s = String::new();
495+
f.read_to_string(&mut s).unwrap();
496+
497+
let syntax = SyntaxDefinition::load_from_str(&s, true).unwrap();
498+
499+
let mut state = ParseState::new(&syntax);
500+
501+
let mut ss = SyntaxSet::new();
502+
ss.add_syntax(syntax);
503+
ss.link_syntaxes();
504+
505+
let mut stack = ScopeStack::new();
506+
let ops = state.parse_line(line);
507+
debug_print_ops(line, &ops);
508+
509+
let mut criteria_met = Vec::new();
510+
for &(_, ref op) in ops.iter() {
511+
stack.apply(op);
512+
let stack_str = format!("{:?}", stack);
513+
println!("{}", stack_str);
514+
for expectation in expect.iter() {
515+
if stack_str.contains(expectation) {
516+
criteria_met.push(expectation);
517+
}
518+
}
519+
}
520+
if let Some(missing) = expect.iter().filter(|e| !criteria_met.contains(&e)).next() {
521+
panic!("expected scope stack '{}' missing", missing);
522+
}
523+
}
524+
525+
#[test]
526+
fn can_parse_non_nested_clear_scopes() {
527+
let line = "'hello #simple_cleared_scopes_test world test \\n '\n";
528+
let expect = [
529+
"<source.test>, <example.meta-scope.after-clear-scopes.example>, <example.pushes-clear-scopes.example>",
530+
"<source.test>, <example.meta-scope.after-clear-scopes.example>, <example.pops-clear-scopes.example>",
531+
"<source.test>, <string.quoted.single.example>, <constant.character.escape.example>",
532+
];
533+
expect_scope_stacks(&line, &expect);
534+
}
535+
536+
#[test]
537+
fn can_parse_non_nested_too_many_clear_scopes() {
538+
let line = "'hello #too_many_cleared_scopes_test world test \\n '\n";
539+
let expect = [
540+
"<example.meta-scope.after-clear-scopes.example>, <example.pushes-clear-scopes.example>",
541+
"<example.meta-scope.after-clear-scopes.example>, <example.pops-clear-scopes.example>",
542+
"<source.test>, <string.quoted.single.example>, <constant.character.escape.example>",
543+
];
544+
expect_scope_stacks(&line, &expect);
545+
}
546+
547+
#[test]
548+
fn can_parse_nested_clear_scopes() {
549+
let line = "'hello #nested_clear_scopes_test world foo bar test \\n '\n";
550+
let expect = [
551+
"<source.test>, <example.meta-scope.after-clear-scopes.example>, <example.pushes-clear-scopes.example>",
552+
"<source.test>, <example.meta-scope.cleared-previous-meta-scope.example>, <foo>",
553+
"<source.test>, <example.meta-scope.after-clear-scopes.example>, <example.pops-clear-scopes.example>",
554+
"<source.test>, <string.quoted.single.example>, <constant.character.escape.example>",
555+
];
556+
expect_scope_stacks(&line, &expect);
557+
}
558+
559+
#[test]
560+
fn can_parse_infinite_loop() {
561+
let line = "#infinite_loop_test 123\n";
562+
let expect = [
563+
"<source.test>, <constant.numeric.test>",
564+
];
565+
expect_scope_stacks(&line, &expect);
566+
}
567+
568+
#[test]
569+
fn can_parse_infinite_seeming_loop() {
570+
let line = "#infinite_seeming_loop_test hello\n";
571+
let expect = [
572+
"<source.test>, <keyword.test>",
573+
"<source.test>, <test>, <string.unquoted.test>",
574+
"<source.test>, <test>, <keyword.control.test>",
575+
];
576+
expect_scope_stacks(&line, &expect);
577+
}
486578
}

src/parsing/scope.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::fmt;
66
use std::str::FromStr;
77
use std::u64;
88
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
9-
use std::cmp::Ordering;
9+
use std::cmp::{Ordering, min};
1010
use std::mem;
1111

1212
/// Multiplier on the power of 2 for MatchPower.
@@ -374,7 +374,8 @@ impl ScopeStack {
374374
ScopeStackOp::Clear(amount) => {
375375
let cleared = match amount {
376376
ClearAmount::TopN(n) => {
377-
let to_leave = self.scopes.len()-n;
377+
// don't try to clear more scopes than are on the stack
378+
let to_leave = self.scopes.len() - min(n, self.scopes.len());
378379
self.scopes.split_off(to_leave)
379380
}
380381
ClearAmount::All => {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
%YAML 1.2
2+
---
3+
name: Used by tests in src/parsing/parser.rs
4+
scope: source.test
5+
contexts:
6+
main:
7+
- match: '#infinite_seeming_loop_test'
8+
scope: keyword.test
9+
push: infinite_seeming_loop_c
10+
- match: '(?=#infinite_loop_test)'
11+
push: infinite_loop_test_pop_if_not_whitespace
12+
- match: \'
13+
scope: punctuation.definition.string.begin.example
14+
push: cleared_scopes_string_test
15+
- match: '\d+'
16+
scope: constant.numeric.test
17+
18+
infinite_loop_test_pop_if_not_whitespace:
19+
- match: '(?=\S)'
20+
pop: true
21+
infinite_seeming_loop_a:
22+
- meta_content_scope: test
23+
- match: 'h'
24+
scope: string.unquoted.test
25+
- match: 'ello'
26+
scope: keyword.control.test
27+
infinite_seeming_loop_b:
28+
- match: ''
29+
pop: true
30+
- match: '(?=.)'
31+
pop: true
32+
- match: '(?=h)'
33+
pop: true
34+
- match: 'h'
35+
scope: entity.name.function.test
36+
- match: 'e'
37+
scope: storage.type.test
38+
infinite_seeming_loop_c:
39+
- match: ''
40+
push: [infinite_seeming_loop_a, infinite_seeming_loop_b]
41+
cleared_scopes_string_test:
42+
- meta_scope: string.quoted.single.example
43+
- match: '#too_many_cleared_scopes_test'
44+
scope: example.pushes-clear-scopes.example
45+
push:
46+
- clear_scopes: 10
47+
- meta_scope: example.meta-scope.after-clear-scopes.example
48+
- match: 'test'
49+
scope: example.pops-clear-scopes.example
50+
pop: true
51+
- match: '#simple_cleared_scopes_test'
52+
scope: example.pushes-clear-scopes.example
53+
push:
54+
- clear_scopes: 1
55+
- meta_scope: example.meta-scope.after-clear-scopes.example
56+
- match: 'test'
57+
scope: example.pops-clear-scopes.example
58+
pop: true
59+
- match: '#nested_clear_scopes_test'
60+
scope: example.pushes-clear-scopes.example
61+
push:
62+
- clear_scopes: 1
63+
- meta_scope: example.meta-scope.after-clear-scopes.example
64+
- match: 'foo'
65+
scope: foo
66+
push:
67+
- clear_scopes: 1
68+
- meta_scope: example.meta-scope.cleared-previous-meta-scope.example
69+
- match: 'bar'
70+
scope: bar
71+
pop: true
72+
- match: 'test'
73+
scope: example.pops-clear-scopes.example
74+
pop: true
75+
- match: '\\.'
76+
scope: constant.character.escape.example
77+
- match: \'
78+
scope: punctuation.definition.string.end.example
79+
pop: true

0 commit comments

Comments
 (0)