Skip to content

Commit 53a4b78

Browse files
committed
replacer
1 parent 4de0cb1 commit 53a4b78

File tree

3 files changed

+92
-0
lines changed

3 files changed

+92
-0
lines changed

ast/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ location = ["fold", "rustpython-parser-core/location"]
1414
fold = []
1515
unparse = ["rustpython-literal"]
1616
visitor = []
17+
replace = ["fold"]
1718
all-nodes-with-ranges = []
1819

1920
[dependencies]

ast/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ pub use rustpython_parser_core::source_code;
3737
#[cfg(feature = "visitor")]
3838
pub use visitor::Visitor;
3939

40+
#[cfg(feature = "replace")]
41+
pub mod replace;
42+
#[cfg(feature = "replace")]
43+
pub use replace::Replacer;
44+
4045
#[cfg(feature = "constant-optimization")]
4146
mod optimizer;
4247
#[cfg(feature = "constant-optimization")]

ast/src/replace.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use crate as ast;
2+
use crate::fold::{Fold, Foldable};
3+
use crate::text_size::TextRange;
4+
use std::collections::HashMap;
5+
6+
pub enum Replacement {
7+
Node(ast::Expr),
8+
Sequence(Vec<ast::Expr>),
9+
}
10+
11+
impl From<ast::Expr> for Replacement {
12+
fn from(node: ast::Expr) -> Self {
13+
Self::Node(node)
14+
}
15+
}
16+
17+
impl From<Vec<ast::Expr>> for Replacement {
18+
fn from(nodes: Vec<ast::Expr>) -> Self {
19+
Self::Sequence(nodes)
20+
}
21+
}
22+
23+
pub struct ReplaceError {
24+
node: ast::Ast,
25+
reason: &'static str,
26+
}
27+
28+
impl ReplaceError {
29+
fn new(node: impl Into<ast::Ast>, reason: &'static str) -> Self {
30+
let node = node.into();
31+
Self { node, reason }
32+
}
33+
}
34+
35+
pub struct Replacer {
36+
map: HashMap<String, Replacement>,
37+
}
38+
39+
impl Fold<TextRange> for Replacer {
40+
type TargetU = TextRange;
41+
type Error = ReplaceError;
42+
type UserContext = ();
43+
44+
fn will_map_user(&mut self, _user: &TextRange) -> Self::UserContext {}
45+
fn map_user(&mut self, user: TextRange, _context: ()) -> Result<Self::TargetU, Self::Error> {
46+
Ok(user)
47+
}
48+
49+
fn fold_expr(
50+
&mut self,
51+
node: ast::Expr<TextRange>,
52+
) -> Result<ast::Expr<Self::TargetU>, Self::Error> {
53+
let node = match node {
54+
ast::Expr::Name(name) if self.map.contains_key(name.id.as_str()) => {
55+
let expr = &self.map[name.id.as_str()];
56+
match expr {
57+
Replacement::Node(node) => node.clone(),
58+
Replacement::Sequence(_) => {
59+
return Err(ReplaceError::new(name, "single node expected"));
60+
}
61+
}
62+
}
63+
// Starred
64+
node => node,
65+
};
66+
Ok(node)
67+
}
68+
}
69+
70+
#[cfg(tests)]
71+
mod tests {
72+
#[test]
73+
fn replace() {
74+
let replacements = vec![("a".to_owned(), ast::Constant::parse("2").unwrap())];
75+
let mut replacer = Replacer {
76+
map: replacements.into_iter().collect(),
77+
};
78+
79+
let source = "x = (1, a, 3)";
80+
let ast = ast::Statement::parse(source, "<test>").unwrap();
81+
let edited = replacer.fold(ast).unwrap();
82+
83+
let expected = ast::Statement::parse("x = (1, 2, 3)", "<test>").unwrap();
84+
assert_eq!(edited, expected);
85+
}
86+
}

0 commit comments

Comments
 (0)