Fork of daniellemaywood.uk/gleam — Wasm codegen work
7.3 kB
211 lines
1use crate::{
2 analyse::TargetSupport,
3 build::{Origin, Target},
4 config::PackageConfig,
5 javascript::*,
6 uid::UniqueIdGenerator,
7 warning::{TypeWarningEmitter, WarningEmitter},
8};
9use camino::{Utf8Path, Utf8PathBuf};
10
11mod assert;
12mod assignments;
13mod bit_arrays;
14mod blocks;
15mod bools;
16mod case;
17mod case_clause_guards;
18mod consts;
19mod custom_types;
20mod echo;
21mod externals;
22mod functions;
23mod generics;
24mod lists;
25mod modules;
26mod numbers;
27mod panic;
28mod prelude;
29mod records;
30mod recursion;
31mod results;
32mod strings;
33mod todo;
34mod tuples;
35mod type_alias;
36mod use_;
37
38pub static CURRENT_PACKAGE: &str = "thepackage";
39
40#[macro_export]
41macro_rules! assert_js {
42 ($(($name:literal, $module_src:literal)),+, $src:literal $(,)?) => {
43 let compiled =
44 $crate::javascript::tests::compile_js($src, vec![$(($crate::javascript::tests::CURRENT_PACKAGE, $name, $module_src)),*]);
45 let mut output = String::from("----- SOURCE CODE\n");
46 for (name, src) in [$(($name, $module_src)),*] {
47 output.push_str(&format!("-- {name}.gleam\n{src}\n\n"));
48 }
49 output.push_str(&format!("-- main.gleam\n{}\n\n----- COMPILED JAVASCRIPT\n{compiled}", $src));
50 insta::assert_snapshot!(insta::internals::AutoName, output, $src);
51 };
52
53 (($dep_package:expr, $dep_name:expr, $dep_src:expr), $src:expr $(,)?) => {{
54 let compiled =
55 $crate::javascript::tests::compile_js($src, vec![($dep_package, $dep_name, $dep_src)]);
56 let output = format!(
57 "----- SOURCE CODE\n{}\n\n----- COMPILED JAVASCRIPT\n{}",
58 $src, compiled
59 );
60 insta::assert_snapshot!(insta::internals::AutoName, output, $src);
61 }};
62
63 (($dep_package:expr, $dep_name:expr, $dep_src:expr), $src:expr, $js:expr $(,)?) => {{
64 let output =
65 $crate::javascript::tests::compile_js($src, Some(($dep_package, $dep_name, $dep_src)));
66 assert_eq!(($src, output), ($src, $js.to_string()));
67 }};
68
69 ($src:expr $(,)?) => {{
70 let compiled =
71 $crate::javascript::tests::compile_js($src, vec![]);
72 let output = format!(
73 "----- SOURCE CODE\n{}\n\n----- COMPILED JAVASCRIPT\n{}",
74 $src, compiled
75 );
76 insta::assert_snapshot!(insta::internals::AutoName, output, $src);
77 }};
78
79 ($src:expr, $js:expr $(,)?) => {{
80 let output =
81 $crate::javascript::tests::compile_js($src, vec![]);
82 assert_eq!(($src, output), ($src, $js.to_string()));
83 }};
84}
85
86#[macro_export]
87macro_rules! assert_ts_def {
88 (($dep_1_package:expr, $dep_1_name:expr, $dep_1_src:expr), ($dep_2_package:expr, $dep_2_name:expr, $dep_2_src:expr), $src:expr $(,)?) => {{
89 let compiled = $crate::javascript::tests::compile_ts(
90 $src,
91 vec![
92 ($dep_1_package, $dep_1_name, $dep_1_src),
93 ($dep_2_package, $dep_2_name, $dep_2_src),
94 ],
95 );
96 let output = format!(
97 "----- SOURCE CODE\n{}\n\n----- TYPESCRIPT DEFINITIONS\n{}",
98 $src, compiled
99 );
100 insta::assert_snapshot!(insta::internals::AutoName, output, $src);
101 }};
102
103 (($dep_package:expr, $dep_name:expr, $dep_src:expr), $src:expr $(,)?) => {{
104 let compiled =
105 $crate::javascript::tests::compile_ts($src, vec![($dep_package, $dep_name, $dep_src)]);
106 let output = format!(
107 "----- SOURCE CODE\n{}\n\n----- TYPESCRIPT DEFINITIONS\n{}",
108 $src, compiled
109 );
110 insta::assert_snapshot!(insta::internals::AutoName, output, $src);
111 }};
112
113 ($src:expr $(,)?) => {{
114 let compiled = $crate::javascript::tests::compile_ts($src, vec![]);
115 let output = format!(
116 "----- SOURCE CODE\n{}\n\n----- TYPESCRIPT DEFINITIONS\n{}",
117 $src, compiled
118 );
119 insta::assert_snapshot!(insta::internals::AutoName, output, $src);
120 }};
121}
122
123pub fn compile(src: &str, deps: Vec<(&str, &str, &str)>) -> TypedModule {
124 let mut modules = im::HashMap::new();
125 let ids = UniqueIdGenerator::new();
126 // DUPE: preludeinsertion
127 // TODO: Currently we do this here and also in the tests. It would be better
128 // to have one place where we create all this required state for use in each
129 // place.
130 let _ = modules.insert(
131 PRELUDE_MODULE_NAME.into(),
132 crate::type_::build_prelude(&ids),
133 );
134 let mut direct_dependencies = std::collections::HashMap::from_iter(vec![]);
135
136 deps.iter().for_each(|(dep_package, dep_name, dep_src)| {
137 let mut dep_config = PackageConfig::default();
138 dep_config.name = (*dep_package).into();
139 let parsed = crate::parse::parse_module(
140 Utf8PathBuf::from("test/path"),
141 dep_src,
142 &WarningEmitter::null(),
143 )
144 .expect("dep syntax error");
145 let mut ast = parsed.module;
146 ast.name = (*dep_name).into();
147 let line_numbers = LineNumbers::new(dep_src);
148
149 let dep = crate::analyse::ModuleAnalyzerConstructor::<()> {
150 target: Target::JavaScript,
151 ids: &ids,
152 origin: Origin::Src,
153 importable_modules: &modules,
154 warnings: &TypeWarningEmitter::null(),
155 direct_dependencies: &std::collections::HashMap::new(),
156 target_support: TargetSupport::Enforced,
157 package_config: &dep_config,
158 }
159 .infer_module(ast, line_numbers, "".into())
160 .expect("should successfully infer");
161 let _ = modules.insert((*dep_name).into(), dep.type_info);
162 let _ = direct_dependencies.insert((*dep_package).into(), ());
163 });
164
165 let parsed =
166 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null())
167 .expect("syntax error");
168 let mut ast = parsed.module;
169 ast.name = "my/mod".into();
170 let line_numbers = LineNumbers::new(src);
171 let mut config = PackageConfig::default();
172 config.name = "thepackage".into();
173
174 crate::analyse::ModuleAnalyzerConstructor::<()> {
175 target: Target::JavaScript,
176 ids: &ids,
177 origin: Origin::Src,
178 importable_modules: &modules,
179 warnings: &TypeWarningEmitter::null(),
180 direct_dependencies: &direct_dependencies,
181 target_support: TargetSupport::NotEnforced,
182 package_config: &config,
183 }
184 .infer_module(ast, line_numbers, "src/module.gleam".into())
185 .expect("should successfully infer")
186}
187
188pub fn compile_js(src: &str, deps: Vec<(&str, &str, &str)>) -> String {
189 let ast = compile(src, deps);
190 let line_numbers = LineNumbers::new(src);
191 let stdlib_package = StdlibPackage::Present;
192 let output = module(ModuleConfig {
193 module: &ast,
194 line_numbers: &line_numbers,
195 src: &"".into(),
196 typescript: TypeScriptDeclarations::None,
197 stdlib_package,
198 path: Utf8Path::new("src/module.gleam"),
199 project_root: "project/root".into(),
200 });
201
202 output.replace(
203 std::include_str!("../../templates/echo.mjs"),
204 "// ...omitted code from `templates/echo.mjs`...",
205 )
206}
207
208pub fn compile_ts(src: &str, deps: Vec<(&str, &str, &str)>) -> String {
209 let ast = compile(src, deps);
210 ts_declaration(&ast)
211}