MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor oxc into multiple files

+311 -300
-1
src/strip/Cargo.toml
··· 3 3 edition = "2024" 4 4 5 5 [lib] 6 - path = "oxc.rs" 7 6 crate-type = ["staticlib", "cdylib"] 8 7 9 8 [dependencies]
src/strip/oxc.h include/oxc.h
-299
src/strip/oxc.rs
··· 1 - use std::ffi::{CStr, c_char, c_int}; 2 - use std::path::Path; 3 - use std::ptr; 4 - 5 - use oxc_allocator::Allocator; 6 - use oxc_ast::ast::{BindingPattern, VariableDeclarationKind}; 7 - use oxc_ast_visit::{Visit, walk}; 8 - use oxc_codegen::Codegen; 9 - use oxc_parser::Parser; 10 - use oxc_semantic::{ScopeFlags, SemanticBuilder}; 11 - use oxc_span::SourceType; 12 - use oxc_transformer::{TransformOptions, Transformer, TypeScriptOptions}; 13 - 14 - pub const OXC_ERR_NULL_INPUT: c_int = -1; 15 - pub const OXC_ERR_INVALID_UTF8: c_int = -2; 16 - pub const OXC_ERR_TRANSFORM_FAILED: c_int = -4; 17 - pub const OXC_ERR_OUTPUT_TOO_LARGE: c_int = -5; 18 - 19 - #[unsafe(no_mangle)] 20 - pub unsafe extern "C" fn OXC_strip_types(input: *const c_char, filename: *const c_char, output: *mut c_char, output_len: usize) -> c_int { 21 - if input.is_null() || output.is_null() { 22 - return OXC_ERR_NULL_INPUT; 23 - } 24 - 25 - let filename_str = match unsafe { CStr::from_ptr(filename).to_str() } { 26 - Ok(s) => s, 27 - Err(_) => return OXC_ERR_INVALID_UTF8, 28 - }; 29 - 30 - let input_str = match unsafe { CStr::from_ptr(input).to_str() } { 31 - Ok(s) => s, 32 - Err(_) => return OXC_ERR_INVALID_UTF8, 33 - }; 34 - 35 - match strip_types_internal(input_str, filename_str) { 36 - Ok(result) => { 37 - let bytes = result.as_bytes(); 38 - if bytes.len() + 1 > output_len { 39 - return OXC_ERR_OUTPUT_TOO_LARGE; 40 - } 41 - unsafe { 42 - ptr::copy_nonoverlapping(bytes.as_ptr(), output as *mut u8, bytes.len()); 43 - *output.add(bytes.len()) = 0; 44 - } 45 - bytes.len() as c_int 46 - } 47 - Err(_) => OXC_ERR_TRANSFORM_FAILED, 48 - } 49 - } 50 - 51 - fn strip_types_internal(source: &str, filename: &str) -> Result<String, String> { 52 - let allocator = Allocator::default(); 53 - let source_type = SourceType::from_path(filename).unwrap_or_else(|_| SourceType::ts()); 54 - let parser_ret = Parser::new(&allocator, source, source_type).parse(); 55 - 56 - if !parser_ret.errors.is_empty() { 57 - let errors: Vec<String> = parser_ret.errors.iter().map(|e| e.to_string()).collect(); 58 - return Err(format!("Parse errors: {}", errors.join("; "))); 59 - } 60 - 61 - let mut program = parser_ret.program; 62 - let semantic_ret = SemanticBuilder::new().build(&program); 63 - 64 - if !semantic_ret.errors.is_empty() { 65 - let errors: Vec<String> = semantic_ret.errors.iter().map(|e| e.to_string()).collect(); 66 - return Err(format!("Semantic errors: {}", errors.join("; "))); 67 - } 68 - 69 - let scoping = semantic_ret.semantic.into_scoping(); 70 - let transform_options = TransformOptions { 71 - typescript: TypeScriptOptions { 72 - only_remove_type_imports: true, 73 - ..Default::default() 74 - }, 75 - ..Default::default() 76 - }; 77 - 78 - let source_path = Path::new(filename); 79 - let transformer = Transformer::new(&allocator, source_path, &transform_options); 80 - let ret = transformer.build_with_scoping(scoping, &mut program); 81 - 82 - if !ret.errors.is_empty() { 83 - let errors: Vec<String> = ret.errors.iter().map(|e| e.to_string()).collect(); 84 - return Err(format!("Transform errors: {}", errors.join("; "))); 85 - } 86 - 87 - let output = Codegen::new().build(&program).code; 88 - Ok(output) 89 - } 90 - 91 - struct VarCollector<'a> { 92 - vars: Vec<&'a str>, 93 - func_depth: usize, 94 - target_depth: usize, 95 - } 96 - 97 - impl<'a> VarCollector<'a> { 98 - fn new() -> Self { 99 - Self { 100 - vars: Vec::new(), 101 - func_depth: 0, 102 - target_depth: 0, 103 - } 104 - } 105 - fn for_func_body() -> Self { 106 - Self { 107 - vars: Vec::new(), 108 - func_depth: 0, 109 - target_depth: 1, 110 - } 111 - } 112 - } 113 - 114 - impl<'a> Visit<'a> for VarCollector<'a> { 115 - fn visit_function(&mut self, func: &oxc_ast::ast::Function<'a>, flags: ScopeFlags) { 116 - self.func_depth += 1; 117 - walk::walk_function(self, func, flags); 118 - self.func_depth -= 1; 119 - } 120 - 121 - fn visit_arrow_function_expression(&mut self, expr: &oxc_ast::ast::ArrowFunctionExpression<'a>) { 122 - self.func_depth += 1; 123 - walk::walk_arrow_function_expression(self, expr); 124 - self.func_depth -= 1; 125 - } 126 - 127 - fn visit_variable_declaration(&mut self, decl: &oxc_ast::ast::VariableDeclaration<'a>) { 128 - if decl.kind == VariableDeclarationKind::Var && self.func_depth == self.target_depth { 129 - for declarator in &decl.declarations { 130 - self.extract_binding_names(&declarator.id); 131 - } 132 - } 133 - walk::walk_variable_declaration(self, decl); 134 - } 135 - } 136 - 137 - impl<'a> VarCollector<'a> { 138 - fn extract_binding_names(&mut self, pattern: &BindingPattern<'a>) { 139 - use BindingPattern::*; 140 - match pattern { 141 - BindingIdentifier(id) => self.vars.push(id.name.as_str()), 142 - ObjectPattern(obj) => { 143 - for prop in &obj.properties { 144 - self.extract_binding_names(&prop.value); 145 - } 146 - if let Some(rest) = &obj.rest { 147 - self.extract_binding_names(&rest.argument); 148 - } 149 - } 150 - ArrayPattern(arr) => { 151 - for elem in arr.elements.iter().flatten() { 152 - self.extract_binding_names(elem); 153 - } 154 - if let Some(rest) = &arr.rest { 155 - self.extract_binding_names(&rest.argument); 156 - } 157 - } 158 - AssignmentPattern(assign) => { 159 - self.extract_binding_names(&assign.left); 160 - } 161 - } 162 - } 163 - } 164 - 165 - fn collect_var_names(source: &str) -> Result<Vec<String>, String> { 166 - let allocator = Allocator::default(); 167 - let source_type = SourceType::mjs(); 168 - let parser_ret = Parser::new(&allocator, source, source_type).parse(); 169 - 170 - if !parser_ret.errors.is_empty() { 171 - let errors: Vec<String> = parser_ret.errors.iter().map(|e| e.to_string()).collect(); 172 - return Err(format!("Parse errors: {}", errors.join("; "))); 173 - } 174 - 175 - let mut collector = VarCollector::new(); 176 - collector.visit_program(&parser_ret.program); 177 - 178 - Ok(collector.vars.into_iter().map(String::from).collect()) 179 - } 180 - 181 - fn collect_var_names_from_func(source: &str) -> Result<Vec<String>, String> { 182 - let allocator = Allocator::default(); 183 - let source_type = SourceType::mjs(); 184 - 185 - let wrapped = format!("(function{})", source); 186 - let parser_ret = Parser::new(&allocator, &wrapped, source_type).parse(); 187 - 188 - if !parser_ret.errors.is_empty() { 189 - let errors: Vec<String> = parser_ret.errors.iter().map(|e| e.to_string()).collect(); 190 - return Err(format!("Parse errors: {}", errors.join("; "))); 191 - } 192 - 193 - let mut collector = VarCollector::for_func_body(); 194 - collector.visit_program(&parser_ret.program); 195 - 196 - Ok(collector.vars.into_iter().map(String::from).collect()) 197 - } 198 - 199 - #[unsafe(no_mangle)] 200 - pub unsafe extern "C" fn OXC_get_hoisted_vars(input: *const c_char, input_len: usize, out_len: *mut usize) -> *mut c_char { 201 - if input.is_null() || out_len.is_null() { 202 - return ptr::null_mut(); 203 - } 204 - 205 - let input_slice = unsafe { std::slice::from_raw_parts(input as *const u8, input_len) }; 206 - let input_str = match std::str::from_utf8(input_slice) { 207 - Ok(s) => s, 208 - Err(_) => return ptr::null_mut(), 209 - }; 210 - 211 - match collect_var_names(input_str) { 212 - Ok(vars) => { 213 - if vars.is_empty() { 214 - return ptr::null_mut(); 215 - } 216 - let total_len: usize = vars.iter().map(|s| s.len() + 1).sum::<usize>() + 1; 217 - 218 - let layout = std::alloc::Layout::from_size_align(total_len, 1).unwrap(); 219 - let ptr = unsafe { std::alloc::alloc(layout) as *mut c_char }; 220 - if ptr.is_null() { 221 - return ptr::null_mut(); 222 - } 223 - 224 - let mut offset = 0; 225 - for v in &vars { 226 - unsafe { 227 - ptr::copy_nonoverlapping(v.as_ptr(), ptr.add(offset) as *mut u8, v.len()); 228 - *ptr.add(offset + v.len()) = 0; 229 - } 230 - offset += v.len() + 1; 231 - } 232 - unsafe { 233 - *ptr.add(offset) = 0; 234 - } 235 - 236 - unsafe { 237 - *out_len = total_len; 238 - } 239 - ptr 240 - } 241 - Err(_) => ptr::null_mut(), 242 - } 243 - } 244 - 245 - #[unsafe(no_mangle)] 246 - pub unsafe extern "C" fn OXC_get_func_hoisted_vars(input: *const c_char, input_len: usize, out_len: *mut usize) -> *mut c_char { 247 - if input.is_null() || out_len.is_null() { 248 - return ptr::null_mut(); 249 - } 250 - 251 - let input_slice = unsafe { std::slice::from_raw_parts(input as *const u8, input_len) }; 252 - let input_str = match std::str::from_utf8(input_slice) { 253 - Ok(s) => s, 254 - Err(_) => return ptr::null_mut(), 255 - }; 256 - 257 - match collect_var_names_from_func(input_str) { 258 - Ok(vars) => { 259 - if vars.is_empty() { 260 - return ptr::null_mut(); 261 - } 262 - let total_len: usize = vars.iter().map(|s| s.len() + 1).sum::<usize>() + 1; 263 - 264 - let layout = std::alloc::Layout::from_size_align(total_len, 1).unwrap(); 265 - let ptr = unsafe { std::alloc::alloc(layout) as *mut c_char }; 266 - if ptr.is_null() { 267 - return ptr::null_mut(); 268 - } 269 - 270 - let mut offset = 0; 271 - for v in &vars { 272 - unsafe { 273 - ptr::copy_nonoverlapping(v.as_ptr(), ptr.add(offset) as *mut u8, v.len()); 274 - *ptr.add(offset + v.len()) = 0; 275 - } 276 - offset += v.len() + 1; 277 - } 278 - unsafe { 279 - *ptr.add(offset) = 0; 280 - } 281 - unsafe { 282 - *out_len = total_len; 283 - } 284 - ptr 285 - } 286 - Err(_) => ptr::null_mut(), 287 - } 288 - } 289 - 290 - #[unsafe(no_mangle)] 291 - pub unsafe extern "C" fn OXC_free_hoisted_vars(ptr: *mut c_char, len: usize) { 292 - if ptr.is_null() || len == 0 { 293 - return; 294 - } 295 - let layout = std::alloc::Layout::from_size_align(len, 1).unwrap(); 296 - unsafe { 297 - std::alloc::dealloc(ptr as *mut u8, layout); 298 - } 299 - }
+113
src/strip/src/collector.rs
··· 1 + use oxc_allocator::Allocator; 2 + use oxc_ast::ast::{BindingPattern, VariableDeclarationKind}; 3 + use oxc_ast_visit::{Visit, walk}; 4 + use oxc_parser::Parser; 5 + use oxc_semantic::ScopeFlags; 6 + use oxc_span::SourceType; 7 + 8 + struct VarCollector<'a> { 9 + vars: Vec<&'a str>, 10 + func_depth: usize, 11 + target_depth: usize, 12 + } 13 + 14 + impl<'a> VarCollector<'a> { 15 + fn new() -> Self { 16 + Self { 17 + vars: Vec::new(), 18 + func_depth: 0, 19 + target_depth: 0, 20 + } 21 + } 22 + 23 + fn for_func_body() -> Self { 24 + Self { 25 + vars: Vec::new(), 26 + func_depth: 0, 27 + target_depth: 1, 28 + } 29 + } 30 + 31 + fn extract_binding_names(&mut self, pattern: &BindingPattern<'a>) { 32 + use BindingPattern::*; 33 + match pattern { 34 + BindingIdentifier(id) => self.vars.push(id.name.as_str()), 35 + ObjectPattern(obj) => { 36 + for prop in &obj.properties { 37 + self.extract_binding_names(&prop.value); 38 + } 39 + if let Some(rest) = &obj.rest { 40 + self.extract_binding_names(&rest.argument); 41 + } 42 + } 43 + ArrayPattern(arr) => { 44 + for elem in arr.elements.iter().flatten() { 45 + self.extract_binding_names(elem); 46 + } 47 + if let Some(rest) = &arr.rest { 48 + self.extract_binding_names(&rest.argument); 49 + } 50 + } 51 + AssignmentPattern(assign) => { 52 + self.extract_binding_names(&assign.left); 53 + } 54 + } 55 + } 56 + } 57 + 58 + impl<'a> Visit<'a> for VarCollector<'a> { 59 + fn visit_function(&mut self, func: &oxc_ast::ast::Function<'a>, flags: ScopeFlags) { 60 + self.func_depth += 1; 61 + walk::walk_function(self, func, flags); 62 + self.func_depth -= 1; 63 + } 64 + 65 + fn visit_arrow_function_expression(&mut self, expr: &oxc_ast::ast::ArrowFunctionExpression<'a>) { 66 + self.func_depth += 1; 67 + walk::walk_arrow_function_expression(self, expr); 68 + self.func_depth -= 1; 69 + } 70 + 71 + fn visit_variable_declaration(&mut self, decl: &oxc_ast::ast::VariableDeclaration<'a>) { 72 + if decl.kind == VariableDeclarationKind::Var && self.func_depth == self.target_depth { 73 + for declarator in &decl.declarations { 74 + self.extract_binding_names(&declarator.id); 75 + } 76 + } 77 + walk::walk_variable_declaration(self, decl); 78 + } 79 + } 80 + 81 + pub fn collect_var_names(source: &str) -> Result<Vec<String>, String> { 82 + let allocator = Allocator::default(); 83 + let source_type = SourceType::mjs(); 84 + let parser_ret = Parser::new(&allocator, source, source_type).parse(); 85 + 86 + if !parser_ret.errors.is_empty() { 87 + let errors: Vec<String> = parser_ret.errors.iter().map(|e| e.to_string()).collect(); 88 + return Err(format!("Parse errors: {}", errors.join("; "))); 89 + } 90 + 91 + let mut collector = VarCollector::new(); 92 + collector.visit_program(&parser_ret.program); 93 + 94 + Ok(collector.vars.into_iter().map(String::from).collect()) 95 + } 96 + 97 + pub fn collect_var_names_from_func(source: &str) -> Result<Vec<String>, String> { 98 + let allocator = Allocator::default(); 99 + let source_type = SourceType::mjs(); 100 + 101 + let wrapped = format!("(function{})", source); 102 + let parser_ret = Parser::new(&allocator, &wrapped, source_type).parse(); 103 + 104 + if !parser_ret.errors.is_empty() { 105 + let errors: Vec<String> = parser_ret.errors.iter().map(|e| e.to_string()).collect(); 106 + return Err(format!("Parse errors: {}", errors.join("; "))); 107 + } 108 + 109 + let mut collector = VarCollector::for_func_body(); 110 + collector.visit_program(&parser_ret.program); 111 + 112 + Ok(collector.vars.into_iter().map(String::from).collect()) 113 + }
+143
src/strip/src/ffi.rs
··· 1 + use std::ffi::{CStr, c_char, c_int}; 2 + use std::ptr; 3 + 4 + use crate::collector::{collect_var_names, collect_var_names_from_func}; 5 + use crate::strip::strip_types_internal; 6 + 7 + pub const OXC_ERR_NULL_INPUT: c_int = -1; 8 + pub const OXC_ERR_INVALID_UTF8: c_int = -2; 9 + pub const OXC_ERR_TRANSFORM_FAILED: c_int = -4; 10 + pub const OXC_ERR_OUTPUT_TOO_LARGE: c_int = -5; 11 + 12 + #[unsafe(no_mangle)] 13 + pub unsafe extern "C" fn OXC_strip_types(input: *const c_char, filename: *const c_char, output: *mut c_char, output_len: usize) -> c_int { 14 + if input.is_null() || output.is_null() { 15 + return OXC_ERR_NULL_INPUT; 16 + } 17 + 18 + let filename_str = match unsafe { CStr::from_ptr(filename).to_str() } { 19 + Ok(s) => s, 20 + Err(_) => return OXC_ERR_INVALID_UTF8, 21 + }; 22 + 23 + let input_str = match unsafe { CStr::from_ptr(input).to_str() } { 24 + Ok(s) => s, 25 + Err(_) => return OXC_ERR_INVALID_UTF8, 26 + }; 27 + 28 + match strip_types_internal(input_str, filename_str) { 29 + Ok(result) => { 30 + let bytes = result.as_bytes(); 31 + if bytes.len() + 1 > output_len { 32 + return OXC_ERR_OUTPUT_TOO_LARGE; 33 + } 34 + unsafe { 35 + ptr::copy_nonoverlapping(bytes.as_ptr(), output as *mut u8, bytes.len()); 36 + *output.add(bytes.len()) = 0; 37 + } 38 + bytes.len() as c_int 39 + } 40 + Err(_) => OXC_ERR_TRANSFORM_FAILED, 41 + } 42 + } 43 + 44 + #[unsafe(no_mangle)] 45 + pub unsafe extern "C" fn OXC_get_hoisted_vars(input: *const c_char, input_len: usize, out_len: *mut usize) -> *mut c_char { 46 + if input.is_null() || out_len.is_null() { 47 + return ptr::null_mut(); 48 + } 49 + 50 + let input_slice = unsafe { std::slice::from_raw_parts(input as *const u8, input_len) }; 51 + let input_str = match std::str::from_utf8(input_slice) { 52 + Ok(s) => s, 53 + Err(_) => return ptr::null_mut(), 54 + }; 55 + 56 + match collect_var_names(input_str) { 57 + Ok(vars) => { 58 + if vars.is_empty() { 59 + return ptr::null_mut(); 60 + } 61 + let total_len: usize = vars.iter().map(|s| s.len() + 1).sum::<usize>() + 1; 62 + 63 + let layout = std::alloc::Layout::from_size_align(total_len, 1).unwrap(); 64 + let ptr = unsafe { std::alloc::alloc(layout) as *mut c_char }; 65 + if ptr.is_null() { 66 + return ptr::null_mut(); 67 + } 68 + 69 + let mut offset = 0; 70 + for v in &vars { 71 + unsafe { 72 + ptr::copy_nonoverlapping(v.as_ptr(), ptr.add(offset) as *mut u8, v.len()); 73 + *ptr.add(offset + v.len()) = 0; 74 + } 75 + offset += v.len() + 1; 76 + } 77 + unsafe { 78 + *ptr.add(offset) = 0; 79 + *out_len = total_len; 80 + } 81 + 82 + ptr 83 + } 84 + Err(_) => ptr::null_mut(), 85 + } 86 + } 87 + 88 + #[unsafe(no_mangle)] 89 + pub unsafe extern "C" fn OXC_get_func_hoisted_vars(input: *const c_char, input_len: usize, out_len: *mut usize) -> *mut c_char { 90 + if input.is_null() || out_len.is_null() { 91 + return ptr::null_mut(); 92 + } 93 + 94 + let input_slice = unsafe { std::slice::from_raw_parts(input as *const u8, input_len) }; 95 + let input_str = match std::str::from_utf8(input_slice) { 96 + Ok(s) => s, 97 + Err(_) => return ptr::null_mut(), 98 + }; 99 + 100 + match collect_var_names_from_func(input_str) { 101 + Ok(vars) => { 102 + if vars.is_empty() { 103 + return ptr::null_mut(); 104 + } 105 + let total_len: usize = vars.iter().map(|s| s.len() + 1).sum::<usize>() + 1; 106 + 107 + let layout = std::alloc::Layout::from_size_align(total_len, 1).unwrap(); 108 + let ptr = unsafe { std::alloc::alloc(layout) as *mut c_char }; 109 + 110 + if ptr.is_null() { 111 + return ptr::null_mut(); 112 + } 113 + 114 + let mut offset = 0; 115 + for v in &vars { 116 + unsafe { 117 + ptr::copy_nonoverlapping(v.as_ptr(), ptr.add(offset) as *mut u8, v.len()); 118 + *ptr.add(offset + v.len()) = 0; 119 + } 120 + offset += v.len() + 1; 121 + } 122 + 123 + unsafe { 124 + *ptr.add(offset) = 0; 125 + *out_len = total_len; 126 + } 127 + 128 + ptr 129 + } 130 + Err(_) => ptr::null_mut(), 131 + } 132 + } 133 + 134 + #[unsafe(no_mangle)] 135 + pub unsafe extern "C" fn OXC_free_hoisted_vars(ptr: *mut c_char, len: usize) { 136 + if ptr.is_null() || len == 0 { 137 + return; 138 + } 139 + let layout = std::alloc::Layout::from_size_align(len, 1).unwrap(); 140 + unsafe { 141 + std::alloc::dealloc(ptr as *mut u8, layout); 142 + } 143 + }
+7
src/strip/src/lib.rs
··· 1 + mod collector; 2 + mod ffi; 3 + mod strip; 4 + 5 + pub use collector::{collect_var_names, collect_var_names_from_func}; 6 + pub use ffi::*; 7 + pub use strip::strip_types_internal;
+48
src/strip/src/strip.rs
··· 1 + use std::path::Path; 2 + 3 + use oxc_allocator::Allocator; 4 + use oxc_codegen::Codegen; 5 + use oxc_parser::Parser; 6 + use oxc_semantic::SemanticBuilder; 7 + use oxc_span::SourceType; 8 + use oxc_transformer::{TransformOptions, Transformer, TypeScriptOptions}; 9 + 10 + pub fn strip_types_internal(source: &str, filename: &str) -> Result<String, String> { 11 + let allocator = Allocator::default(); 12 + let source_type = SourceType::from_path(filename).unwrap_or_else(|_| SourceType::ts()); 13 + let parser_ret = Parser::new(&allocator, source, source_type).parse(); 14 + 15 + if !parser_ret.errors.is_empty() { 16 + let errors: Vec<String> = parser_ret.errors.iter().map(|e| e.to_string()).collect(); 17 + return Err(format!("Parse errors: {}", errors.join("; "))); 18 + } 19 + 20 + let mut program = parser_ret.program; 21 + let semantic_ret = SemanticBuilder::new().build(&program); 22 + 23 + if !semantic_ret.errors.is_empty() { 24 + let errors: Vec<String> = semantic_ret.errors.iter().map(|e| e.to_string()).collect(); 25 + return Err(format!("Semantic errors: {}", errors.join("; "))); 26 + } 27 + 28 + let scoping = semantic_ret.semantic.into_scoping(); 29 + let transform_options = TransformOptions { 30 + typescript: TypeScriptOptions { 31 + only_remove_type_imports: true, 32 + ..Default::default() 33 + }, 34 + ..Default::default() 35 + }; 36 + 37 + let source_path = Path::new(filename); 38 + let transformer = Transformer::new(&allocator, source_path, &transform_options); 39 + let ret = transformer.build_with_scoping(scoping, &mut program); 40 + 41 + if !ret.errors.is_empty() { 42 + let errors: Vec<String> = ret.errors.iter().map(|e| e.to_string()).collect(); 43 + return Err(format!("Transform errors: {}", errors.join("; "))); 44 + } 45 + 46 + let output = Codegen::new().build(&program).code; 47 + Ok(output) 48 + }