|
| 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 | +} |
0 commit comments