1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use crate::parsers::language_python;
use ast_grep_core::language::{Language, TSLanguage};
use std::borrow::Cow;
#[derive(Clone, Copy)]
pub struct Python;
impl Language for Python {
fn get_ts_language(&self) -> TSLanguage {
language_python()
}
fn expando_char(&self) -> char {
'µ'
}
fn pre_process_pattern<'q>(&self, query: &'q str) -> Cow<'q, str> {
let mut buf = [0; 4];
let expando = self.expando_char().encode_utf8(&mut buf);
let replaced = query.replace(self.meta_var_char(), expando);
Cow::Owned(replaced)
}
}
#[cfg(test)]
mod test {
use super::*;
fn test_match(query: &str, source: &str) {
use crate::test::test_match_lang;
test_match_lang(query, source, Python);
}
fn test_non_match(query: &str, source: &str) {
use crate::test::test_non_match_lang;
test_non_match_lang(query, source, Python);
}
#[test]
fn test_python_str() {
test_match("print($A)", "print(123)");
test_match("print('123')", "print('123')");
test_non_match("print('123')", "print('456')");
test_non_match("'123'", "'456'");
}
#[test]
fn test_python_pattern() {
test_match("$A = 0", "a = 0");
test_match(
r#"
match $A:
case $B:
$C
case [$D(0, 0)]:
$E
case [$D($F, $G)]:
$H
case [$D(0, $I), $D(0, $J)]:
$K
case _:
$L
"#,
r#"
match points:
case []:
print("No points")
case [Point(0, 0)]:
print("The origin")
case [Point(x, y)]:
print(f"Single point {x}, {y}")
case [Point(0, y1), Point(0, y2)]:
print(f"Two on the Y axis at {y1}, {y2}")
case _:
print("Something else")
"#,
);
}
fn test_replace(src: &str, pattern: &str, replacer: &str) -> String {
use crate::test::test_replace_lang;
test_replace_lang(src, pattern, replacer, Python)
}
#[test]
fn test_python_replace() {
let ret = test_replace(
r#"
if flag:
a = value_pos
else:
a = value_neg"#,
r#"
if $FLAG:
$VAR = $POS
else:
$VAR = $NEG
"#,
"$VAR = $POS if $FLAG else $NEG",
);
assert_eq!(ret, "\na = value_pos if flag else value_neg");
let ret = test_replace(
r#"
try:
f = open(file_path, "r")
file_content = f.read()
except:
pass
finally:
f.close()"#,
r#"
try:
$A = open($B, $C)
$D = $A.read()
except:
pass
finally:
$A.close()"#,
r#"
with open($B, $C) as $A:
$D = $A.open()"#,
);
assert_eq!(
ret,
r#"
with open(file_path, "r") as f:
file_content = f.open()"#
);
}
}