Generate AST

This commit is contained in:
Aleksey Kladov 2018-08-09 17:43:39 +03:00
parent 36bd28633b
commit d8b2a5efc0
7 changed files with 134 additions and 52 deletions

View File

@ -3,7 +3,7 @@ extern crate libsyntax2;
mod extend_selection;
use libsyntax2::{
SyntaxNodeRef,
SyntaxNodeRef, AstNode,
algo::walk,
SyntaxKind::*,
};

54
src/ast/generated.rs Normal file
View File

@ -0,0 +1,54 @@
use std::sync::Arc;
use {
SyntaxNode, SyntaxRoot, TreeRoot, AstNode,
SyntaxKind::*,
};
#[derive(Debug)]
pub struct File<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
}
impl<R: TreeRoot> AstNode<R> for File<R> {
fn cast(syntax: SyntaxNode<R>) -> Option<Self> {
match syntax.kind() {
FILE => Some(File { syntax }),
_ => None,
}
}
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
#[derive(Debug)]
pub struct FnItem<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
}
impl<R: TreeRoot> AstNode<R> for FnItem<R> {
fn cast(syntax: SyntaxNode<R>) -> Option<Self> {
match syntax.kind() {
FN_ITEM => Some(FnItem { syntax }),
_ => None,
}
}
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
#[derive(Debug)]
pub struct Name<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
}
impl<R: TreeRoot> AstNode<R> for Name<R> {
fn cast(syntax: SyntaxNode<R>) -> Option<Self> {
match syntax.kind() {
NAME => Some(Name { syntax }),
_ => None,
}
}
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}

22
src/ast/generated.rs.tera Normal file
View File

@ -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 }}<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
}
impl<R: TreeRoot> AstNode<R> for {{ Name }}<R> {
fn cast(syntax: SyntaxNode<R>) -> Option<Self> {
match syntax.kind() {
{{ node.kind }} => Some({{ Name }} { syntax }),
_ => None,
}
}
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
{% endfor %}

View File

@ -1,57 +1,41 @@
mod generated;
use std::sync::Arc;
use {
SyntaxNode, SyntaxRoot, TreeRoot,
SyntaxKind::*,
};
pub use self::generated::*;
#[derive(Debug)]
pub struct File<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
pub trait AstNode<R: TreeRoot>: Sized {
fn cast(syntax: SyntaxNode<R>) -> Option<Self>;
fn syntax(&self) -> &SyntaxNode<R>;
}
#[derive(Debug)]
pub struct Function<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
}
#[derive(Debug)]
pub struct Name<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
}
impl File<Arc<SyntaxRoot>> {
pub fn parse(text: &str) -> Self {
File {
syntax: ::parse(text),
}
File::cast(::parse(text)).unwrap()
}
}
impl<R: TreeRoot> File<R> {
pub fn functions<'a>(&'a self) -> impl Iterator<Item = Function<R>> + 'a {
self.syntax
pub fn functions<'a>(&'a self) -> impl Iterator<Item = FnItem<R>> + 'a {
self.syntax()
.children()
.filter(|node| node.kind() == FN_ITEM)
.map(|node| Function { syntax: node })
.filter_map(FnItem::cast)
}
}
impl<R: TreeRoot> Function<R> {
pub fn syntax(&self) -> SyntaxNode<R> {
self.syntax.clone()
}
impl<R: TreeRoot> FnItem<R> {
pub fn name(&self) -> Option<Name<R>> {
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<R: TreeRoot> Function<R> {
impl<R: TreeRoot> Name<R> {
pub fn text(&self) -> String {
self.syntax.text()
}
}
impl<R: TreeRoot> File<R> {
pub fn syntax(&self) -> SyntaxNode<R> {
self.syntax.clone()
self.syntax().text()
}
}

View File

@ -212,5 +212,16 @@ Grammar(
"PARAM",
"SELF_PARAM",
"ARG_LIST",
],
ast: [
(
kind: "FILE"
),
(
kind: "FN_ITEM"
),
(
kind: "NAME"
),
]
)

View File

@ -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},

View File

@ -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<String> {
let grammar = grammar()?;
let template = fs::read_to_string(SYNTAX_KINDS_TEMPLATE)?;
fn render_template(template: &str) -> Result<String> {
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::<String>()
.into())
});
let ret = tera
.render("grammar", &grammar)
.map_err(|e| format_err!("template error: {:?}", e))?;
@ -94,12 +119,6 @@ fn get_kinds() -> Result<String> {
}
}
fn grammar() -> Result<ron::value::Value> {
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))?;