From 8e90799e2791a322f6df5c435280433e2c381773 Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Thu, 17 Jul 2025 15:48:26 +0200 Subject: [PATCH] =?UTF-8?q?feat(generate):=C2=A0Support=20for=20--stage=3D?= =?UTF-8?q?json/parser/lib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/cli/src/main.rs | 31 ++++++++++++++++++++++++------- crates/generate/src/generate.rs | 11 ++++++----- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 60314d28..cb849f09 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -85,6 +85,17 @@ struct Init { pub grammar_path: Option, } +#[derive(Clone, Debug, Default, ValueEnum, PartialEq, Eq)] +enum GenerationStage { + /// Generate `grammar.json` and `node-types.json` + Json, + /// Generate `parser.c` and related files + #[default] + Parser, + /// Compile to a library + Lib, +} + #[derive(Args)] #[command(alias = "gen", alias = "g")] struct Generate { @@ -107,8 +118,12 @@ struct Generate { ) )] pub abi_version: Option, - /// Compile all defined languages in the current dir - #[arg(long, short = 'b')] + /// Which generation stage to end after + #[arg(long)] + #[clap(value_enum, default_value_t=GenerationStage::Parser)] + pub stage: GenerationStage, + /// Deprecated: use --stage=lib. + #[arg(long, short = 'b', conflicts_with = "stage")] pub build: bool, /// Compile a parser in debug mode #[arg(long, short = '0')] @@ -133,9 +148,6 @@ struct Generate { default_value = "node" )] pub js_runtime: Option, - /// Only generate `grammar.json` by evaluating `grammar.js`, but do not generate `parser.c` and related files afterwards - #[arg(long)] - pub evaluate_only: bool, } #[derive(Args)] @@ -803,6 +815,11 @@ impl Generate { version.parse().expect("invalid abi version flag") } }); + if self.build { + // TODO: remove the `--build` argument in 0.27 + // TODO: migrate to `warn!` once https://github.com/tree-sitter/tree-sitter/pull/4604 is merged + eprintln!("Warning: --build is deprecated, use --stage=lib instead"); + } if let Err(err) = tree_sitter_generate::generate_parser_in_directory( current_dir, self.output.as_deref(), @@ -810,7 +827,7 @@ impl Generate { abi_version, self.report_states_for_rule.as_deref(), self.js_runtime.as_deref(), - self.evaluate_only, + self.stage != GenerationStage::Json, ) { if self.json { eprintln!("{}", serde_json::to_string_pretty(&err)?); @@ -821,7 +838,7 @@ impl Generate { Err(anyhow!(err.to_string())).with_context(|| "Error when generating parser")?; } } - if self.build { + if self.stage == GenerationStage::Lib || self.build { if let Some(path) = self.libdir { loader = loader::Loader::with_parser_lib_path(path); } diff --git a/crates/generate/src/generate.rs b/crates/generate/src/generate.rs index e954ffc0..ffc71587 100644 --- a/crates/generate/src/generate.rs +++ b/crates/generate/src/generate.rs @@ -196,9 +196,8 @@ where let src_path = out_path.map_or_else(|| repo_path.join("src"), |p| p.into()); let header_path = src_path.join("tree_sitter"); - // Ensure that the output directories exist. + // Ensure that the output directory exists fs::create_dir_all(&src_path)?; - fs::create_dir_all(&header_path)?; if grammar_path.file_name().unwrap() != "grammar.json" { fs::write(src_path.join("grammar.json"), &grammar_json).map_err(|e| { @@ -210,13 +209,14 @@ where } // If our job is only to generate `grammar.json` and not `parser.c`, stop here. + let input_grammar = parse_grammar(&grammar_json)?; + if !generate_parser { + let node_types_json = generate_node_types_from_grammar(&input_grammar)?.node_types_json; + write_file(&src_path.join("node-types.json"), node_types_json)?; return Ok(()); } - // Parse and preprocess the grammar. - let input_grammar = parse_grammar(&grammar_json)?; - let semantic_version = read_grammar_version(&repo_path)?; if semantic_version.is_none() && abi_version > ABI_VERSION_MIN { @@ -238,6 +238,7 @@ where write_file(&src_path.join("parser.c"), c_code)?; write_file(&src_path.join("node-types.json"), node_types_json)?; + fs::create_dir_all(&header_path)?; write_file(&header_path.join("alloc.h"), ALLOC_HEADER)?; write_file(&header_path.join("array.h"), ARRAY_HEADER)?; write_file(&header_path.join("parser.h"), tree_sitter::PARSER_HEADER)?;