Skip to content

Commit f77a766

Browse files
committed
feat: add Grammar2 support to CLI with --grammar1 fallback
- Default to Grammar2 runtime (GrammarInterpreter with named bindings) - Add --grammar1 flag to use legacy ZpegCompiler + pest_vm runtime - Add load_grammar2() function using Grammar2::from_source() - Add zyntax_embed dependency for Grammar2 access - Update documentation with Grammar2/Grammar1 workflow descriptions
1 parent ea44729 commit f77a766

5 files changed

Lines changed: 116 additions & 3 deletions

File tree

crates/zyntax_cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ path = "src/main.rs"
1010
[dependencies]
1111
zyntax_typed_ast = { path = "../typed_ast" }
1212
zyntax_compiler = { path = "../compiler", features = ["cranelift-backend"] }
13+
zyntax_embed = { path = "../zyntax_embed" }
1314
zyn_peg = { path = "../zyn_peg" }
1415
pest = "2.7"
1516
pest_derive = "2.7"

crates/zyntax_cli/src/cli.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ pub enum Commands {
108108
#[arg(long)]
109109
no_cache: bool,
110110

111+
/// Use legacy Grammar1 runtime (ZpegCompiler + pest_vm)
112+
/// Default is Grammar2 (GrammarInterpreter with named bindings)
113+
#[arg(long)]
114+
grammar1: bool,
115+
111116
// === Runtime Library Options ===
112117

113118
/// ZPack archive(s) to load runtime symbols from (.zpack files)
@@ -277,6 +282,7 @@ impl Commands {
277282
import_map,
278283
cache_dir,
279284
no_cache,
285+
grammar1,
280286
packs,
281287
static_libs,
282288
} => Some(CompileArgs {
@@ -295,6 +301,7 @@ impl Commands {
295301
import_map: import_map.clone(),
296302
cache_dir: cache_dir.clone(),
297303
no_cache: *no_cache,
304+
grammar1: *grammar1,
298305
packs: packs.clone(),
299306
static_libs: static_libs.clone(),
300307
}),
@@ -355,6 +362,7 @@ pub struct CompileArgs {
355362
pub import_map: Option<PathBuf>,
356363
pub cache_dir: Option<PathBuf>,
357364
pub no_cache: bool,
365+
pub grammar1: bool,
358366
pub packs: Vec<PathBuf>,
359367
pub static_libs: Vec<PathBuf>,
360368
}

crates/zyntax_cli/src/commands.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ pub fn compile(
108108
import_map: Option<PathBuf>,
109109
cache_dir: Option<PathBuf>,
110110
no_cache: bool,
111+
grammar1: bool,
111112
packs: Vec<PathBuf>,
112113
static_libs: Vec<PathBuf>,
113114
verbose: bool,
@@ -167,7 +168,19 @@ pub fn compile(
167168
InputFormat::ZynGrammar => {
168169
let grammar_path = grammar.ok_or("--grammar is required for zyn format")?;
169170
let source_path = source.ok_or("--source is required for zyn format")?;
170-
formats::zyn_grammar::load(&grammar_path, &source_path, verbose)?
171+
if grammar1 {
172+
// Use legacy Grammar1 runtime (ZpegCompiler + pest_vm)
173+
if verbose {
174+
println!("{} Using legacy Grammar1 runtime", "info:".blue());
175+
}
176+
formats::zyn_grammar::load(&grammar_path, &source_path, verbose)?
177+
} else {
178+
// Use Grammar2 runtime (GrammarInterpreter with named bindings)
179+
if verbose {
180+
println!("{} Using Grammar2 runtime (default)", "info:".blue());
181+
}
182+
formats::zyn_grammar::load_grammar2(&grammar_path, &source_path, verbose)?
183+
}
171184
}
172185
};
173186

crates/zyntax_cli/src/formats/zyn_grammar.rs

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
//! ZynPEG grammar-based compilation format
22
//!
3-
//! Workflow (No Rust compilation required!):
3+
//! Two runtimes are supported:
4+
//!
5+
//! ## Grammar1 (Legacy)
46
//! 1. Parse .zyn grammar with ZynPEG
57
//! 2. Compile grammar to zpeg module (pest grammar + JSON commands)
68
//! 3. Parse source code with pest_vm (dynamic grammar)
79
//! 4. Execute JSON commands via host functions to build TypedAST
810
//! 5. Lower TypedAST to HIR and compile
911
//!
12+
//! ## Grammar2 (Default)
13+
//! 1. Parse .zyn grammar with ZynPEG to GrammarIR
14+
//! 2. Use GrammarInterpreter to parse source directly to TypedProgram
15+
//! 3. Lower TypedProgram to HIR and compile
16+
//!
1017
//! Usage:
1118
//! ```bash
12-
//! zyntax compile --source my_code.lang --grammar my_lang.zyn --format zyn -o output --run
19+
//! # Default (Grammar2)
20+
//! zyntax compile --source my_code.lang --grammar my_lang.zyn --format zyn -o output --jit
21+
//!
22+
//! # Legacy (Grammar1)
23+
//! zyntax compile --source my_code.lang --grammar my_lang.zyn --format zyn --grammar1 -o output --jit
1324
//! ```
1425
1526
use colored::Colorize;
@@ -19,6 +30,7 @@ use std::sync::{Arc, Mutex};
1930
use zyntax_compiler::hir::HirModule;
2031
use zyntax_compiler::lowering::{AstLowering, LoweringConfig, LoweringContext};
2132
use zyntax_typed_ast::{AstArena, InternedString, TypeRegistry, TypedProgram};
33+
use zyntax_embed::Grammar2;
2234

2335
/// Load and compile source using a ZynPEG grammar
2436
pub fn load(
@@ -101,6 +113,84 @@ pub fn load(
101113
Ok(hir_module)
102114
}
103115

116+
/// Load and compile source using Grammar2 runtime (GrammarInterpreter)
117+
///
118+
/// This is the new default runtime that uses named bindings and direct TypedAST construction.
119+
pub fn load_grammar2(
120+
grammar_path: &PathBuf,
121+
source_path: &PathBuf,
122+
verbose: bool,
123+
) -> Result<HirModule, Box<dyn std::error::Error>> {
124+
if verbose {
125+
println!("{} Grammar file: {:?}", "info:".blue(), grammar_path);
126+
println!("{} Source file: {:?}", "info:".blue(), source_path);
127+
}
128+
129+
// Verify files exist
130+
if !grammar_path.exists() {
131+
return Err(format!("Grammar file not found: {:?}", grammar_path).into());
132+
}
133+
if !source_path.exists() {
134+
return Err(format!("Source file not found: {:?}", source_path).into());
135+
}
136+
137+
// Read grammar
138+
let grammar_code = std::fs::read_to_string(grammar_path)?;
139+
if verbose {
140+
println!("{} Read {} bytes of grammar", "info:".blue(), grammar_code.len());
141+
}
142+
143+
// Read source code
144+
let source_code = std::fs::read_to_string(source_path)?;
145+
if verbose {
146+
println!("{} Read {} bytes of source code", "info:".blue(), source_code.len());
147+
}
148+
149+
// Step 1: Compile grammar with Grammar2
150+
if verbose {
151+
println!("{} Compiling grammar with Grammar2...", "info:".blue());
152+
}
153+
let grammar = Grammar2::from_source(&grammar_code)
154+
.map_err(|e| format!("Failed to compile grammar: {}", e))?;
155+
156+
if verbose {
157+
println!("{} Language: {} v{}", "info:".blue(), grammar.name(), grammar.version());
158+
}
159+
160+
// Step 2: Parse source to TypedProgram
161+
if verbose {
162+
println!("{} Parsing source with Grammar2...", "info:".blue());
163+
}
164+
let source_file_name = source_path.to_string_lossy().to_string();
165+
let mut typed_program = grammar.parse_with_filename(&source_code, &source_file_name)
166+
.map_err(|e| format!("Failed to parse source: {}", e))?;
167+
168+
// Add the source file to the program for proper diagnostics
169+
use zyntax_typed_ast::source::SourceFile;
170+
typed_program.source_files = vec![SourceFile::new(source_file_name.clone(), source_code.clone())];
171+
172+
if verbose {
173+
println!(
174+
"{} Parsed to TypedProgram with {} declarations",
175+
"info:".blue(),
176+
typed_program.declarations.len()
177+
);
178+
}
179+
180+
// Step 3: Lower TypedProgram to HIR
181+
let hir_module = lower_to_hir(typed_program, verbose)?;
182+
183+
if verbose {
184+
println!(
185+
"{} Lowered to HIR with {} functions",
186+
"info:".blue(),
187+
hir_module.functions.len()
188+
);
189+
}
190+
191+
Ok(hir_module)
192+
}
193+
104194
/// Compile .zyn grammar to zpeg module
105195
fn compile_grammar(
106196
grammar_code: &str,

crates/zyntax_cli/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ fn main() {
4848
args.import_map,
4949
args.cache_dir,
5050
args.no_cache,
51+
args.grammar1,
5152
args.packs,
5253
args.static_libs,
5354
cli.verbose,

0 commit comments

Comments
 (0)