Rust: write generated schema into schema/ast.py

This commit is contained in:
Arthur Baars
2024-09-19 15:03:29 +02:00
parent f4071ddb28
commit b2bddd3415
2 changed files with 439 additions and 554 deletions

View File

@@ -1,3 +1,4 @@
use std::io::Write;
use std::{fs, path::PathBuf}; use std::{fs, path::PathBuf};
pub mod codegen; pub mod codegen;
@@ -47,7 +48,13 @@ fn to_lower_snake_case(s: &str) -> String {
buf buf
} }
fn print_schema(grammar: &AstSrc, super_types: BTreeMap<String, BTreeSet<String>>) { fn write_schema(
grammar: &AstSrc,
super_types: BTreeMap<String, BTreeSet<String>>,
) -> std::io::Result<String> {
let mut buf: Vec<u8> = Vec::new();
writeln!(buf, "from .prelude import *\n")?;
for node in &grammar.enums { for node in &grammar.enums {
let super_classses = if let Some(cls) = super_types.get(&node.name) { let super_classses = if let Some(cls) = super_types.get(&node.name) {
let super_classes: Vec<String> = cls.iter().map(|x| class_name(x)).collect(); let super_classes: Vec<String> = cls.iter().map(|x| class_name(x)).collect();
@@ -55,9 +62,9 @@ fn print_schema(grammar: &AstSrc, super_types: BTreeMap<String, BTreeSet<String>
} else { } else {
"AstNode".to_owned() "AstNode".to_owned()
}; };
println!("class {}({}):", class_name(&node.name), super_classses); writeln!(buf, "class {}({}):", class_name(&node.name), super_classses)?;
println!(" pass"); writeln!(buf, " pass")?;
println!(""); writeln!(buf, "")?;
} }
for node in &grammar.nodes { for node in &grammar.nodes {
let super_classses = if let Some(cls) = super_types.get(&node.name) { let super_classses = if let Some(cls) = super_types.get(&node.name) {
@@ -66,7 +73,7 @@ fn print_schema(grammar: &AstSrc, super_types: BTreeMap<String, BTreeSet<String>
} else { } else {
"AstNode".to_owned() "AstNode".to_owned()
}; };
println!("class {}({}):", class_name(&node.name), super_classses); writeln!(buf, "class {}({}):", class_name(&node.name), super_classses)?;
let mut empty = true; let mut empty = true;
for field in get_fields(node) { for field in get_fields(node) {
if field.tp == "SyntaxToken" { if field.tp == "SyntaxToken" {
@@ -75,10 +82,11 @@ fn print_schema(grammar: &AstSrc, super_types: BTreeMap<String, BTreeSet<String>
empty = false; empty = false;
if field.tp == "string" { if field.tp == "string" {
println!( writeln!(
buf,
" {}: optional[string]", " {}: optional[string]",
property_name(&node.name, &field.name), property_name(&node.name, &field.name),
); )?;
} else { } else {
let list = field.is_many; let list = field.is_many;
let (o, c) = if list { let (o, c) = if list {
@@ -86,20 +94,22 @@ fn print_schema(grammar: &AstSrc, super_types: BTreeMap<String, BTreeSet<String>
} else { } else {
("optional[", "]") ("optional[", "]")
}; };
println!( writeln!(
buf,
" {}: {}\"{}\"{} | child", " {}: {}\"{}\"{} | child",
property_name(&node.name, &field.name), property_name(&node.name, &field.name),
o, o,
class_name(&field.tp), class_name(&field.tp),
c c
); )?;
}; };
} }
if empty { if empty {
println!(" pass"); writeln!(buf, " pass")?;
} }
println!(""); writeln!(buf, "")?;
} }
Ok(String::from_utf8_lossy(&buf).to_string())
} }
struct FieldInfo { struct FieldInfo {
@@ -390,40 +400,45 @@ fn get_fields(node: &AstNodeSrc) -> Vec<FieldInfo> {
result result
} }
fn print_extractor(grammar: &AstSrc) { fn write_extractor(grammar: &AstSrc) -> std::io::Result<String> {
let mut buf: Vec<u8> = Vec::new();
for node in &grammar.enums { for node in &grammar.enums {
let type_name = &node.name; let type_name = &node.name;
let class_name = class_name(&node.name); let class_name = class_name(&node.name);
println!( writeln!(
buf,
" fn emit_{}(&mut self, node: ast::{}) -> Label<generated::{}> {{", " fn emit_{}(&mut self, node: ast::{}) -> Label<generated::{}> {{",
to_lower_snake_case(type_name), to_lower_snake_case(type_name),
type_name, type_name,
class_name class_name
); )?;
println!(" match node {{"); writeln!(buf, " match node {{")?;
for variant in &node.variants { for variant in &node.variants {
println!( writeln!(
buf,
" ast::{}::{}(inner) => self.emit_{}(inner).into(),", " ast::{}::{}(inner) => self.emit_{}(inner).into(),",
type_name, type_name,
variant, variant,
to_lower_snake_case(variant) to_lower_snake_case(variant)
); )?;
} }
println!(" }}"); writeln!(buf, " }}")?;
println!(" }}\n"); writeln!(buf, " }}\n")?;
} }
for node in &grammar.nodes { for node in &grammar.nodes {
let type_name = &node.name; let type_name = &node.name;
let class_name = class_name(&node.name); let class_name = class_name(&node.name);
println!( writeln!(
buf,
" fn emit_{}(&mut self, node: ast::{}) -> Label<generated::{}> {{", " fn emit_{}(&mut self, node: ast::{}) -> Label<generated::{}> {{",
to_lower_snake_case(type_name), to_lower_snake_case(type_name),
type_name, type_name,
class_name class_name
); )?;
for field in get_fields(&node) { for field in get_fields(&node) {
if &field.tp == "SyntaxToken" { if &field.tp == "SyntaxToken" {
continue; continue;
@@ -433,45 +448,53 @@ fn print_extractor(grammar: &AstSrc) {
let struct_field_name = &field.name; let struct_field_name = &field.name;
let class_field_name = property_name(&node.name, &field.name); let class_field_name = property_name(&node.name, &field.name);
if field.tp == "string" { if field.tp == "string" {
println!(" let {} = node.try_get_text();", class_field_name,); writeln!(
buf,
" let {} = node.try_get_text();",
class_field_name,
)?;
} else if field.is_many { } else if field.is_many {
println!( writeln!(
buf,
" let {} = node.{}().map(|x| self.emit_{}(x)).collect();", " let {} = node.{}().map(|x| self.emit_{}(x)).collect();",
class_field_name, class_field_name,
struct_field_name, struct_field_name,
to_lower_snake_case(type_name) to_lower_snake_case(type_name)
); )?;
} else { } else {
println!( writeln!(
buf,
" let {} = node.{}().map(|x| self.emit_{}(x));", " let {} = node.{}().map(|x| self.emit_{}(x));",
class_field_name, class_field_name,
struct_field_name, struct_field_name,
to_lower_snake_case(type_name) to_lower_snake_case(type_name)
); )?;
} }
} }
println!( writeln!(
buf,
" let label = self.trap.emit(generated::{} {{", " let label = self.trap.emit(generated::{} {{",
class_name class_name
); )?;
println!(" id: TrapId::Star,"); writeln!(buf, " id: TrapId::Star,")?;
for field in get_fields(&node) { for field in get_fields(&node) {
if field.tp == "SyntaxToken" { if field.tp == "SyntaxToken" {
continue; continue;
} }
let class_field_name: String = property_name(&node.name, &field.name); let class_field_name: String = property_name(&node.name, &field.name);
println!(" {},", class_field_name); writeln!(buf, " {},", class_field_name)?;
} }
println!(" }});"); writeln!(buf, " }});")?;
println!(" self.emit_location(label, node);"); writeln!(buf, " self.emit_location(label, node);")?;
println!(" label"); writeln!(buf, " label")?;
println!(" }}\n"); writeln!(buf, " }}\n")?;
} }
Ok(String::from_utf8_lossy(&buf).into_owned())
} }
fn main() { fn main() -> std::io::Result<()> {
let grammar: Grammar = fs::read_to_string(project_root().join("generate-schema/rust.ungram")) let grammar: Grammar = fs::read_to_string(project_root().join("generate-schema/rust.ungram"))
.unwrap() .unwrap()
.parse() .parse()
@@ -498,6 +521,15 @@ fn main() {
let super_class_y = super_types.get(&y.name).into_iter().flatten().max(); let super_class_y = super_types.get(&y.name).into_iter().flatten().max();
super_class_x.cmp(&super_class_y).then(x.name.cmp(&y.name)) super_class_x.cmp(&super_class_y).then(x.name.cmp(&y.name))
}); });
//print_schema(&grammar, super_types); let schema = write_schema(&grammar, super_types)?;
print_extractor(&grammar); let schema_path = PathBuf::from("../schema/ast.py");
let extractor = write_extractor(&grammar)?;
print!("{}", extractor);
codegen::ensure_file_contents(
crate::flags::CodegenType::Grammar,
&schema_path,
&schema,
false,
);
Ok(())
} }

File diff suppressed because it is too large Load Diff