1use std::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum Support {
10 Native,
12 SwitchBased,
14 InterfaceBased,
16 Emulated,
18 None,
20}
21
22impl fmt::Display for Support {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 Self::Native => write!(f, "native"),
26 Self::SwitchBased => write!(f, "switch-based"),
27 Self::InterfaceBased => write!(f, "interface-based"),
28 Self::Emulated => write!(f, "emulated"),
29 Self::None => write!(f, "none"),
30 }
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum MemoryModel {
39 GC,
41 ARC,
43 Manual,
45}
46
47impl fmt::Display for MemoryModel {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 Self::GC => write!(f, "GC"),
51 Self::ARC => write!(f, "ARC"),
52 Self::Manual => write!(f, "manual"),
53 }
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
61pub enum AsyncModel {
62 EventLoop,
64 GreenThread,
66 OSThread,
68}
69
70impl fmt::Display for AsyncModel {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match self {
73 Self::EventLoop => write!(f, "event-loop"),
74 Self::GreenThread => write!(f, "green-thread"),
75 Self::OSThread => write!(f, "OS-thread"),
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
84pub enum GenericsModel {
85 Reified,
87 Erased,
89 Monomorphized,
91}
92
93impl fmt::Display for GenericsModel {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 match self {
96 Self::Reified => write!(f, "reified"),
97 Self::Erased => write!(f, "erased"),
98 Self::Monomorphized => write!(f, "monomorphized"),
99 }
100 }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
107pub enum NamingConvention {
108 CamelCase,
110 SnakeCase,
112 PascalCase,
114}
115
116impl fmt::Display for NamingConvention {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 match self {
119 Self::CamelCase => write!(f, "camelCase"),
120 Self::SnakeCase => write!(f, "snake_case"),
121 Self::PascalCase => write!(f, "PascalCase"),
122 }
123 }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
130pub enum ErrorHandling {
131 Exceptions,
133 ResultType,
135 MultipleReturn,
137}
138
139impl fmt::Display for ErrorHandling {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 match self {
142 Self::Exceptions => write!(f, "exceptions"),
143 Self::ResultType => write!(f, "result-type"),
144 Self::MultipleReturn => write!(f, "multiple-return"),
145 }
146 }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
153pub enum IndentStyle {
154 Spaces(u8),
156 Tabs,
158}
159
160#[derive(Debug, Clone, PartialEq, Eq)]
164pub struct TargetCapabilities {
165 pub memory_model: MemoryModel,
167 pub null_safety: bool,
169 pub algebraic_types: Support,
171 pub async_model: AsyncModel,
173 pub generics: GenericsModel,
175 pub first_class_functions: bool,
177 pub pattern_matching: Support,
179 pub traits: Support,
181 pub string_interpolation: Support,
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
189pub struct TargetConventions {
190 pub naming: NamingConvention,
192 pub type_naming: NamingConvention,
194 pub error_handling: ErrorHandling,
196 pub indent: IndentStyle,
198 pub file_extension: String,
200}
201
202#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
211pub enum NodeKindHint {
212 Match,
214 EnumDecl,
216 EnumVariant,
218 HandlingBlock,
220 EffectOp,
222 Ownership,
224 Interpolation,
226 ImplBlock,
228 TraitDecl,
230}
231
232#[derive(Debug, Clone, PartialEq, Eq)]
240pub struct TargetProfile {
241 pub id: String,
243 pub display_name: String,
245 pub capabilities: TargetCapabilities,
247 pub conventions: TargetConventions,
249 pub ai_hints: Vec<NodeKindHint>,
252}
253
254impl TargetProfile {
255 #[must_use]
257 pub fn javascript() -> Self {
258 Self {
259 id: "js".into(),
260 display_name: "JavaScript".into(),
261 capabilities: TargetCapabilities {
262 memory_model: MemoryModel::GC,
263 null_safety: false,
264 algebraic_types: Support::Emulated,
265 async_model: AsyncModel::EventLoop,
266 generics: GenericsModel::Erased,
267 first_class_functions: true,
268 pattern_matching: Support::SwitchBased,
269 traits: Support::Emulated,
270 string_interpolation: Support::Native,
271 },
272 conventions: TargetConventions {
273 naming: NamingConvention::CamelCase,
274 type_naming: NamingConvention::PascalCase,
275 error_handling: ErrorHandling::Exceptions,
276 indent: IndentStyle::Spaces(2),
277 file_extension: "js".into(),
278 },
279 ai_hints: vec![
281 NodeKindHint::EnumDecl,
282 NodeKindHint::EnumVariant,
283 NodeKindHint::Match,
284 NodeKindHint::HandlingBlock,
285 NodeKindHint::EffectOp,
286 NodeKindHint::Ownership,
287 NodeKindHint::TraitDecl,
288 NodeKindHint::ImplBlock,
289 ],
290 }
291 }
292
293 #[must_use]
295 pub fn typescript() -> Self {
296 Self {
297 id: "ts".into(),
298 display_name: "TypeScript".into(),
299 capabilities: TargetCapabilities {
300 memory_model: MemoryModel::GC,
301 null_safety: true,
302 algebraic_types: Support::Emulated,
303 async_model: AsyncModel::EventLoop,
304 generics: GenericsModel::Erased,
305 first_class_functions: true,
306 pattern_matching: Support::None,
307 traits: Support::InterfaceBased,
308 string_interpolation: Support::Native,
309 },
310 conventions: TargetConventions {
311 naming: NamingConvention::CamelCase,
312 type_naming: NamingConvention::PascalCase,
313 error_handling: ErrorHandling::Exceptions,
314 indent: IndentStyle::Spaces(2),
315 file_extension: "ts".into(),
316 },
317 ai_hints: vec![
320 NodeKindHint::Match,
321 NodeKindHint::HandlingBlock,
322 NodeKindHint::EffectOp,
323 NodeKindHint::Ownership,
324 ],
325 }
326 }
327
328 #[must_use]
330 pub fn python() -> Self {
331 Self {
332 id: "python".into(),
333 display_name: "Python".into(),
334 capabilities: TargetCapabilities {
335 memory_model: MemoryModel::GC,
336 null_safety: false,
337 algebraic_types: Support::Emulated,
338 async_model: AsyncModel::OSThread,
339 generics: GenericsModel::Erased,
340 first_class_functions: true,
341 pattern_matching: Support::Native,
342 traits: Support::Emulated,
343 string_interpolation: Support::Native,
344 },
345 conventions: TargetConventions {
346 naming: NamingConvention::SnakeCase,
347 type_naming: NamingConvention::PascalCase,
348 error_handling: ErrorHandling::Exceptions,
349 indent: IndentStyle::Spaces(4),
350 file_extension: "py".into(),
351 },
352 ai_hints: vec![
356 NodeKindHint::HandlingBlock,
357 NodeKindHint::EffectOp,
358 NodeKindHint::Ownership,
359 NodeKindHint::TraitDecl,
360 NodeKindHint::ImplBlock,
361 ],
362 }
363 }
364
365 #[must_use]
367 pub fn rust() -> Self {
368 Self {
369 id: "rust".into(),
370 display_name: "Rust".into(),
371 capabilities: TargetCapabilities {
372 memory_model: MemoryModel::Manual,
373 null_safety: true,
374 algebraic_types: Support::Native,
375 async_model: AsyncModel::OSThread,
376 generics: GenericsModel::Monomorphized,
377 first_class_functions: true,
378 pattern_matching: Support::Native,
379 traits: Support::Native,
380 string_interpolation: Support::Emulated,
381 },
382 conventions: TargetConventions {
383 naming: NamingConvention::SnakeCase,
384 type_naming: NamingConvention::PascalCase,
385 error_handling: ErrorHandling::ResultType,
386 indent: IndentStyle::Spaces(4),
387 file_extension: "rs".into(),
388 },
389 ai_hints: vec![
393 NodeKindHint::HandlingBlock,
394 NodeKindHint::EffectOp,
395 NodeKindHint::Interpolation,
396 ],
397 }
398 }
399
400 #[must_use]
402 pub fn go() -> Self {
403 Self {
404 id: "go".into(),
405 display_name: "Go".into(),
406 capabilities: TargetCapabilities {
407 memory_model: MemoryModel::GC,
408 null_safety: false,
409 algebraic_types: Support::Emulated,
410 async_model: AsyncModel::GreenThread,
411 generics: GenericsModel::Reified,
412 first_class_functions: true,
413 pattern_matching: Support::None,
414 traits: Support::InterfaceBased,
415 string_interpolation: Support::Emulated,
416 },
417 conventions: TargetConventions {
418 naming: NamingConvention::CamelCase,
419 type_naming: NamingConvention::PascalCase,
420 error_handling: ErrorHandling::MultipleReturn,
421 indent: IndentStyle::Tabs,
422 file_extension: "go".into(),
423 },
424 ai_hints: vec![
426 NodeKindHint::EnumDecl,
427 NodeKindHint::EnumVariant,
428 NodeKindHint::Match,
429 NodeKindHint::HandlingBlock,
430 NodeKindHint::EffectOp,
431 NodeKindHint::Ownership,
432 NodeKindHint::Interpolation,
433 NodeKindHint::TraitDecl,
434 NodeKindHint::ImplBlock,
435 ],
436 }
437 }
438
439 #[must_use]
441 pub fn all_builtins() -> Vec<Self> {
442 vec![
443 Self::javascript(),
444 Self::typescript(),
445 Self::python(),
446 Self::rust(),
447 Self::go(),
448 ]
449 }
450
451 #[must_use]
453 pub fn from_id(id: &str) -> Option<Self> {
454 match id {
455 "js" | "javascript" => Some(Self::javascript()),
456 "ts" | "typescript" => Some(Self::typescript()),
457 "python" | "py" => Some(Self::python()),
458 "rust" | "rs" => Some(Self::rust()),
459 "go" | "golang" => Some(Self::go()),
460 _ => None,
461 }
462 }
463}
464
465impl fmt::Display for TargetProfile {
466 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467 write!(f, "{} ({})", self.display_name, self.id)
468 }
469}
470
471#[must_use]
480pub fn classify_node(node: &bock_air::AIRNode) -> Option<NodeKindHint> {
481 use bock_air::NodeKind;
482 match &node.kind {
483 NodeKind::Match { .. } => Some(NodeKindHint::Match),
484 NodeKind::EnumDecl { .. } => Some(NodeKindHint::EnumDecl),
485 NodeKind::EnumVariant { .. } => Some(NodeKindHint::EnumVariant),
486 NodeKind::HandlingBlock { .. } => Some(NodeKindHint::HandlingBlock),
487 NodeKind::EffectOp { .. } => Some(NodeKindHint::EffectOp),
488 NodeKind::Move { .. } | NodeKind::Borrow { .. } | NodeKind::MutableBorrow { .. } => {
489 Some(NodeKindHint::Ownership)
490 }
491 NodeKind::Interpolation { .. } => Some(NodeKindHint::Interpolation),
492 NodeKind::ImplBlock { .. } => Some(NodeKindHint::ImplBlock),
493 NodeKind::TraitDecl { .. } => Some(NodeKindHint::TraitDecl),
494 _ => None,
495 }
496}
497
498#[cfg(test)]
501mod tests {
502 use super::*;
503
504 #[test]
505 fn all_builtins_has_five_targets() {
506 let profiles = TargetProfile::all_builtins();
507 assert_eq!(profiles.len(), 5);
508
509 let ids: Vec<&str> = profiles.iter().map(|p| p.id.as_str()).collect();
510 assert!(ids.contains(&"js"));
511 assert!(ids.contains(&"ts"));
512 assert!(ids.contains(&"python"));
513 assert!(ids.contains(&"rust"));
514 assert!(ids.contains(&"go"));
515 }
516
517 #[test]
518 fn from_id_resolves_aliases() {
519 assert_eq!(TargetProfile::from_id("js").unwrap().id, "js");
520 assert_eq!(TargetProfile::from_id("javascript").unwrap().id, "js");
521 assert_eq!(TargetProfile::from_id("ts").unwrap().id, "ts");
522 assert_eq!(TargetProfile::from_id("typescript").unwrap().id, "ts");
523 assert_eq!(TargetProfile::from_id("python").unwrap().id, "python");
524 assert_eq!(TargetProfile::from_id("py").unwrap().id, "python");
525 assert_eq!(TargetProfile::from_id("rust").unwrap().id, "rust");
526 assert_eq!(TargetProfile::from_id("rs").unwrap().id, "rust");
527 assert_eq!(TargetProfile::from_id("go").unwrap().id, "go");
528 assert_eq!(TargetProfile::from_id("golang").unwrap().id, "go");
529 assert!(TargetProfile::from_id("unknown").is_none());
530 }
531
532 #[test]
533 fn js_profile_capabilities() {
534 let js = TargetProfile::javascript();
535 assert_eq!(js.capabilities.memory_model, MemoryModel::GC);
536 assert!(!js.capabilities.null_safety);
537 assert_eq!(js.capabilities.algebraic_types, Support::Emulated);
538 assert_eq!(js.capabilities.async_model, AsyncModel::EventLoop);
539 assert_eq!(js.capabilities.generics, GenericsModel::Erased);
540 assert!(js.capabilities.first_class_functions);
541 assert_eq!(js.capabilities.pattern_matching, Support::SwitchBased);
542 assert_eq!(js.capabilities.string_interpolation, Support::Native);
543 }
544
545 #[test]
546 fn rust_profile_capabilities() {
547 let rust = TargetProfile::rust();
548 assert_eq!(rust.capabilities.memory_model, MemoryModel::Manual);
549 assert!(rust.capabilities.null_safety);
550 assert_eq!(rust.capabilities.algebraic_types, Support::Native);
551 assert_eq!(rust.capabilities.generics, GenericsModel::Monomorphized);
552 assert_eq!(rust.capabilities.pattern_matching, Support::Native);
553 assert_eq!(rust.capabilities.traits, Support::Native);
554 }
555
556 #[test]
557 fn go_profile_capabilities() {
558 let go = TargetProfile::go();
559 assert_eq!(go.capabilities.memory_model, MemoryModel::GC);
560 assert_eq!(go.capabilities.async_model, AsyncModel::GreenThread);
561 assert_eq!(go.capabilities.pattern_matching, Support::None);
562 assert_eq!(go.capabilities.traits, Support::InterfaceBased);
563 assert_eq!(go.conventions.error_handling, ErrorHandling::MultipleReturn);
564 }
565
566 #[test]
567 fn python_profile_conventions() {
568 let py = TargetProfile::python();
569 assert_eq!(py.conventions.naming, NamingConvention::SnakeCase);
570 assert_eq!(py.conventions.type_naming, NamingConvention::PascalCase);
571 assert_eq!(py.conventions.indent, IndentStyle::Spaces(4));
572 assert_eq!(py.conventions.file_extension, "py");
573 }
574
575 #[test]
576 fn ts_profile_has_null_safety_and_erased_generics() {
577 let ts = TargetProfile::typescript();
578 assert!(ts.capabilities.null_safety);
579 assert_eq!(ts.capabilities.generics, GenericsModel::Erased);
580 assert_eq!(ts.capabilities.traits, Support::InterfaceBased);
581 }
582
583 #[test]
584 fn display_format() {
585 let js = TargetProfile::javascript();
586 assert_eq!(format!("{js}"), "JavaScript (js)");
587 }
588
589 #[test]
590 fn support_display() {
591 assert_eq!(format!("{}", Support::Native), "native");
592 assert_eq!(format!("{}", Support::SwitchBased), "switch-based");
593 assert_eq!(format!("{}", Support::InterfaceBased), "interface-based");
594 assert_eq!(format!("{}", Support::Emulated), "emulated");
595 assert_eq!(format!("{}", Support::None), "none");
596 }
597
598 #[test]
601 fn spec_js_profile() {
602 let p = TargetProfile::javascript();
603 let c = &p.capabilities;
604 assert_eq!(c.memory_model, MemoryModel::GC);
605 assert!(!c.null_safety);
606 assert_eq!(c.algebraic_types, Support::Emulated);
607 assert_eq!(c.async_model, AsyncModel::EventLoop);
608 assert_eq!(c.generics, GenericsModel::Erased);
609 assert!(c.first_class_functions);
610 assert_eq!(c.pattern_matching, Support::SwitchBased);
611 assert_eq!(c.traits, Support::Emulated);
612 assert_eq!(c.string_interpolation, Support::Native);
613 assert_eq!(p.conventions.naming, NamingConvention::CamelCase);
615 assert_eq!(p.conventions.type_naming, NamingConvention::PascalCase);
616 assert_eq!(p.conventions.error_handling, ErrorHandling::Exceptions);
617 assert_eq!(p.conventions.indent, IndentStyle::Spaces(2));
618 assert_eq!(p.conventions.file_extension, "js");
619 }
620
621 #[test]
622 fn spec_ts_profile() {
623 let p = TargetProfile::typescript();
624 let c = &p.capabilities;
625 assert_eq!(c.memory_model, MemoryModel::GC);
626 assert!(c.null_safety);
627 assert_eq!(c.algebraic_types, Support::Emulated);
628 assert_eq!(c.async_model, AsyncModel::EventLoop);
629 assert_eq!(c.generics, GenericsModel::Erased);
630 assert!(c.first_class_functions);
631 assert_eq!(c.pattern_matching, Support::None);
632 assert_eq!(c.traits, Support::InterfaceBased);
633 assert_eq!(c.string_interpolation, Support::Native);
634 assert_eq!(p.conventions.naming, NamingConvention::CamelCase);
636 assert_eq!(p.conventions.type_naming, NamingConvention::PascalCase);
637 assert_eq!(p.conventions.error_handling, ErrorHandling::Exceptions);
638 assert_eq!(p.conventions.indent, IndentStyle::Spaces(2));
639 assert_eq!(p.conventions.file_extension, "ts");
640 }
641
642 #[test]
643 fn spec_python_profile() {
644 let p = TargetProfile::python();
645 let c = &p.capabilities;
646 assert_eq!(c.memory_model, MemoryModel::GC);
647 assert!(!c.null_safety);
648 assert_eq!(c.algebraic_types, Support::Emulated);
649 assert_eq!(c.async_model, AsyncModel::OSThread);
650 assert_eq!(c.generics, GenericsModel::Erased);
651 assert!(c.first_class_functions);
652 assert_eq!(c.pattern_matching, Support::Native);
653 assert_eq!(c.traits, Support::Emulated);
654 assert_eq!(c.string_interpolation, Support::Native);
655 assert_eq!(p.conventions.naming, NamingConvention::SnakeCase);
657 assert_eq!(p.conventions.type_naming, NamingConvention::PascalCase);
658 assert_eq!(p.conventions.error_handling, ErrorHandling::Exceptions);
659 assert_eq!(p.conventions.indent, IndentStyle::Spaces(4));
660 assert_eq!(p.conventions.file_extension, "py");
661 }
662
663 #[test]
664 fn spec_rust_profile() {
665 let p = TargetProfile::rust();
666 let c = &p.capabilities;
667 assert_eq!(c.memory_model, MemoryModel::Manual);
668 assert!(c.null_safety);
669 assert_eq!(c.algebraic_types, Support::Native);
670 assert_eq!(c.async_model, AsyncModel::OSThread);
671 assert_eq!(c.generics, GenericsModel::Monomorphized);
672 assert!(c.first_class_functions);
673 assert_eq!(c.pattern_matching, Support::Native);
674 assert_eq!(c.traits, Support::Native);
675 assert_eq!(c.string_interpolation, Support::Emulated);
676 assert_eq!(p.conventions.naming, NamingConvention::SnakeCase);
678 assert_eq!(p.conventions.type_naming, NamingConvention::PascalCase);
679 assert_eq!(p.conventions.error_handling, ErrorHandling::ResultType);
680 assert_eq!(p.conventions.indent, IndentStyle::Spaces(4));
681 assert_eq!(p.conventions.file_extension, "rs");
682 }
683
684 #[test]
685 fn spec_go_profile() {
686 let p = TargetProfile::go();
687 let c = &p.capabilities;
688 assert_eq!(c.memory_model, MemoryModel::GC);
689 assert!(!c.null_safety);
690 assert_eq!(c.algebraic_types, Support::Emulated);
691 assert_eq!(c.async_model, AsyncModel::GreenThread);
692 assert_eq!(c.generics, GenericsModel::Reified);
693 assert!(c.first_class_functions);
694 assert_eq!(c.pattern_matching, Support::None);
695 assert_eq!(c.traits, Support::InterfaceBased);
696 assert_eq!(c.string_interpolation, Support::Emulated);
697 assert_eq!(p.conventions.naming, NamingConvention::CamelCase);
699 assert_eq!(p.conventions.type_naming, NamingConvention::PascalCase);
700 assert_eq!(p.conventions.error_handling, ErrorHandling::MultipleReturn);
701 assert_eq!(p.conventions.indent, IndentStyle::Tabs);
702 assert_eq!(p.conventions.file_extension, "go");
703 }
704}