Move code generation into a build script

master
Nick Krichevsky 2024-05-17 15:01:41 -04:00
parent 96ca870db0
commit 8cdfbd61a9
4 changed files with 23 additions and 90 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
/target /target
# Autogenerated
/src/ast.rs

View File

@ -4,10 +4,6 @@ version = "0.1.0"
edition = "2021" edition = "2021"
default-run = "jlox-rust" default-run = "jlox-rust"
[[bin]]
name = "astgen"
path = "bin/astgen.rs"
[dependencies] [dependencies]
anyhow = "1.0.83" anyhow = "1.0.83"
itertools = "0.12.1" itertools = "0.12.1"

View File

@ -2,26 +2,31 @@
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
fs::File,
io::{self, Write}, io::{self, Write},
iter, iter,
}; };
fn main() { pub fn main() {
let mut stdout = io::stdout(); let mut ast_file = File::create("./src/ast.rs").expect("could not open src/ast.rs for writing");
do_ast_codegen(&mut ast_file);
}
fn do_ast_codegen<W: Write>(mut output: W) {
// Autogenerated file, clippy can leave us alone // Autogenerated file, clippy can leave us alone
writeln!(stdout, "#![allow(clippy::all)]").unwrap(); writeln!(output, "#![allow(clippy::all)]").unwrap();
writeln!( writeln!(
stdout, output,
"// This file is auto-generated. Do not modify it directly, but rather use bin/astgen.rs.\n" "// This file is auto-generated. Do not modify it directly, but rather modify build.rs.\n"
) )
.unwrap(); .unwrap();
define_imports(&mut stdout, &["crate::lex::Token"]).expect("failed to define imports"); define_imports(&mut output, &["crate::lex::Token"]).expect("failed to define imports");
writeln!(stdout).unwrap(); writeln!(output).unwrap();
define_literals( define_literals(
&mut stdout, &mut output,
"LiteralValue", "LiteralValue",
&BTreeMap::from([ &BTreeMap::from([
("Number", Some("f64")), ("Number", Some("f64")),
@ -32,7 +37,7 @@ fn main() {
]), ]),
) )
.expect("failed to generate literals"); .expect("failed to generate literals");
writeln!(stdout).unwrap(); writeln!(output).unwrap();
let expr_types = BTreeMap::from([ let expr_types = BTreeMap::from([
( (
@ -44,21 +49,21 @@ fn main() {
("Literal", "value: LiteralValue"), ("Literal", "value: LiteralValue"),
]); ]);
define_ast(&mut stdout, "Expr", &expr_types).expect("failed to generate ast values"); define_ast(&mut output, "Expr", &expr_types).expect("failed to generate ast values");
writeln!(stdout).unwrap(); writeln!(output).unwrap();
define_visitor_trait(&mut stdout, "ExprVisitor", "Expr", &expr_types) define_visitor_trait(&mut output, "ExprVisitor", "Expr", &expr_types)
.expect("failed to generate visitor trait"); .expect("failed to generate visitor trait");
writeln!(stdout).unwrap(); writeln!(output).unwrap();
let statement_types = BTreeMap::from([ let statement_types = BTreeMap::from([
("Expression", "expression: Expr"), ("Expression", "expression: Expr"),
("Print", "expression: Expr"), ("Print", "expression: Expr"),
]); ]);
define_ast(&mut stdout, "Stmt", &statement_types).expect("failed to generate ast values"); define_ast(&mut output, "Stmt", &statement_types).expect("failed to generate ast values");
writeln!(stdout).unwrap(); writeln!(output).unwrap();
define_visitor_trait(&mut stdout, "StmtVisitor", "Stmt", &statement_types) define_visitor_trait(&mut output, "StmtVisitor", "Stmt", &statement_types)
.expect("failed to generate visitor trait"); .expect("failed to generate visitor trait");
} }

View File

@ -1,70 +0,0 @@
#![allow(clippy::all)]
// This file is auto-generated. Do not modify it directly, but rather use bin/astgen.rs.
use crate::lex::Token;
#[derive(Debug, Clone)]
pub enum LiteralValue {
False,
Nil,
Number(f64),
String(String),
True,
}
#[derive(Debug, Clone)]
pub enum Expr {
Binary {
left: Box<Expr>,
operator: Token,
right: Box<Expr>,
},
Grouping {
expr: Box<Expr>,
},
Literal {
value: LiteralValue,
},
Unary {
expr: Box<Expr>,
operator: Token,
},
}
pub trait ExprVisitor<T> {
fn visit_expr(&mut self, expr: &Expr) -> T {
match expr {
Expr::Binary { left, operator, right } => self.visit_binary(left.as_ref(), &operator, right.as_ref()),
Expr::Grouping { expr } => self.visit_grouping(expr.as_ref()),
Expr::Literal { value } => self.visit_literal(&value),
Expr::Unary { expr, operator } => self.visit_unary(expr.as_ref(), &operator),
}
}
fn visit_binary(&mut self, left: &Expr, operator: &Token, right: &Expr) -> T;
fn visit_grouping(&mut self, expr: &Expr) -> T;
fn visit_literal(&mut self, value: &LiteralValue) -> T;
fn visit_unary(&mut self, expr: &Expr, operator: &Token) -> T;
}
#[derive(Debug, Clone)]
pub enum Stmt {
Expression {
expression: Expr,
},
Print {
expression: Expr,
},
}
pub trait StmtVisitor<T> {
fn visit_stmt(&mut self, stmt: &Stmt) -> T {
match stmt {
Stmt::Expression { expression } => self.visit_expression(&expression),
Stmt::Print { expression } => self.visit_print(&expression),
}
}
fn visit_expression(&mut self, expression: &Expr) -> T;
fn visit_print(&mut self, expression: &Expr) -> T;
}