Skip to content

Commit 4072971

Browse files
committed
mod expr support, lite refactoring, simple Decode trait alias for complex Parser
1 parent cbce028 commit 4072971

File tree

5 files changed

+55
-64
lines changed

5 files changed

+55
-64
lines changed

rust/rustell/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ Rustell is a development tool for Rust, designed for users who are not accustome
44

55
Its usage is similar to "rustfmt": Rustell reads Rust source code (which may omit some required semicolons) from standard input ("stdin") and produces corrected output with all necessary semicolons inserted through standard output ("stdout"). It is intended to be used as a text editor plugin, typically invoked right before running "rustfmt".
66

7-
Rustell is implemented as a partial Rust parser. It parses only those parts of the code that depend on semicolons, such as "use", "mod", and "let" statements, as well as statements inside "{ ... }" code blocks (excluding the final statement in a block). Rustell converts these expressions into an internal AST, consuming any existing semicolons if present. Code that does not belong to these statements is left untouched and represented as a special "other" node type within the AST.
7+
Rustell is implemented as a partial Rust parser. It parses only those parts of the code that depend on semicolons, such as "use", "mod", and "let" statements, as well as statements inside "{ ... }" code blocks (excluding the final statement in a block). Rustell converts these expressions into an internal AST, consuming any existing semicolons if present. Code that does not belong to these statements is left untouched and represented as a special "raw" node type within the AST.
88

9-
Finally, Rustell renders its AST back into Rust source code, appending semicolons where required while preserving "other" nodes exactly as they appeared in the original input.
9+
Finally, Rustell renders its AST back into Rust source code, appending semicolons where required while preserving "raw" nodes exactly as they appeared in the original input.

rust/rustell/src/decode.rs

Lines changed: 39 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,43 @@ use chumsky::prelude::Parser;
33
use chumsky::prelude::*;
44
use chumsky::text::whitespace;
55

6-
pub fn expr<'a>() -> impl Parser<
7-
'a,
8-
&'a str,
9-
Vec<Expr<'a>>,
10-
extra::Err<Rich<'a, char>>,
11-
> {
12-
choice((expr_use(), expr_other()))
6+
pub trait Decode<'a, T>:
7+
Parser<'a, &'a str, T, extra::Err<Rich<'a, char>>>
8+
{
9+
}
10+
11+
impl<'a, T, U> Decode<'a, T> for U where
12+
U: Parser<'a, &'a str, T, extra::Err<Rich<'a, char>>>
13+
{
14+
}
15+
16+
pub fn expr<'a>() -> impl Decode<'a, Vec<Expr<'a>>> {
17+
choice((expr_ast(), expr_raw()))
1318
.repeated()
1419
.collect::<Vec<_>>()
1520
}
1621

17-
fn expr_use<'a>()
18-
-> impl Parser<'a, &'a str, Expr<'a>, extra::Err<Rich<'a, char>>>
19-
{
22+
fn expr_ast<'a>() -> impl Decode<'a, Expr<'a>> {
23+
choice((expr_mod(), expr_use()))
24+
}
25+
26+
fn expr_mod<'a>() -> impl Decode<'a, Expr<'a>> {
27+
let tok =
28+
token(text::ascii::ident()).and_is(keyword().not());
29+
just("mod")
30+
.ignore_then(tok)
31+
.then_ignore(lexeme(";").or_not())
32+
.map(Expr::Mod)
33+
}
34+
35+
fn expr_use<'a>() -> impl Decode<'a, Expr<'a>> {
2036
just("use")
2137
.ignore_then(expr_use_rec())
2238
.then_ignore(lexeme(";").or_not())
2339
.map(Expr::Use)
2440
}
2541

26-
fn expr_use_rec<'a>() -> impl Parser<
27-
'a,
28-
&'a str,
29-
ExprUse<'a>,
30-
extra::Err<Rich<'a, char>>,
31-
> {
42+
fn expr_use_rec<'a>() -> impl Decode<'a, ExprUse<'a>> {
3243
recursive(|expr_use_rec| {
3344
let item = expr_use_tok()
3445
.then(
@@ -62,61 +73,41 @@ fn expr_use_rec<'a>() -> impl Parser<
6273
})
6374
}
6475

65-
fn expr_use_tok<'a>()
66-
-> impl Parser<'a, &'a str, &'a str, extra::Err<Rich<'a, char>>>
67-
+ Clone {
76+
fn expr_use_tok<'a>() -> impl Decode<'a, &'a str> + Clone {
6877
token(text::ascii::ident()).and_is(
6978
keyword_except(&["crate", "super", "self", "Self"])
7079
.not(),
7180
)
7281
}
7382

74-
fn expr_other<'a>()
75-
-> impl Parser<'a, &'a str, Expr<'a>, extra::Err<Rich<'a, char>>>
76-
{
83+
fn expr_raw<'a>() -> impl Decode<'a, Expr<'a>> {
7784
any()
78-
.and_is(expr_use().not())
85+
.and_is(expr_ast().not())
7986
.repeated()
8087
.at_least(1)
8188
.to_slice()
82-
.map(Expr::Other)
89+
.map(Expr::Raw)
8390
}
8491

8592
fn token<'a>(
86-
tok: impl Parser<
87-
'a,
88-
&'a str,
89-
&'a str,
90-
extra::Err<Rich<'a, char>>,
91-
> + Clone,
92-
) -> impl Parser<
93-
'a,
94-
&'a str,
95-
&'a str,
96-
extra::Err<Rich<'a, char>>,
97-
> + Clone {
93+
tok: impl Decode<'a, &'a str> + Clone,
94+
) -> impl Decode<'a, &'a str> + Clone {
9895
whitespace().or_not().ignore_then(tok)
9996
}
10097

10198
fn lexeme<'a>(
10299
seq: &'a str,
103-
) -> impl Parser<
104-
'a,
105-
&'a str,
106-
&'a str,
107-
extra::Err<Rich<'a, char>>,
108-
> + Clone {
100+
) -> impl Decode<'a, &'a str> + Clone {
109101
token(just(seq))
110102
}
111103

104+
fn keyword<'a>() -> impl Decode<'a, &'a str> + Clone {
105+
keyword_except(&[])
106+
}
107+
112108
fn keyword_except<'a>(
113109
except: &[&str],
114-
) -> impl Parser<
115-
'a,
116-
&'a str,
117-
&'a str,
118-
extra::Err<Rich<'a, char>>,
119-
> + Clone {
110+
) -> impl Decode<'a, &'a str> + Clone {
120111
choice(
121112
[
122113
"as", "break", "const", "continue", "crate",

rust/rustell/src/encode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn expr_one<'a>(ast: &'a Expr<'a>) -> IntoIter<&'a str> {
1515
.chain(once(";"))
1616
.collect::<Vec<_>>()
1717
.into_iter(),
18-
Expr::Other(x) => vec![*x].into_iter(),
18+
Expr::Raw(x) => vec![*x].into_iter(),
1919
}
2020
}
2121

rust/rustell/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use chumsky::prelude::Parser;
66
pub enum Expr<'a> {
77
Mod(&'a str),
88
Use(ExprUse<'a>),
9-
Other(&'a str),
9+
Raw(&'a str),
1010
}
1111

1212
#[derive(Eq, PartialEq, Debug, Clone)]

rust/rustell/tests/integration.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,14 @@ fn test_parser_crate() {
143143
}
144144

145145
#[test]
146-
fn test_parser_other_then_use() {
146+
fn test_parser_raw_then_use() {
147147
let lhs = r#"
148148
fn test() {
149149
println!("Hello")
150150
}
151151
use crate::module::Type;"#;
152152
let rhs = vec![
153-
Expr::Other(
153+
Expr::Raw(
154154
r#"
155155
fn test() {
156156
println!("Hello")
@@ -183,7 +183,7 @@ fn test_parser_multiple() {
183183
use std::fs;
184184
"#;
185185
let rhs = vec![
186-
Expr::Other("\n "),
186+
Expr::Raw("\n "),
187187
Expr::Use(ExprUse::Item {
188188
module: "std",
189189
rename: None,
@@ -193,7 +193,7 @@ fn test_parser_multiple() {
193193
nested: None,
194194
})),
195195
}),
196-
Expr::Other("\n "),
196+
Expr::Raw("\n "),
197197
Expr::Use(ExprUse::Item {
198198
module: "std",
199199
rename: None,
@@ -203,15 +203,15 @@ fn test_parser_multiple() {
203203
nested: None,
204204
})),
205205
}),
206-
Expr::Other("\n "),
206+
Expr::Raw("\n "),
207207
];
208208
assert_eq!(decode(lhs), rhs);
209209
assert_eq!(decode(&sloppy(lhs)), rhs);
210210
assert_eq!(decode(&encode(&rhs)), rhs)
211211
}
212212

213213
#[test]
214-
fn test_parser_multiple_with_other() {
214+
fn test_parser_multiple_with_raw() {
215215
let lhs = r#"
216216
use std::io;
217217
fn test() {
@@ -220,7 +220,7 @@ fn test_parser_multiple_with_other() {
220220
use std::fs;
221221
"#;
222222
let rhs = vec![
223-
Expr::Other("\n "),
223+
Expr::Raw("\n "),
224224
Expr::Use(ExprUse::Item {
225225
module: "std",
226226
rename: None,
@@ -230,7 +230,7 @@ fn test_parser_multiple_with_other() {
230230
nested: None,
231231
})),
232232
}),
233-
Expr::Other(
233+
Expr::Raw(
234234
r#"
235235
fn test() {
236236
println!("Hello")
@@ -246,7 +246,7 @@ fn test_parser_multiple_with_other() {
246246
nested: None,
247247
})),
248248
}),
249-
Expr::Other("\n "),
249+
Expr::Raw("\n "),
250250
];
251251
assert_eq!(decode(lhs), rhs);
252252
assert_eq!(decode(&sloppy(lhs)), rhs);
@@ -267,7 +267,7 @@ fn test_parser_mixed_all_cases() {
267267
}
268268
"#;
269269
let rhs = vec![
270-
Expr::Other("\n "),
270+
Expr::Raw("\n "),
271271
Expr::Use(ExprUse::Item {
272272
module: "std",
273273
rename: None,
@@ -297,7 +297,7 @@ fn test_parser_mixed_all_cases() {
297297
},
298298
]))),
299299
}),
300-
Expr::Other("\n "),
300+
Expr::Raw("\n "),
301301
Expr::Use(ExprUse::Item {
302302
module: "crate",
303303
rename: None,
@@ -311,7 +311,7 @@ fn test_parser_mixed_all_cases() {
311311
})),
312312
})),
313313
}),
314-
Expr::Other(
314+
Expr::Raw(
315315
r#"
316316
317317
fn hello() {

0 commit comments

Comments
 (0)