Skip to content

Commit 229eaf3

Browse files
gvozdvmozgubenfdking
authored andcommitted
chore: add configurable indentation handling to parser context
1 parent 89a24b1 commit 229eaf3

File tree

13 files changed

+275
-83
lines changed

13 files changed

+275
-83
lines changed

crates/lib/src/core/config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,17 @@ impl Index<&str> for Value {
457457
}
458458

459459
impl Value {
460+
pub fn to_bool(&self) -> bool {
461+
match *self {
462+
Value::Int(v) => v != 0,
463+
Value::Bool(v) => v,
464+
Value::Float(v) => v != 0.0,
465+
Value::String(ref v) => !v.is_empty(),
466+
Value::Map(ref v) => !v.is_empty(),
467+
Value::None => false,
468+
}
469+
}
470+
460471
pub fn as_map(&self) -> Option<&AHashMap<String, Value>> {
461472
if let Self::Map(map) = self { Some(map) } else { None }
462473
}

crates/lib/src/core/parser/context.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use ahash::AHashMap;
2+
use itertools::Itertools;
23

34
use super::match_result::MatchResult;
45
use super::matchable::Matchable;
@@ -14,12 +15,13 @@ pub struct ParseContext {
1415
match_stack: Vec<String>,
1516
match_depth: usize,
1617
track_progress: bool,
17-
pub terminators: Vec<Box<dyn Matchable>>,
18+
pub(crate) terminators: Vec<Box<dyn Matchable>>,
1819
parse_cache: AHashMap<((String, (usize, usize), &'static str, usize), String), MatchResult>,
20+
pub(crate) indentation_config: AHashMap<String, bool>,
1921
}
2022

2123
impl ParseContext {
22-
pub fn new(dialect: Dialect) -> Self {
24+
pub fn new(dialect: Dialect, indentation_config: AHashMap<String, bool>) -> Self {
2325
Self {
2426
dialect,
2527
tqdm: None,
@@ -29,16 +31,21 @@ impl ParseContext {
2931
track_progress: true,
3032
terminators: Vec::new(),
3133
parse_cache: AHashMap::new(),
34+
indentation_config,
3235
}
3336
}
3437

3538
pub fn dialect(&self) -> &Dialect {
3639
&self.dialect
3740
}
3841

39-
pub fn from_config(_config: FluffConfig) -> Self {
42+
pub fn from_config(config: FluffConfig) -> Self {
4043
let dialect = dialect_selector("ansi").unwrap();
41-
Self::new(dialect)
44+
let indentation_config = config.raw["indentation"].as_map().unwrap().clone();
45+
let indentation_config: AHashMap<_, _> =
46+
indentation_config.into_iter().map(|(key, value)| (key, value.to_bool())).collect();
47+
48+
Self::new(dialect, indentation_config)
4249
}
4350

4451
pub fn progress_bar<T>(&mut self, mut f: impl FnMut(&mut Self) -> T) -> T {

crates/lib/src/core/parser/grammar/anyof.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ mod tests {
332332
g.disallow_gaps();
333333
}
334334

335-
let mut ctx = ParseContext::new(fresh_ansi_dialect());
335+
let mut ctx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
336336

337337
// Check directly
338338
let mut segments = g.match_segments(&test_segments(), &mut ctx).unwrap();
@@ -347,7 +347,7 @@ mod tests {
347347

348348
#[test]
349349
fn test__parser__grammar_oneof_templated() {
350-
let mut ctx = ParseContext::new(fresh_ansi_dialect());
350+
let mut ctx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
351351

352352
let bs = StringParser::new(
353353
"bar",
@@ -420,7 +420,7 @@ mod tests {
420420
];
421421

422422
let segments = generate_test_segments_func(vec!["a", " ", "b", " ", "c", "d", " ", "d"]);
423-
let mut parse_cx = ParseContext::new(fresh_ansi_dialect());
423+
let mut parse_cx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
424424

425425
for (mode, sequence, terminators, output, max_times) in cases {
426426
let elements = sequence
@@ -514,7 +514,7 @@ mod tests {
514514
None,
515515
);
516516

517-
let mut ctx = ParseContext::new(fresh_ansi_dialect());
517+
let mut ctx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
518518
let g = AnyNumberOf::new(vec![Box::new(bar), Box::new(foo)]);
519519
let result = g.match_segments(&segments, &mut ctx).unwrap().matched_segments;
520520

@@ -558,7 +558,7 @@ mod tests {
558558
let g1 = one_of(vec![Box::new(foo_regex.clone()), Box::new(foo.clone())]);
559559
let g2 = one_of(vec![Box::new(foo), Box::new(foo_regex)]);
560560

561-
let mut ctx = ParseContext::new(fresh_ansi_dialect());
561+
let mut ctx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
562562

563563
for segment in g1.match_segments(&segments, &mut ctx).unwrap().matched_segments.iter() {
564564
assert_eq!(segment.get_raw().unwrap(), "foo");

crates/lib/src/core/parser/grammar/base.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use std::borrow::Cow;
2+
use std::ops::Deref;
3+
14
use ahash::AHashSet;
25
use itertools::enumerate;
36
use uuid::Uuid;
@@ -99,7 +102,7 @@ impl Matchable for BaseGrammar {
99102

100103
#[derive(Clone, Hash)]
101104
pub struct Ref {
102-
reference: String,
105+
reference: Cow<'static, str>,
103106
exclude: Option<Box<dyn Matchable>>,
104107
terminators: Vec<Box<dyn Matchable>>,
105108
reset_terminators: bool,
@@ -116,9 +119,9 @@ impl std::fmt::Debug for Ref {
116119

117120
impl Ref {
118121
// Constructor function
119-
pub fn new(reference: impl ToString) -> Self {
122+
pub fn new(reference: impl Into<Cow<'static, str>>) -> Self {
120123
Ref {
121-
reference: reference.to_string(),
124+
reference: reference.into(),
122125
exclude: None,
123126
terminators: Vec::new(),
124127
reset_terminators: false,
@@ -145,7 +148,7 @@ impl Ref {
145148

146149
// Static method to create a Ref instance for a keyword
147150
pub fn keyword(keyword: &str) -> Self {
148-
let name = format!("{}KeywordSegment", capitalize(keyword));
151+
let name = capitalize(keyword) + "KeywordSegment";
149152
Ref::new(name)
150153
}
151154
}
@@ -178,7 +181,7 @@ impl Matchable for Ref {
178181
crumbs: Option<Vec<&str>>,
179182
) -> Option<(AHashSet<String>, AHashSet<String>)> {
180183
if let Some(ref c) = crumbs {
181-
if c.contains(&self.reference.as_str()) {
184+
if c.contains(&self.reference.deref()) {
182185
let loop_string = c.join(" -> ");
183186
panic!("Self referential grammar detected: {}", loop_string);
184187
}
@@ -450,7 +453,7 @@ mod tests {
450453
// Assuming 'generate_test_segments' and 'fresh_ansi_dialect' are implemented
451454
// elsewhere
452455
let ts = generate_test_segments_func(vec!["ABS", "ABSOLUTE"]);
453-
let mut ctx = ParseContext::new(fresh_ansi_dialect());
456+
let mut ctx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
454457

455458
// Assert ABS does not match, due to the exclude
456459
assert!(ni.match_segments(&[ts[0].clone()], &mut ctx).unwrap().matched_segments.is_empty());
@@ -463,7 +466,7 @@ mod tests {
463466

464467
#[test]
465468
fn test_parser_grammar_nothing() {
466-
let mut ctx = ParseContext::new(fresh_ansi_dialect());
469+
let mut ctx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
467470

468471
assert!(
469472
Nothing::new()
@@ -488,7 +491,7 @@ mod tests {
488491
(0..2, "bar", true, (0..2).into()),
489492
];
490493

491-
let mut ctx = ParseContext::new(fresh_ansi_dialect());
494+
let mut ctx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
492495
for (segments_slice, matcher_keyword, trim_noncode, result_slice) in cases {
493496
let matchers = vec![
494497
StringParser::new(
@@ -560,7 +563,7 @@ mod tests {
560563
Box::new(Sequence::new(vec![bs, fs])),
561564
];
562565

563-
let mut ctx = ParseContext::new(fresh_ansi_dialect());
566+
let mut ctx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
564567
// Matching the first element of the list
565568
let (match_result, matcher) =
566569
longest_trimmed_match(&test_segments(), matchers.clone(), &mut ctx, true).unwrap();
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,96 @@
1+
use crate::core::errors::SQLParseError;
2+
use crate::core::parser::context::ParseContext;
3+
use crate::core::parser::match_result::MatchResult;
4+
use crate::core::parser::matchable::Matchable;
5+
use crate::core::parser::segments::base::{ErasedSegment, Segment};
6+
use crate::core::parser::segments::meta::Indent;
7+
use crate::helpers::ToErasedSegment;
18

9+
#[derive(Clone, Debug, Hash, PartialEq)]
10+
pub struct Conditional {
11+
meta: Indent,
12+
indented_joins: bool,
13+
indented_using_on: bool,
14+
indented_on_contents: bool,
15+
indented_then: bool,
16+
}
17+
18+
impl Conditional {
19+
pub fn new(meta: Indent) -> Self {
20+
Self {
21+
meta,
22+
indented_joins: false,
23+
indented_using_on: false,
24+
indented_on_contents: false,
25+
indented_then: false,
26+
}
27+
}
28+
29+
pub fn indented_joins(mut self) -> Self {
30+
self.indented_joins = true;
31+
self
32+
}
33+
34+
pub fn indented_using_on(mut self) -> Self {
35+
self.indented_using_on = true;
36+
self
37+
}
38+
39+
pub fn indented_on_contents(mut self) -> Self {
40+
self.indented_on_contents = true;
41+
self
42+
}
43+
44+
pub fn indented_then(mut self) -> Self {
45+
self.indented_then = true;
46+
self
47+
}
48+
49+
pub fn indented_then_contents(mut self) -> Self {
50+
self
51+
}
52+
53+
fn is_enabled(&self, parse_context: &mut ParseContext) -> bool {
54+
macro_rules! check_config_match {
55+
($self:expr, $parse_context:expr, $field:ident) => {{
56+
let config_value = $parse_context
57+
.indentation_config
58+
.get(stringify!($field))
59+
.copied()
60+
.unwrap_or_default();
61+
62+
if $self.$field && $self.$field != config_value {
63+
return false;
64+
}
65+
}};
66+
}
67+
68+
check_config_match!(self, parse_context, indented_joins);
69+
check_config_match!(self, parse_context, indented_using_on);
70+
check_config_match!(self, parse_context, indented_on_contents);
71+
check_config_match!(self, parse_context, indented_then);
72+
73+
true
74+
}
75+
}
76+
77+
impl Segment for Conditional {}
78+
79+
impl Matchable for Conditional {
80+
fn match_segments(
81+
&self,
82+
segments: &[ErasedSegment],
83+
parse_context: &mut ParseContext,
84+
) -> Result<MatchResult, SQLParseError> {
85+
if !self.is_enabled(parse_context) {
86+
return Ok(MatchResult::from_unmatched(segments.to_vec()));
87+
}
88+
89+
dbg!(self);
90+
91+
Ok(MatchResult {
92+
matched_segments: vec![self.meta.clone().to_erased_segment()],
93+
unmatched_segments: segments.to_vec(),
94+
})
95+
}
96+
}

crates/lib/src/core/parser/grammar/delimited.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ mod tests {
306306
(2.into(), true, false, vec!["bar", ".", "bar", "foo"], 0),
307307
];
308308

309-
let mut ctx = ParseContext::new(fresh_ansi_dialect());
309+
let mut ctx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
310310

311311
for (min_delimiters, allow_gaps, allow_trailing, token_list, match_len) in cases {
312312
let test_segments = generate_test_segments_func(token_list);
@@ -349,7 +349,7 @@ mod tests {
349349

350350
#[test]
351351
fn test__parser__grammar_anything_bracketed() {
352-
let mut ctx = ParseContext::new(fresh_ansi_dialect());
352+
let mut ctx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
353353
let foo = StringParser::new(
354354
"foo",
355355
|segment| {
@@ -393,7 +393,7 @@ mod tests {
393393
for (terminators, match_length) in cases {
394394
let _panic = enter_panic(terminators.join(" "));
395395

396-
let mut cx = ParseContext::new(fresh_ansi_dialect());
396+
let mut cx = ParseContext::new(fresh_ansi_dialect(), <_>::default());
397397
let terms = terminators
398398
.iter()
399399
.map(|it| {

crates/lib/src/core/parser/grammar/noncode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ mod tests {
5555
#[test]
5656
fn test__parser__grammar_noncode() {
5757
let dialect = fresh_ansi_dialect(); // Assuming this function exists and returns a Dialect
58-
let mut ctx = ParseContext::new(dialect);
58+
let mut ctx = ParseContext::new(dialect, <_>::default());
5959

6060
let matcher = NonCodeMatcher;
6161
let test_segments = test_segments(); // Assuming this function exists and generates test segments

0 commit comments

Comments
 (0)