Skip to content

Commit 398b6ce

Browse files
committed
feat: implement ZynPEG 2.0 Phase 2 - code generation
Phase 2 of the new PEG implementation: Parser Generation (crates/zyn_peg/src/codegen/parser_gen.rs): - Generate parse_* methods from GrammarIR rules - Pattern matching code for sequences, choices, optionals - Memoization integration for O(n) parsing - Named binding collection during parsing Action Generation (crates/zyn_peg/src/codegen/action_gen.rs): - Generate Rust code from ActionIR - Support for Construct, HelperCall, PassThrough - Expression code generation (field access, method calls) Pratt Parser (crates/zyn_peg/src/codegen/pratt_gen.rs): - Generate Pratt parser for expression precedence - Configurable operator precedence and associativity - Support for prefix, infix, and postfix operators - Standard expression config with common operators
1 parent 726b2e7 commit 398b6ce

5 files changed

Lines changed: 1317 additions & 0 deletions

File tree

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
//! Action Code Generator for ZynPEG 2.0
2+
//!
3+
//! Generates Rust code from ActionIR to construct TypedAST nodes.
4+
//!
5+
//! # Action Types
6+
//!
7+
//! - `Construct`: Direct struct/enum construction
8+
//! - `HelperCall`: Call helper functions like `fold_binary_left`
9+
//! - `PassThrough`: Return a binding directly
10+
//! - `Match`: Pattern match on a binding
11+
//! - `Conditional`: If/else based on a condition
12+
13+
use crate::grammar::{ActionIR, ExprIR};
14+
15+
/// Code generator for semantic actions
16+
pub struct ActionGenerator {
17+
/// Generated code buffer
18+
code: String,
19+
/// Indentation level
20+
indent: usize,
21+
}
22+
23+
impl ActionGenerator {
24+
pub fn new() -> Self {
25+
ActionGenerator {
26+
code: String::new(),
27+
indent: 0,
28+
}
29+
}
30+
31+
/// Generate action code from ActionIR
32+
pub fn generate(&mut self, action: &ActionIR) -> String {
33+
self.code.clear();
34+
self.generate_action(action);
35+
self.code.clone()
36+
}
37+
38+
fn generate_action(&mut self, action: &ActionIR) {
39+
match action {
40+
ActionIR::Construct { type_path, fields } => {
41+
self.line(&format!("{} {{", type_path));
42+
self.indent += 1;
43+
for (name, expr) in fields {
44+
let expr_code = self.generate_expr(expr);
45+
self.line(&format!("{}: {},", name, expr_code));
46+
}
47+
self.indent -= 1;
48+
self.line("}");
49+
}
50+
51+
ActionIR::HelperCall { function, args } => {
52+
let args_code: Vec<String> = args.iter()
53+
.map(|e| self.generate_expr(e))
54+
.collect();
55+
self.line(&format!("{}({})", function, args_code.join(", ")));
56+
}
57+
58+
ActionIR::PassThrough { binding } => {
59+
self.line(binding);
60+
}
61+
62+
ActionIR::Match { binding, cases } => {
63+
self.line(&format!("match {} {{", binding));
64+
self.indent += 1;
65+
for (pattern, action) in cases {
66+
self.line(&format!("{:?} => {{", pattern));
67+
self.indent += 1;
68+
self.generate_action(action);
69+
self.indent -= 1;
70+
self.line("}");
71+
}
72+
self.line("_ => panic!(\"unmatched case\"),");
73+
self.indent -= 1;
74+
self.line("}");
75+
}
76+
77+
ActionIR::Conditional { condition, then_action, else_action } => {
78+
let cond_code = self.generate_expr(condition);
79+
self.line(&format!("if {} {{", cond_code));
80+
self.indent += 1;
81+
self.generate_action(then_action);
82+
self.indent -= 1;
83+
if let Some(else_act) = else_action {
84+
self.line("} else {");
85+
self.indent += 1;
86+
self.generate_action(else_act);
87+
self.indent -= 1;
88+
}
89+
self.line("}");
90+
}
91+
}
92+
}
93+
94+
/// Generate expression code
95+
fn generate_expr(&self, expr: &ExprIR) -> String {
96+
match expr {
97+
ExprIR::Binding(name) => name.clone(),
98+
99+
ExprIR::FieldAccess { base, field } => {
100+
format!("{}.{}", self.generate_expr(base), field)
101+
}
102+
103+
ExprIR::MethodCall { receiver, method, args } => {
104+
let args_code: Vec<String> = args.iter()
105+
.map(|e| self.generate_expr(e))
106+
.collect();
107+
format!("{}.{}({})", self.generate_expr(receiver), method, args_code.join(", "))
108+
}
109+
110+
ExprIR::FunctionCall { function, args } => {
111+
let args_code: Vec<String> = args.iter()
112+
.map(|e| self.generate_expr(e))
113+
.collect();
114+
format!("{}({})", function, args_code.join(", "))
115+
}
116+
117+
ExprIR::StringLit(s) => format!("{:?}", s),
118+
119+
ExprIR::IntLit(n) => format!("{}", n),
120+
121+
ExprIR::BoolLit(b) => format!("{}", b),
122+
123+
ExprIR::List(items) => {
124+
let items_code: Vec<String> = items.iter()
125+
.map(|e| self.generate_expr(e))
126+
.collect();
127+
format!("vec![{}]", items_code.join(", "))
128+
}
129+
130+
ExprIR::UnwrapOr { optional, default } => {
131+
format!("{}.unwrap_or({})", self.generate_expr(optional), self.generate_expr(default))
132+
}
133+
134+
ExprIR::MapOption { optional, param, body } => {
135+
format!("{}.map(|{}| {})", self.generate_expr(optional), param, self.generate_expr(body))
136+
}
137+
138+
ExprIR::StructLit { type_name, fields } => {
139+
let fields_code: Vec<String> = fields.iter()
140+
.map(|(name, expr)| format!("{}: {}", name, self.generate_expr(expr)))
141+
.collect();
142+
format!("{} {{ {} }}", type_name, fields_code.join(", "))
143+
}
144+
145+
ExprIR::EnumVariant { type_name, variant, value } => {
146+
if let Some(v) = value {
147+
format!("{}::{}({})", type_name, variant, self.generate_expr(v))
148+
} else {
149+
format!("{}::{}", type_name, variant)
150+
}
151+
}
152+
153+
ExprIR::Cast { expr, target_type } => {
154+
format!("{} as {}", self.generate_expr(expr), target_type)
155+
}
156+
157+
ExprIR::Intern(expr) => {
158+
format!("self.state.intern(&{})", self.generate_expr(expr))
159+
}
160+
161+
ExprIR::Text(expr) => {
162+
format!("{}.text()", self.generate_expr(expr))
163+
}
164+
165+
ExprIR::GetSpan(expr) => {
166+
format!("{}.span()", self.generate_expr(expr))
167+
}
168+
169+
ExprIR::IsSome(expr) => {
170+
format!("{}.is_some()", self.generate_expr(expr))
171+
}
172+
173+
ExprIR::Binary { left, op, right } => {
174+
format!("({} {} {})", self.generate_expr(left), op, self.generate_expr(right))
175+
}
176+
177+
ExprIR::Default(type_name) => {
178+
if type_name.is_empty() {
179+
"Default::default()".to_string()
180+
} else {
181+
format!("{}::default()", type_name)
182+
}
183+
}
184+
}
185+
}
186+
187+
fn line(&mut self, text: &str) {
188+
for _ in 0..self.indent {
189+
self.code.push_str(" ");
190+
}
191+
self.code.push_str(text);
192+
self.code.push('\n');
193+
}
194+
}
195+
196+
impl Default for ActionGenerator {
197+
fn default() -> Self {
198+
Self::new()
199+
}
200+
}
201+
202+
#[cfg(test)]
203+
mod tests {
204+
use super::*;
205+
206+
#[test]
207+
fn test_generate_construct() {
208+
let action = ActionIR::Construct {
209+
type_path: "TypedExpression::Binary".to_string(),
210+
fields: vec![
211+
("left".to_string(), ExprIR::Binding("left".to_string())),
212+
("op".to_string(), ExprIR::Binding("op".to_string())),
213+
("right".to_string(), ExprIR::Binding("right".to_string())),
214+
],
215+
};
216+
217+
let mut gen = ActionGenerator::new();
218+
let code = gen.generate(&action);
219+
220+
assert!(code.contains("TypedExpression::Binary"));
221+
assert!(code.contains("left: left"));
222+
assert!(code.contains("op: op"));
223+
assert!(code.contains("right: right"));
224+
}
225+
226+
#[test]
227+
fn test_generate_helper_call() {
228+
let action = ActionIR::HelperCall {
229+
function: "fold_binary_left".to_string(),
230+
args: vec![
231+
ExprIR::Binding("items".to_string()),
232+
],
233+
};
234+
235+
let mut gen = ActionGenerator::new();
236+
let code = gen.generate(&action);
237+
238+
assert!(code.contains("fold_binary_left(items)"));
239+
}
240+
241+
#[test]
242+
fn test_generate_pass_through() {
243+
let action = ActionIR::PassThrough {
244+
binding: "inner".to_string(),
245+
};
246+
247+
let mut gen = ActionGenerator::new();
248+
let code = gen.generate(&action);
249+
250+
assert!(code.contains("inner"));
251+
}
252+
253+
#[test]
254+
fn test_generate_expr_unwrap_or() {
255+
let gen = ActionGenerator::new();
256+
let expr = ExprIR::UnwrapOr {
257+
optional: Box::new(ExprIR::Binding("params".to_string())),
258+
default: Box::new(ExprIR::List(vec![])),
259+
};
260+
261+
let code = gen.generate_expr(&expr);
262+
assert!(code.contains("params.unwrap_or(vec![])"));
263+
}
264+
}

crates/zyn_peg/src/codegen/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//! Code Generation for ZynPEG 2.0
2+
//!
3+
//! This module generates Rust code from GrammarIR:
4+
//! - `parser_gen.rs`: Generate parse_* methods for each rule
5+
//! - `action_gen.rs`: Generate action code from ActionIR
6+
//! - `pratt_gen.rs`: Generate Pratt parser for expression precedence
7+
8+
pub mod parser_gen;
9+
pub mod action_gen;
10+
pub mod pratt_gen;
11+
12+
pub use parser_gen::*;
13+
pub use action_gen::*;
14+
pub use pratt_gen::*;

0 commit comments

Comments
 (0)