Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

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

gleam / compiler-core / src / codegen.rs
5.3 kB 184 lines
1use crate::{ 2 build::Module, 3 config::PackageConfig, 4 erlang, 5 io::{FileSystemWriter, Utf8Writer}, 6 javascript, 7 line_numbers::LineNumbers, 8 Result, 9}; 10use itertools::Itertools; 11use std::{fmt::Debug, path::Path}; 12 13/// A code generator that creates a .erl Erlang module and record header files 14/// for each Gleam module in the package. 15#[derive(Debug)] 16pub struct Erlang<'a> { 17 output_directory: &'a Path, 18} 19 20impl<'a> Erlang<'a> { 21 pub fn new(output_directory: &'a Path) -> Self { 22 Self { output_directory } 23 } 24 25 pub fn render<Writer: FileSystemWriter>( 26 &self, 27 writer: Writer, 28 modules: &[Module], 29 ) -> Result<()> { 30 for module in modules { 31 let erl_name = module.name.replace("/", "@"); 32 self.erlang_module(&writer, module, &erl_name)?; 33 self.erlang_record_headers(&writer, module, &erl_name)?; 34 } 35 Ok(()) 36 } 37 38 fn erlang_module<Writer: FileSystemWriter>( 39 &self, 40 writer: &Writer, 41 module: &Module, 42 erl_name: &str, 43 ) -> Result<()> { 44 let name = format!("{}.erl", erl_name); 45 let path = self.output_directory.join(&name); 46 let mut file = writer.writer(&path)?; 47 let line_numbers = LineNumbers::new(&module.code); 48 let res = erlang::module(&module.ast, &line_numbers, &mut file); 49 tracing::debug!(name = ?name, "Generated Erlang module"); 50 res 51 } 52 53 fn erlang_record_headers<Writer: FileSystemWriter>( 54 &self, 55 writer: &Writer, 56 module: &Module, 57 erl_name: &str, 58 ) -> Result<()> { 59 for (name, text) in erlang::records(&module.ast) { 60 let name = format!("{}_{}.hrl", erl_name, name); 61 tracing::debug!(name = ?name, "Generated Erlang header"); 62 writer 63 .writer(&self.output_directory.join(name))? 64 .write(text.as_bytes())?; 65 } 66 Ok(()) 67 } 68} 69 70/// A code generator that creates a .app Erlang application file for the package 71#[derive(Debug)] 72pub struct ErlangApp<'a> { 73 output_directory: &'a Path, 74} 75 76impl<'a> ErlangApp<'a> { 77 pub fn new(output_directory: &'a Path) -> Self { 78 Self { output_directory } 79 } 80 81 pub fn render<Writer: FileSystemWriter>( 82 &self, 83 writer: Writer, 84 config: &PackageConfig, 85 modules: &[Module], 86 ) -> Result<()> { 87 fn tuple(key: &str, value: &str) -> String { 88 format!(" {{{}, {}}},\n", key, value) 89 } 90 91 let path = self.output_directory.join(format!("{}.app", &config.name)); 92 93 let start_module = config 94 .erlang 95 .application_start_module 96 .as_ref() 97 .map(|module| tuple("mod", &format!("'{}'", module.replace("/", "@")))) 98 .unwrap_or_default(); 99 100 let modules = modules 101 .iter() 102 .map(|m| m.name.replace("/", "@")) 103 .sorted() 104 .join(",\n "); 105 106 // TODO: When precompiling for production (i.e. as a precompiled hex 107 // package) we will need to exclude the dev deps. 108 let applications = config 109 .dependencies 110 .keys() 111 .chain(config.dev_dependencies.keys()) 112 .chain(config.erlang.extra_applications.iter()) 113 .sorted() 114 .join(",\n "); 115 116 let text = format!( 117 r#"{{application, {package}, [ 118{start_module} {{vsn, "{version}"}}, 119 {{applications, [{applications}]}}, 120 {{description, "{description}"}}, 121 {{modules, [{modules}]}}, 122 {{registered, []}} 123]}}. 124"#, 125 applications = applications, 126 description = config.description, 127 modules = modules, 128 package = config.name, 129 start_module = start_module, 130 version = config.version, 131 ); 132 133 writer.writer(&path)?.write(text.as_bytes()) 134 } 135} 136 137#[derive(Debug)] 138pub struct JavaScript<'a> { 139 output_directory: &'a Path, 140} 141 142impl<'a> JavaScript<'a> { 143 pub fn new(output_directory: &'a Path) -> Self { 144 Self { output_directory } 145 } 146 147 pub fn render(&self, writer: &impl FileSystemWriter, modules: &[Module]) -> Result<()> { 148 for module in modules { 149 let js_name = module.name.clone(); 150 self.js_module(writer, module, &js_name)? 151 } 152 self.write_prelude(writer)?; 153 Ok(()) 154 } 155 156 fn write_prelude(&self, writer: &impl FileSystemWriter) -> Result<()> { 157 tracing::debug!("Generated js prelude"); 158 writer 159 .writer(&self.output_directory.join("gleam.mjs"))? 160 .str_write(javascript::PRELUDE)?; 161 Ok(()) 162 } 163 164 fn js_module( 165 &self, 166 writer: &impl FileSystemWriter, 167 module: &Module, 168 js_name: &str, 169 ) -> Result<()> { 170 let name = format!("{}.mjs", js_name); 171 let path = self.output_directory.join(&name); 172 let mut file = writer.writer(&path)?; 173 let line_numbers = LineNumbers::new(&module.code); 174 let res = javascript::module( 175 &module.ast, 176 &line_numbers, 177 &module.input_path, 178 &module.code, 179 &mut file, 180 ); 181 tracing::debug!(name = ?js_name, "Generated js module"); 182 res 183 } 184}