diff --git a/libeditor/src/lib.rs b/libeditor/src/lib.rs index 387859d4aff..e7cc6a696e3 100644 --- a/libeditor/src/lib.rs +++ b/libeditor/src/lib.rs @@ -3,7 +3,7 @@ extern crate libsyntax2; mod extend_selection; use libsyntax2::{ - SyntaxNodeRef, + SyntaxNodeRef, AstNode, algo::walk, SyntaxKind::*, }; diff --git a/src/ast/generated.rs b/src/ast/generated.rs new file mode 100644 index 00000000000..612b04f8678 --- /dev/null +++ b/src/ast/generated.rs @@ -0,0 +1,54 @@ +use std::sync::Arc; +use { + SyntaxNode, SyntaxRoot, TreeRoot, AstNode, + SyntaxKind::*, +}; + + +#[derive(Debug)] +pub struct File> { + syntax: SyntaxNode, +} + +impl AstNode for File { + fn cast(syntax: SyntaxNode) -> Option { + match syntax.kind() { + FILE => Some(File { syntax }), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} + + +#[derive(Debug)] +pub struct FnItem> { + syntax: SyntaxNode, +} + +impl AstNode for FnItem { + fn cast(syntax: SyntaxNode) -> Option { + match syntax.kind() { + FN_ITEM => Some(FnItem { syntax }), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} + + +#[derive(Debug)] +pub struct Name> { + syntax: SyntaxNode, +} + +impl AstNode for Name { + fn cast(syntax: SyntaxNode) -> Option { + match syntax.kind() { + NAME => Some(Name { syntax }), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} + diff --git a/src/ast/generated.rs.tera b/src/ast/generated.rs.tera new file mode 100644 index 00000000000..f2559383a2d --- /dev/null +++ b/src/ast/generated.rs.tera @@ -0,0 +1,22 @@ +use std::sync::Arc; +use { + SyntaxNode, SyntaxRoot, TreeRoot, AstNode, + SyntaxKind::*, +}; +{% for node in ast %} +{% set Name = node.kind | camel %} +#[derive(Debug)] +pub struct {{ Name }}> { + syntax: SyntaxNode, +} + +impl AstNode for {{ Name }} { + fn cast(syntax: SyntaxNode) -> Option { + match syntax.kind() { + {{ node.kind }} => Some({{ Name }} { syntax }), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +{% endfor %} diff --git a/src/ast.rs b/src/ast/mod.rs similarity index 59% rename from src/ast.rs rename to src/ast/mod.rs index a595b9324de..dc7e006c94d 100644 --- a/src/ast.rs +++ b/src/ast/mod.rs @@ -1,57 +1,41 @@ +mod generated; + use std::sync::Arc; use { SyntaxNode, SyntaxRoot, TreeRoot, SyntaxKind::*, }; +pub use self::generated::*; -#[derive(Debug)] -pub struct File> { - syntax: SyntaxNode, +pub trait AstNode: Sized { + fn cast(syntax: SyntaxNode) -> Option; + fn syntax(&self) -> &SyntaxNode; } -#[derive(Debug)] -pub struct Function> { - syntax: SyntaxNode, -} - -#[derive(Debug)] -pub struct Name> { - syntax: SyntaxNode, -} - - impl File> { pub fn parse(text: &str) -> Self { - File { - syntax: ::parse(text), - } + File::cast(::parse(text)).unwrap() } } impl File { - pub fn functions<'a>(&'a self) -> impl Iterator> + 'a { - self.syntax + pub fn functions<'a>(&'a self) -> impl Iterator> + 'a { + self.syntax() .children() - .filter(|node| node.kind() == FN_ITEM) - .map(|node| Function { syntax: node }) + .filter_map(FnItem::cast) } } -impl Function { - pub fn syntax(&self) -> SyntaxNode { - self.syntax.clone() - } - +impl FnItem { pub fn name(&self) -> Option> { - self.syntax + self.syntax() .children() - .filter(|node| node.kind() == NAME) - .map(|node| Name { syntax: node }) + .filter_map(Name::cast) .next() } pub fn has_atom_attr(&self, atom: &str) -> bool { - self.syntax + self.syntax() .children() .filter(|node| node.kind() == ATTR) .any(|attr| { @@ -81,14 +65,6 @@ impl Function { impl Name { pub fn text(&self) -> String { - self.syntax.text() - } -} - - - -impl File { - pub fn syntax(&self) -> SyntaxNode { - self.syntax.clone() + self.syntax().text() } } diff --git a/src/grammar.ron b/src/grammar.ron index 0443dd7984e..b6a870d84e4 100644 --- a/src/grammar.ron +++ b/src/grammar.ron @@ -212,5 +212,16 @@ Grammar( "PARAM", "SELF_PARAM", "ARG_LIST", + ], + ast: [ + ( + kind: "FILE" + ), + ( + kind: "FN_ITEM" + ), + ( + kind: "NAME" + ), ] ) diff --git a/src/lib.rs b/src/lib.rs index 91b0c9c5525..d1e690bb28a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ mod yellow; pub mod utils; pub use { - ast::File, + ast::{AstNode, File}, lexer::{tokenize, Token}, syntax_kinds::SyntaxKind, text_unit::{TextRange, TextUnit}, diff --git a/tools/src/main.rs b/tools/src/main.rs index 714158f5548..b5c966f74c4 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -23,6 +23,8 @@ const INLINE_TESTS_DIR: &str = "tests/data/parser/inline"; const GRAMMAR: &str = "./src/grammar.ron"; const SYNTAX_KINDS: &str = "./src/syntax_kinds/generated.rs"; const SYNTAX_KINDS_TEMPLATE: &str = "./src/syntax_kinds/generated.rs.tera"; +const AST: &str = "./src/ast/generated.rs"; +const AST_TEMPLATE: &str = "./src/ast/generated.rs.tera"; fn main() -> Result<()> { let matches = App::new("tasks") @@ -47,10 +49,16 @@ fn main() -> Result<()> { fn run_gen_command(name: &str, verify: bool) -> Result<()> { match name { - "gen-kinds" => update(Path::new(SYNTAX_KINDS), &get_kinds()?, verify), - "gen-tests" => gen_tests(verify), + "gen-kinds" => { + update(Path::new(SYNTAX_KINDS), &render_template(SYNTAX_KINDS_TEMPLATE)?, verify)?; + update(Path::new(AST), &render_template(AST_TEMPLATE)?, verify)?; + }, + "gen-tests" => { + gen_tests(verify)? + }, _ => unreachable!(), } + Ok(()) } fn update(path: &Path, contents: &str, verify: bool) -> Result<()> { @@ -68,13 +76,30 @@ fn update(path: &Path, contents: &str, verify: bool) -> Result<()> { Ok(()) } -fn get_kinds() -> Result { - let grammar = grammar()?; - let template = fs::read_to_string(SYNTAX_KINDS_TEMPLATE)?; +fn render_template(template: &str) -> Result { + let grammar: ron::value::Value = { + let text = fs::read_to_string(GRAMMAR)?; + ron::de::from_str(&text)? + }; + let template = fs::read_to_string(template)?; let mut tera = tera::Tera::default(); tera.add_raw_template("grammar", &template) .map_err(|e| format_err!("template error: {:?}", e))?; tera.register_global_function("concat", Box::new(concat)); + tera.register_filter("camel", |arg, _| { + Ok(arg.as_str().unwrap() + .split("_") + .flat_map(|word| { + word.chars() + .next().unwrap() + .to_uppercase() + .chain( + word.chars().skip(1).flat_map(|c| c.to_lowercase()) + ) + }) + .collect::() + .into()) + }); let ret = tera .render("grammar", &grammar) .map_err(|e| format_err!("template error: {:?}", e))?; @@ -94,12 +119,6 @@ fn get_kinds() -> Result { } } -fn grammar() -> Result { - let text = fs::read_to_string(GRAMMAR)?; - let ret = ron::de::from_str(&text)?; - Ok(ret) -} - fn gen_tests(verify: bool) -> Result<()> { let tests = tests_from_dir(Path::new(GRAMMAR_DIR))?;