diff options
author | Aleksey Kladov <[email protected]> | 2021-05-09 17:51:06 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2021-05-09 17:55:43 +0100 |
commit | 4f3c0adc5aafea465c71c85f36484da970df1ba2 (patch) | |
tree | 4cb502a6c345c3ecfb4090067ab1de2cfd69ed55 /crates/syntax | |
parent | 680a0d54e4d2d474ae41f4f4a95c749495a02883 (diff) |
internal: introduce `ast::make::ext` module with common shortcuts
There's a tension between keeping a well-architectured minimal
orthogonal set of constructs, and providing convenience functions.
Relieve this pressure by introducing an dedicated module for
non-orthogonal shortcuts.
This is inspired by the django.shortcuts module which serves a similar
purpose architecturally.
Diffstat (limited to 'crates/syntax')
-rw-r--r-- | crates/syntax/src/ast/make.rs | 95 |
1 files changed, 52 insertions, 43 deletions
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index c39e248ce..a378b1d37 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | //! | 3 | //! |
4 | //! Note that all functions here intended to be stupid constructors, which just | 4 | //! Note that all functions here intended to be stupid constructors, which just |
5 | //! assemble a finish node from immediate children. If you want to do something | 5 | //! assemble a finish node from immediate children. If you want to do something |
6 | //! smarter than that, it probably doesn't belong in this module. | 6 | //! smarter than that, it belongs to the `ext` submodule. |
7 | //! | 7 | //! |
8 | //! Keep in mind that `from_text` functions should be kept private. The public | 8 | //! Keep in mind that `from_text` functions should be kept private. The public |
9 | //! API should require to assemble every node piecewise. The trick of | 9 | //! API should require to assemble every node piecewise. The trick of |
@@ -14,13 +14,49 @@ use stdx::{format_to, never}; | |||
14 | 14 | ||
15 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken}; | 15 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken}; |
16 | 16 | ||
17 | /// While the parent module defines basic atomic "constructors", the `ext` | ||
18 | /// module defines shortcuts for common things. | ||
19 | /// | ||
20 | /// It's named `ext` rather than `shortcuts` just to keep it short. | ||
21 | pub mod ext { | ||
22 | use super::*; | ||
23 | |||
24 | pub fn ident_path(ident: &str) -> ast::Path { | ||
25 | path_unqualified(path_segment(name_ref(ident))) | ||
26 | } | ||
27 | |||
28 | pub fn expr_unreachable() -> ast::Expr { | ||
29 | expr_from_text("unreachable!()") | ||
30 | } | ||
31 | pub fn expr_todo() -> ast::Expr { | ||
32 | expr_from_text("todo!()") | ||
33 | } | ||
34 | |||
35 | pub fn ty_bool() -> ast::Type { | ||
36 | ty_path(ident_path("bool")) | ||
37 | } | ||
38 | pub fn ty_option(t: ast::Type) -> ast::Type { | ||
39 | ty_from_text(&format!("Option<{}>", t)) | ||
40 | } | ||
41 | pub fn ty_result(t: ast::Type, e: ast::Type) -> ast::Type { | ||
42 | ty_from_text(&format!("Result<{}, {}>", t, e)) | ||
43 | } | ||
44 | } | ||
45 | |||
17 | pub fn name(text: &str) -> ast::Name { | 46 | pub fn name(text: &str) -> ast::Name { |
18 | ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text)) | 47 | ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text)) |
19 | } | 48 | } |
20 | |||
21 | pub fn name_ref(text: &str) -> ast::NameRef { | 49 | pub fn name_ref(text: &str) -> ast::NameRef { |
22 | ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text)) | 50 | ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text)) |
23 | } | 51 | } |
52 | fn raw_ident_esc(ident: &str) -> &'static str { | ||
53 | let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some(); | ||
54 | if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") { | ||
55 | "r#" | ||
56 | } else { | ||
57 | "" | ||
58 | } | ||
59 | } | ||
24 | 60 | ||
25 | pub fn lifetime(text: &str) -> ast::Lifetime { | 61 | pub fn lifetime(text: &str) -> ast::Lifetime { |
26 | let mut text = text; | 62 | let mut text = text; |
@@ -32,15 +68,6 @@ pub fn lifetime(text: &str) -> ast::Lifetime { | |||
32 | ast_from_text(&format!("fn f<{}>() {{ }}", text)) | 68 | ast_from_text(&format!("fn f<{}>() {{ }}", text)) |
33 | } | 69 | } |
34 | 70 | ||
35 | fn raw_ident_esc(ident: &str) -> &'static str { | ||
36 | let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some(); | ||
37 | if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") { | ||
38 | "r#" | ||
39 | } else { | ||
40 | "" | ||
41 | } | ||
42 | } | ||
43 | |||
44 | // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la | 71 | // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la |
45 | // `expr_xxx`. | 72 | // `expr_xxx`. |
46 | pub fn ty(text: &str) -> ast::Type { | 73 | pub fn ty(text: &str) -> ast::Type { |
@@ -49,9 +76,6 @@ pub fn ty(text: &str) -> ast::Type { | |||
49 | pub fn ty_unit() -> ast::Type { | 76 | pub fn ty_unit() -> ast::Type { |
50 | ty_from_text("()") | 77 | ty_from_text("()") |
51 | } | 78 | } |
52 | pub fn ty_bool() -> ast::Type { | ||
53 | ty_path(path_unqualified(path_segment(name_ref("bool")))) | ||
54 | } | ||
55 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | 79 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { |
56 | let mut count: usize = 0; | 80 | let mut count: usize = 0; |
57 | let mut contents = types.into_iter().inspect(|_| count += 1).join(", "); | 81 | let mut contents = types.into_iter().inspect(|_| count += 1).join(", "); |
@@ -61,11 +85,6 @@ pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | |||
61 | 85 | ||
62 | ty_from_text(&format!("({})", contents)) | 86 | ty_from_text(&format!("({})", contents)) |
63 | } | 87 | } |
64 | // FIXME: handle path to type | ||
65 | pub fn ty_generic(name: ast::NameRef, types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | ||
66 | let contents = types.into_iter().join(", "); | ||
67 | ty_from_text(&format!("{}<{}>", name, contents)) | ||
68 | } | ||
69 | pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { | 88 | pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { |
70 | ty_from_text(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) }) | 89 | ty_from_text(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) }) |
71 | } | 90 | } |
@@ -107,7 +126,7 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path { | |||
107 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { | 126 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { |
108 | ast_from_text(&format!("{}::{}", qual, segment)) | 127 | ast_from_text(&format!("{}::{}", qual, segment)) |
109 | } | 128 | } |
110 | 129 | // FIXME: path concatenation operation doesn't make sense as AST op. | |
111 | pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path { | 130 | pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path { |
112 | ast_from_text(&format!("{}::{}", first, second)) | 131 | ast_from_text(&format!("{}::{}", first, second)) |
113 | } | 132 | } |
@@ -123,15 +142,14 @@ pub fn path_from_segments( | |||
123 | format!("use {};", segments) | 142 | format!("use {};", segments) |
124 | }) | 143 | }) |
125 | } | 144 | } |
126 | 145 | // FIXME: should not be pub | |
127 | pub fn path_from_text(text: &str) -> ast::Path { | 146 | pub fn path_from_text(text: &str) -> ast::Path { |
128 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) | 147 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) |
129 | } | 148 | } |
130 | 149 | ||
131 | pub fn glob_use_tree() -> ast::UseTree { | 150 | pub fn use_tree_glob() -> ast::UseTree { |
132 | ast_from_text("use *;") | 151 | ast_from_text("use *;") |
133 | } | 152 | } |
134 | |||
135 | pub fn use_tree( | 153 | pub fn use_tree( |
136 | path: ast::Path, | 154 | path: ast::Path, |
137 | use_tree_list: Option<ast::UseTreeList>, | 155 | use_tree_list: Option<ast::UseTreeList>, |
@@ -226,15 +244,6 @@ pub fn expr_literal(text: &str) -> ast::Literal { | |||
226 | pub fn expr_empty_block() -> ast::Expr { | 244 | pub fn expr_empty_block() -> ast::Expr { |
227 | expr_from_text("{}") | 245 | expr_from_text("{}") |
228 | } | 246 | } |
229 | pub fn expr_unimplemented() -> ast::Expr { | ||
230 | expr_from_text("unimplemented!()") | ||
231 | } | ||
232 | pub fn expr_unreachable() -> ast::Expr { | ||
233 | expr_from_text("unreachable!()") | ||
234 | } | ||
235 | pub fn expr_todo() -> ast::Expr { | ||
236 | expr_from_text("todo!()") | ||
237 | } | ||
238 | pub fn expr_path(path: ast::Path) -> ast::Expr { | 247 | pub fn expr_path(path: ast::Path) -> ast::Expr { |
239 | expr_from_text(&path.to_string()) | 248 | expr_from_text(&path.to_string()) |
240 | } | 249 | } |
@@ -463,17 +472,6 @@ pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt { | |||
463 | ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) | 472 | ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) |
464 | } | 473 | } |
465 | 474 | ||
466 | pub fn token(kind: SyntaxKind) -> SyntaxToken { | ||
467 | tokens::SOURCE_FILE | ||
468 | .tree() | ||
469 | .syntax() | ||
470 | .clone_for_update() | ||
471 | .descendants_with_tokens() | ||
472 | .filter_map(|it| it.into_token()) | ||
473 | .find(|it| it.kind() == kind) | ||
474 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | ||
475 | } | ||
476 | |||
477 | pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { | 475 | pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { |
478 | ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) | 476 | ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) |
479 | } | 477 | } |
@@ -611,6 +609,17 @@ fn unroot(n: SyntaxNode) -> SyntaxNode { | |||
611 | SyntaxNode::new_root(n.green().into()) | 609 | SyntaxNode::new_root(n.green().into()) |
612 | } | 610 | } |
613 | 611 | ||
612 | pub fn token(kind: SyntaxKind) -> SyntaxToken { | ||
613 | tokens::SOURCE_FILE | ||
614 | .tree() | ||
615 | .syntax() | ||
616 | .clone_for_update() | ||
617 | .descendants_with_tokens() | ||
618 | .filter_map(|it| it.into_token()) | ||
619 | .find(|it| it.kind() == kind) | ||
620 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | ||
621 | } | ||
622 | |||
614 | pub mod tokens { | 623 | pub mod tokens { |
615 | use once_cell::sync::Lazy; | 624 | use once_cell::sync::Lazy; |
616 | 625 | ||