1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::iter;
4
5use convert_case::{Case, Casing};
6use derive_more::{Deref, Display, From};
7use itertools::Itertools;
8use thiserror::Error;
9
10use galvan_ast::*;
11use galvan_files::{FileError, Source};
12use galvan_into_ast::{AstError, SegmentAst, SourceIntoAst};
13use galvan_resolver::{LookupError, Scope};
14
15use builtins::builtin_fns;
16
17static SUPPRESS_WARNINGS: &str = "#![allow(warnings, unused)]";
18
19#[macro_export]
23macro_rules! galvan_module {
24 () => {
25 "galvan_module"
26 };
27 ($ext:literal) => {
28 concat!("galvan_module.", $ext)
29 };
30}
31
32mod builtins;
33mod error;
34#[cfg(feature = "exec")]
35pub mod exec;
36
37mod cast;
38mod context;
39mod mapping;
40mod sanitize;
41
42pub use error::{Diagnostic, DiagnosticSeverity, ErrorCollector, Span, TranspilerError};
43
44#[derive(Debug, Error)]
45pub enum TranspileError {
46 #[error(transparent)]
47 Ast(#[from] AstError),
48 #[error(transparent)]
49 Lookup(#[from] LookupError),
50 #[error(transparent)]
51 File(#[from] FileError),
52}
53
54fn transpile_sources(sources: Vec<Source>) -> Result<Vec<TranspileOutput>, TranspileError> {
55 let asts = sources
56 .into_iter()
57 .map(|s| s.try_into_ast())
58 .collect::<Result<Vec<_>, _>>()?;
59
60 transpile_asts(asts)
61}
62
63fn transpile_asts(asts: Vec<Ast>) -> Result<Vec<TranspileOutput>, TranspileError> {
64 let segmented = asts.segmented()?;
65 let builtins = builtins();
66 let predefined = predefined_from(&builtins, builtin_fns());
67 let lookup = Context::new(builtins).with(&predefined)?.with(&segmented)?;
68 let mut scope = Scope::default();
69 scope.set_lookup(lookup.lookup.clone());
70
71 transpile_segmented(&segmented, &lookup, &mut scope)
72}
73
74struct TypeFileContent<'a> {
75 pub ty: &'a TypeDecl,
76 pub fns: Vec<&'a FnDecl>,
77}
78
79struct ExtensionFileContent<'a> {
80 pub elem: &'a TypeElement,
81 pub fns: Vec<&'a FnDecl>,
82}
83
84fn transpile_segmented(
85 segmented: &SegmentedAsts,
86 ctx: &Context,
87 scope: &mut Scope,
88) -> Result<Vec<TranspileOutput>, TranspileError> {
89 let mut main_errors = ErrorCollector::new();
90 #[derive(Hash, PartialEq, Eq, Deref, From, Display)]
91 struct ModuleName(Box<str>);
92 fn module_name(ident: &TypeIdent) -> ModuleName {
93 ident.as_str().to_case(Case::Snake).into_boxed_str().into()
94 }
95
96 fn extension_module_name(ty: &TypeElement) -> ModuleName {
97 extension_name(ty)
98 .to_ascii_lowercase()
99 .into_boxed_str()
100 .into()
101 }
102
103 fn add_extension_module<'a>(
104 extensions: &mut HashMap<ModuleName, ExtensionFileContent<'a>>,
105 func: &'a ToplevelItem<FnDecl>,
106 elem: &'a TypeElement,
107 ) {
108 let content = extensions
109 .entry(extension_module_name(elem))
110 .or_insert_with(|| ExtensionFileContent {
111 elem,
112 fns: Vec::new(),
113 });
114 content.fns.push(&func.item);
115 }
116
117 let mut type_files: HashMap<ModuleName, TypeFileContent> = HashMap::new();
118
119 for ty in &segmented.types {
120 if let Some(duplicate) = type_files.insert(
121 module_name(ty.ident()),
122 TypeFileContent {
123 ty,
124 fns: Vec::new(),
125 },
126 ) {
127 panic!(
128 "File collision for types: {} and {}",
129 ty.item.ident(),
130 duplicate.ty.ident()
131 );
132 }
133 }
134
135 let mut toplevel_functions = Vec::new();
136 let mut extensions: HashMap<ModuleName, ExtensionFileContent> = HashMap::new();
137 for func in &segmented.functions {
138 if let Some(receiver) = func.signature.receiver() {
139 let elem = &receiver.param_type;
140 let TypeElement::Plain(ty) = elem else {
141 add_extension_module(&mut extensions, func, elem);
142 continue;
143 };
144 match type_files.get_mut(&module_name(&ty.ident)) {
145 Some(content) => content.fns.push(&func.item),
146 None => {
147 add_extension_module(&mut extensions, func, elem);
148 }
149 }
150 } else {
151 toplevel_functions.push(func);
152 }
153 }
154
155 let type_files = type_files;
156 let toplevel_functions = toplevel_functions
157 .iter()
158 .map(|func| func.transpile(ctx, scope, &mut main_errors))
159 .collect::<Vec<_>>()
160 .join("\n\n");
161 let toplevel_functions = toplevel_functions.trim();
162
163 let tests = transpile_tests(segmented, ctx, scope, &mut main_errors);
164
165 let modules = type_files
166 .keys()
167 .chain(extensions.keys())
168 .map(|id| sanitize_name(id))
169 .map(|mod_name| format!("mod {mod_name};\npub use self::{mod_name}::*;"))
170 .collect::<Vec<_>>()
171 .join("\n");
172 let modules = modules.trim();
173
174 let main = segmented
175 .main
176 .as_ref()
177 .map(|main| {
178 let main_errors_ref = &mut main_errors;
179 transpile!(
180 ctx,
181 scope,
182 main_errors_ref,
183 "pub(crate) fn __main__() {}",
184 main.body
185 )
186 })
187 .unwrap_or_default();
188
189 let lib = TranspileOutput {
190 file_name: galvan_module!("rs").into(),
191 content: format!(
192 "extern crate galvan; #[allow(unused_imports)] pub(crate) use ::galvan::std::*;\n pub(crate) mod {} {{\n{}\nuse crate::*;\n{}\n}}",
193 galvan_module!(),
194 SUPPRESS_WARNINGS,
195 [modules, toplevel_functions, &main, &tests].join("\n\n")
196 )
197 .into(),
198 };
199
200 let type_files = type_files
201 .iter()
202 .map(|(k, v)| TranspileOutput {
203 file_name: format!("{k}.rs").into(),
204 content: [
205 "use crate::*;",
206 &v.ty.transpile(ctx, scope, &mut main_errors),
207 &transpile_member_functions(v.ty.ident(), &v.fns, ctx, scope, &mut main_errors),
208 ]
209 .join("\n\n")
210 .trim()
211 .into(),
212 })
213 .collect_vec();
214
215 let extension_files = extensions
216 .iter()
217 .map(|(k, v)| TranspileOutput {
218 file_name: format!("{k}.rs").into(),
219 content: [
220 "use crate::*;",
221 &transpile_extension_functions(v.elem, &v.fns, ctx, scope, &mut main_errors),
222 ]
223 .join("\n\n")
224 .trim()
225 .into(),
226 })
227 .collect_vec();
228
229 for diagnostic in main_errors.diagnostics() {
231 match diagnostic.severity {
232 DiagnosticSeverity::Error => {
233 println!("cargo::error={}", diagnostic.message);
234 std::process::exit(1);
235 }
236 DiagnosticSeverity::Warning => {
237 println!("cargo::warning={}", diagnostic.message);
238 }
239 _ => {}
240 }
241 }
242
243 Ok(type_files
244 .into_iter()
245 .chain(extension_files.into_iter())
246 .chain(iter::once(lib))
247 .collect())
248}
249
250fn transpile_tests(
251 segmented_asts: &SegmentedAsts,
252 ctx: &Context,
253 scope: &mut Scope,
254 errors: &mut ErrorCollector,
255) -> String {
256 fn test_name<'a>(desc: &Option<StringLiteral>) -> Cow<'a, str> {
257 desc.as_ref().map_or("test".into(), |desc| {
258 let snake = desc
259 .as_str()
260 .trim_matches('\"')
261 .to_case(Case::Snake)
262 .replace(|c: char| !c.is_ascii_alphanumeric(), "_");
263
264 let snake = if snake.starts_with(|c: char| c.is_ascii_digit()) {
265 format!("test_{}", snake)
266 } else {
267 snake
268 };
269
270 if snake.ends_with(|c: char| c.is_ascii_digit()) {
271 format!("{}_", snake).into()
272 } else {
273 snake.into()
274 }
275 })
276 }
277
278 let mut by_name: HashMap<Cow<'_, str>, Vec<&TestDecl>> = HashMap::new();
279 for test in &segmented_asts.tests {
280 by_name
281 .entry(test_name(&test.item.name))
282 .or_default()
283 .push(&test.item);
284 }
285
286 let resolved_tests = by_name
287 .iter()
288 .flat_map(|(name, tests)| {
289 if tests.len() == 1 {
290 vec![(name.clone(), tests[0])]
291 } else {
292 tests
293 .iter()
294 .enumerate()
295 .map(|(i, &test)| (Cow::from(format!("{}_{}", name, i)), test))
296 .collect_vec()
297 }
298 })
299 .collect_vec();
300
301 if resolved_tests.is_empty() {
302 return "".into();
303 }
304
305 let test_mod = "#[cfg(test)]\nmod tests {\nuse crate::*;\n".to_owned()
306 + resolved_tests
307 .iter()
308 .map(|t| t.transpile(ctx, scope, errors))
309 .collect::<Vec<_>>()
310 .join("\n\n")
311 .as_str()
312 + "\n}";
313
314 test_mod
315}
316
317fn transpile_member_functions(
318 ty: &TypeIdent,
319 fns: &[&FnDecl],
320 ctx: &Context,
321 scope: &mut Scope,
322 errors: &mut ErrorCollector,
323) -> String {
324 if fns.is_empty() {
325 return "".into();
326 }
327
328 let transpiled_fns = fns
329 .iter()
330 .map(|f| f.transpile(ctx, scope, errors))
331 .collect::<Vec<_>>()
332 .join("\n\n");
333 transpile!(ctx, scope, errors, "impl {} {{\n{transpiled_fns}\n}}", ty)
334}
335
336fn transpile_extension_functions(
337 ty: &TypeElement,
338 fns: &[&FnDecl],
339 ctx: &Context,
340 scope: &mut Scope,
341 errors: &mut ErrorCollector,
342) -> String {
343 debug_assert_ne!(fns.len(), 0, "Extension functions should not be empty");
344 if fns
345 .iter()
346 .find(|f| f.signature.visibility.kind != VisibilityKind::Inherited)
347 .is_some()
348 {
349 return String::new();
351 }
352
353 let trait_name = extension_name(&ty);
354 let fn_signatures = fns
355 .iter()
356 .map(|f| FnSignature {
357 visibility: Visibility::private(),
358 ..f.signature.clone()
359 })
360 .map(|s| s.transpile(ctx, scope, errors))
361 .collect::<Vec<_>>()
362 .join(";\n")
363 + ";";
364 let transpiled_fns = fns
365 .iter()
366 .map(|f| f.transpile(ctx, scope, errors))
367 .map(|s| s.strip_prefix("pub(crate) ").unwrap().to_owned())
368 .collect::<Vec<_>>()
369 .join("\n\n");
370
371 transpile! {ctx, scope, errors,
372 "
373 pub trait {trait_name} {{
374 {fn_signatures}
375 }}
376
377 impl {trait_name} for {} {{
378 {transpiled_fns}
379 }}
380 ", ty
381 }
382}
383
384fn extension_name(ty: &TypeElement) -> String {
385 fn escaped_name(ty: &TypeElement) -> String {
386 match ty {
387 TypeElement::Plain(ty) => ty.ident.as_str().to_case(Case::UpperCamel),
388 TypeElement::Tuple(ty) => format!(
389 "Tuple_{}",
390 ty.elements
391 .iter()
392 .map(escaped_name)
393 .collect::<Vec<_>>()
394 .join("_")
395 ),
396 TypeElement::Result(ty) => format!(
397 "Result_{}_{}",
398 escaped_name(&ty.success),
399 ty.error.as_ref().map_or("".into(), escaped_name)
400 ),
401 TypeElement::Optional(ty) => format!("Option_{}_Ext", escaped_name(&ty.inner)),
402 TypeElement::Dictionary(ty) => {
403 format!("Dict_{}_{}", escaped_name(&ty.key), escaped_name(&ty.value))
404 }
405 TypeElement::OrderedDictionary(ty) => format!(
406 "OrderedDict_{}_{}",
407 escaped_name(&ty.key),
408 escaped_name(&ty.value)
409 ),
410 TypeElement::Array(ty) => format!("Array_{}", escaped_name(&ty.elements)),
411 TypeElement::Set(ty) => format!("Set_{}", escaped_name(&ty.elements)),
412 TypeElement::Generic(_ty) => todo!("Generics are not supported yet!"),
413 TypeElement::Void(_) => format!("Void"),
414 TypeElement::Infer(_) => format!("Infer"),
415 TypeElement::Never(_) => format!("Never"),
416 }
417 }
418
419 escaped_name(ty) + "_Ext"
420}
421
422pub struct TranspileOutput {
423 pub file_name: Box<str>,
424 pub content: Box<str>,
425}
426
427pub struct TranspileErrors<'t> {
428 pub source: Source,
429 pub errors: &'t [TranspileError],
430}
431
432impl TranspileErrors<'_> {
433 pub fn is_empty(&self) -> bool {
434 self.errors.is_empty()
435 }
436}
437
438pub fn transpile(sources: Vec<Source>) -> Result<Vec<TranspileOutput>, TranspileError> {
439 transpile_sources(sources)
440}
441
442mod transpile_item;
443mod type_inference;
444
445trait Transpile {
446 fn transpile(&self, ctx: &Context, scope: &mut Scope, errors: &mut ErrorCollector) -> String;
447}
448
449trait Punctuated {
450 fn punctuation() -> &'static str;
451}
452
453mod macros {
454 macro_rules! transpile {
455 ($ctx:ident, $scope:ident, $errors:ident, $string:expr, $($items:expr),*$(,)?) => {
456 format!($string, $(($items).transpile($ctx, $scope, $errors)),*)
457 };
458
459 ($ctx:ident, $scope:ident, $string:expr, $($items:expr),*$(,)?) => {
461 {
462 let mut _temp_errors = crate::ErrorCollector::new();
463 format!($string, $(($items).transpile($ctx, $scope, &mut _temp_errors)),*)
464 }
465 };
466 }
467
468 macro_rules! impl_transpile {
469 ($ty:ty, $string:expr, $($field:tt),*$(,)?) => {
470 impl crate::Transpile for $ty {
471 fn transpile(&self, _ctx: &crate::Context, _scope: &mut crate::Scope, _errors: &mut crate::ErrorCollector) -> String {
472 crate::macros::transpile!(_ctx, _scope, _errors, $string, $(self.$field),*)
473 }
474 }
475 };
476
477 ($ty:ty, $old_signature:expr, $string:expr, $($field:tt),*$(,)?) => {
479 impl crate::Transpile for $ty {
480 fn transpile(&self, _ctx: &crate::Context, _scope: &mut crate::Scope, _errors: &mut crate::ErrorCollector) -> String {
481 crate::macros::transpile!(_ctx, _scope, _errors, $string, $(self.$field),*)
482 }
483 }
484 };
485 }
486
487 #[allow(unused_macros)]
488 macro_rules! impl_transpile_fn {
489 ($ty:ty, $string:expr, $($fun:ident),*$(,)?) => {
490 impl crate::Transpile for $ty {
491 fn transpile(&self, ctx: &crate::Context, scope: &mut crate::Scope) -> String {
492 crate::macros::transpile!(ctx, scope, $string, $(self.$fun()),*)
493 }
494 }
495 };
496 }
497
498 macro_rules! impl_transpile_match {
499 ($ty:ty, $($case:pat_param => ($($args:expr),+)),+$(,)?) => {
500 impl crate::Transpile for $ty {
501 #[deny(bindings_with_variant_name)]
502 #[deny(unreachable_patterns)]
503 #[deny(non_snake_case)]
504 fn transpile(&self, ctx: &crate::Context, scope: &mut crate::Scope, errors: &mut crate::ErrorCollector) -> String {
505 use $ty::*;
506 match self {
507 $($case => crate::macros::transpile!(ctx, scope, errors, $($args),+),)+
508 }
509 }
510 }
511 };
512 }
513
514 macro_rules! impl_transpile_variants {
515 ($ty:ty; $($case:ident$(,)?)+) => {
516 impl crate::Transpile for $ty {
517 #[deny(bindings_with_variant_name)]
518 #[deny(unreachable_patterns)]
519 #[deny(non_snake_case)]
520 fn transpile(&self, ctx: &crate::Context, scope: &mut crate::Scope, errors: &mut crate::ErrorCollector) -> String {
521 use $ty::*;
522 match self {
523 $($case(inner) => inner.transpile(ctx, scope, errors),)+
524 }
525 }
526 }
527 };
528 }
529
530 macro_rules! punct {
531 ($string:expr, $($ty:ty),+) => {
532 $(impl Punctuated for $ty {
533 fn punctuation() -> &'static str {
534 $string
535 }
536 })+
537 $(impl Punctuated for &$ty {
538 fn punctuation() -> &'static str {
539 $string
540 }
541 })+
542 };
543 }
544
545 pub(crate) use {
546 impl_transpile, impl_transpile_match, impl_transpile_variants, punct, transpile,
547 };
548}
549
550use crate::builtins::builtins;
551use crate::context::{predefined_from, Context};
552use crate::macros::transpile;
553use crate::sanitize::sanitize_name;
554use macros::punct;
555
556punct!(
557 ", ",
558 TypeElement,
559 TupleTypeMember,
560 Param,
561 ConstructorCallArg,
562 ClosureParameter,
563 DictLiteralElement
564);
565punct!(",\n", StructTypeMember, EnumTypeMember);
566punct!("\n\n", RootItem, FnDecl);
567punct!(";\n", Statement);
568
569impl<T> Transpile for Vec<T>
570where
571 T: Transpile + Punctuated,
572{
573 fn transpile(&self, ctx: &Context, scope: &mut Scope, errors: &mut ErrorCollector) -> String {
574 self.as_slice().transpile(ctx, scope, errors)
575 }
576}
577
578impl<T> Transpile for [T]
579where
580 T: Transpile + Punctuated,
581{
582 fn transpile(&self, ctx: &Context, scope: &mut Scope, errors: &mut ErrorCollector) -> String {
583 let punct = T::punctuation();
584 self.iter()
585 .map(|e| e.transpile(ctx, scope, errors))
586 .reduce(|acc, e| format!("{acc}{punct}{e}"))
587 .unwrap_or_else(String::new)
588 }
589}
590
591impl<T> Transpile for Option<Vec<T>>
592where
593 T: Transpile + Punctuated,
594{
595 fn transpile(&self, ctx: &Context, scope: &mut Scope, errors: &mut ErrorCollector) -> String {
596 self.as_ref()
597 .map_or_else(String::new, |v| v.transpile(ctx, scope, errors))
598 }
599}
600
601impl Transpile for &str {
602 fn transpile(
603 &self,
604 _ctx: &Context,
605 _scope: &mut Scope,
606 _errors: &mut ErrorCollector,
607 ) -> String {
608 self.to_string()
609 }
610}
611
612impl Transpile for String {
613 fn transpile(
614 &self,
615 _ctx: &Context,
616 _scope: &mut Scope,
617 _errors: &mut ErrorCollector,
618 ) -> String {
619 self.to_owned()
620 }
621}
622
623impl<T> Transpile for Box<T>
624where
625 T: Transpile,
626{
627 fn transpile(&self, ctx: &Context, scope: &mut Scope, errors: &mut ErrorCollector) -> String {
628 self.as_ref().transpile(ctx, scope, errors)
629 }
630}