1use crate::lcnf::*;
6
7use super::types::{
8 AttributeBuilder, BeamBackend, BeamConstPool, BeamDeadEliminator, BeamEndian, BeamExpr,
9 BeamFunction, BeamInstr, BeamLinker, BeamModule, BeamPattern, BeamPrinter, BeamProcess,
10 BeamReg, BeamType, BeamTypeCtx, EtsAccess, EtsTable, EtsType, GenServerSpec, PatternNormalizer,
11 TailCallInfo, XRegAllocator,
12};
13
14pub fn emit_instr(instr: &BeamInstr) -> String {
16 match instr {
17 BeamInstr::Label(l) => format!("label {}.", l),
18 BeamInstr::FuncInfo {
19 module,
20 function,
21 arity,
22 } => {
23 format!("func_info '{}' '{}' {}.", module, function, arity)
24 }
25 BeamInstr::Call { arity, label } => format!("call {} {}.", arity, label),
26 BeamInstr::CallLast {
27 arity,
28 label,
29 deallocate,
30 } => {
31 format!("call_last {} {} {}.", arity, label, deallocate)
32 }
33 BeamInstr::CallExt { arity, destination } => {
34 format!(
35 "call_ext {} {{extfunc, '{}', '{}', {}}}.",
36 arity, destination.module, destination.function, destination.arity
37 )
38 }
39 BeamInstr::CallExtLast {
40 arity,
41 destination,
42 deallocate,
43 } => {
44 format!(
45 "call_ext_last {} {{extfunc, '{}', '{}', {}}} {}.",
46 arity, destination.module, destination.function, destination.arity, deallocate
47 )
48 }
49 BeamInstr::CallFun { arity } => format!("call_fun {}.", arity),
50 BeamInstr::Move { src, dst } => format!("move {} {}.", src, dst),
51 BeamInstr::PutTuple { arity, dst } => format!("put_tuple {} {}.", arity, dst),
52 BeamInstr::Put(val) => format!("put {}.", val),
53 BeamInstr::GetTupleElement { src, index, dst } => {
54 format!("get_tuple_element {} {} {}.", src, index, dst)
55 }
56 BeamInstr::SetTupleElement {
57 value,
58 tuple,
59 index,
60 } => {
61 format!("set_tuple_element {} {} {}.", value, tuple, index)
62 }
63 BeamInstr::IsEq { fail, lhs, rhs } => format!("is_eq {} {} {}.", fail, lhs, rhs),
64 BeamInstr::IsEqExact { fail, lhs, rhs } => {
65 format!("is_eq_exact {} {} {}.", fail, lhs, rhs)
66 }
67 BeamInstr::IsNe { fail, lhs, rhs } => format!("is_ne {} {} {}.", fail, lhs, rhs),
68 BeamInstr::IsLt { fail, lhs, rhs } => format!("is_lt {} {} {}.", fail, lhs, rhs),
69 BeamInstr::IsGe { fail, lhs, rhs } => format!("is_ge {} {} {}.", fail, lhs, rhs),
70 BeamInstr::IsInteger { fail, arg } => format!("is_integer {} {}.", fail, arg),
71 BeamInstr::IsFloat { fail, arg } => format!("is_float {} {}.", fail, arg),
72 BeamInstr::IsAtom { fail, arg } => format!("is_atom {} {}.", fail, arg),
73 BeamInstr::IsNil { fail, arg } => format!("is_nil {} {}.", fail, arg),
74 BeamInstr::IsList { fail, arg } => format!("is_list {} {}.", fail, arg),
75 BeamInstr::IsTuple { fail, arg } => format!("is_tuple {} {}.", fail, arg),
76 BeamInstr::IsBinary { fail, arg } => format!("is_binary {} {}.", fail, arg),
77 BeamInstr::IsFunction { fail, arg } => format!("is_function {} {}.", fail, arg),
78 BeamInstr::Jump(l) => format!("jump {}.", l),
79 BeamInstr::Return => "return.".to_string(),
80 BeamInstr::Send => "send.".to_string(),
81 BeamInstr::RemoveMessage => "remove_message.".to_string(),
82 BeamInstr::LoopRec { fail, dst } => format!("loop_rec {} {}.", fail, dst),
83 BeamInstr::Wait(l) => format!("wait {}.", l),
84 BeamInstr::WaitTimeout { fail, timeout } => {
85 format!("wait_timeout {} {}.", fail, timeout)
86 }
87 BeamInstr::GcBif {
88 name,
89 fail,
90 live,
91 args,
92 dst,
93 } => {
94 let args_str = args
95 .iter()
96 .map(|a| a.to_string())
97 .collect::<Vec<_>>()
98 .join(", ");
99 format!(
100 "gc_bif '{}' {} {} [{}] {}.",
101 name, fail, live, args_str, dst
102 )
103 }
104 BeamInstr::Bif0 { name, dst } => format!("bif '{}' {}.", name, dst),
105 BeamInstr::Allocate { stack_need, live } => {
106 format!("allocate {} {}.", stack_need, live)
107 }
108 BeamInstr::Deallocate(n) => format!("deallocate {}.", n),
109 BeamInstr::Init(r) => format!("init {}.", r),
110 BeamInstr::MakeFun2(idx) => format!("make_fun2 {}.", idx),
111 BeamInstr::GetList { src, head, tail } => {
112 format!("get_list {} {} {}.", src, head, tail)
113 }
114 BeamInstr::PutList { head, tail, dst } => {
115 format!("put_list {} {} {}.", head, tail, dst)
116 }
117 BeamInstr::Raise { class, reason } => format!("raise {} {}.", class, reason),
118 BeamInstr::TryBegin { label, reg } => format!("try {} {}.", label, reg),
119 BeamInstr::TryEnd(r) => format!("try_end {}.", r),
120 BeamInstr::TryCase(r) => format!("try_case {}.", r),
121 BeamInstr::Comment(s) => format!("%% {}", s),
122 }
123}
124pub fn sanitize_atom(s: &str) -> String {
126 let mut out = String::new();
127 for (i, ch) in s.chars().enumerate() {
128 if ch.is_alphanumeric() || ch == '_' {
129 if i == 0 && ch.is_uppercase() {
130 for c in ch.to_lowercase() {
131 out.push(c);
132 }
133 } else {
134 out.push(ch);
135 }
136 } else {
137 out.push('_');
138 }
139 }
140 if out.is_empty() {
141 out.push_str("unnamed");
142 }
143 out
144}
145#[cfg(test)]
146mod tests {
147 use super::*;
148 pub(super) fn make_backend() -> BeamBackend {
149 BeamBackend::new("test_module")
150 }
151 #[test]
152 pub(super) fn test_beam_type_display_primitives() {
153 assert_eq!(BeamType::Integer.to_string(), "integer()");
154 assert_eq!(BeamType::Float.to_string(), "float()");
155 assert_eq!(BeamType::Atom.to_string(), "atom()");
156 assert_eq!(BeamType::Pid.to_string(), "pid()");
157 assert_eq!(BeamType::Any.to_string(), "any()");
158 assert_eq!(BeamType::None.to_string(), "none()");
159 }
160 #[test]
161 pub(super) fn test_beam_type_display_compound() {
162 let list_ty = BeamType::List(Box::new(BeamType::Integer));
163 assert_eq!(list_ty.to_string(), "list(integer())");
164 let tuple_ty = BeamType::Tuple(vec![BeamType::Atom, BeamType::Integer]);
165 assert_eq!(tuple_ty.to_string(), "{atom(), integer()}");
166 let map_ty = BeamType::Map(Box::new(BeamType::Atom), Box::new(BeamType::Any));
167 assert_eq!(map_ty.to_string(), "#{atom() => any()}");
168 }
169 #[test]
170 pub(super) fn test_beam_type_fun_display() {
171 let fun_ty = BeamType::Fun(
172 vec![BeamType::Integer, BeamType::Integer],
173 Box::new(BeamType::Integer),
174 );
175 assert_eq!(
176 fun_ty.to_string(),
177 "fun((integer(), integer()) -> integer())"
178 );
179 }
180 #[test]
181 pub(super) fn test_beam_module_creation() {
182 let mut m = BeamModule::new("my_app");
183 m.add_attribute("author", "oxilean");
184 assert_eq!(m.name, "my_app");
185 assert_eq!(m.attributes.len(), 1);
186 assert_eq!(m.exports.len(), 0);
187 }
188 #[test]
189 pub(super) fn test_beam_module_add_exported_function() {
190 let mut m = BeamModule::new("my_mod");
191 let mut f = BeamFunction::new("add", 2);
192 f.export();
193 m.add_function(f);
194 assert_eq!(m.exports.len(), 1);
195 assert_eq!(m.export_list(), vec!["add/2"]);
196 }
197 #[test]
198 pub(super) fn test_emit_literal_nat() {
199 let backend = make_backend();
200 let lit = LcnfLit::Nat(42);
201 match backend.emit_literal(&lit) {
202 BeamExpr::LitInt(n) => assert_eq!(n, 42),
203 other => panic!("Expected LitInt, got {:?}", other),
204 }
205 }
206 #[test]
207 pub(super) fn test_emit_literal_str() {
208 let backend = make_backend();
209 match backend.emit_literal(&LcnfLit::Str("hello".to_string())) {
210 BeamExpr::LitString(s) => assert_eq!(s, "hello"),
211 other => panic!("Expected LitString, got {:?}", other),
212 }
213 }
214 #[test]
215 pub(super) fn test_sanitize_atom() {
216 assert_eq!(sanitize_atom("hello"), "hello");
217 assert_eq!(sanitize_atom("Hello"), "hello");
218 assert_eq!(sanitize_atom("my.func"), "my_func");
219 assert_eq!(sanitize_atom(""), "unnamed");
220 assert_eq!(sanitize_atom("add_two"), "add_two");
221 }
222 #[test]
223 pub(super) fn test_emit_asm_contains_module() {
224 let mut backend = make_backend();
225 let mut f = BeamFunction::new("main", 0);
226 f.export();
227 backend.module.add_function(f);
228 let asm = backend.emit_asm();
229 assert!(asm.contains("test_module"));
230 assert!(asm.contains("main"));
231 }
232 #[test]
233 pub(super) fn test_emit_instr_move() {
234 let instr = BeamInstr::Move {
235 src: BeamReg::X(0),
236 dst: BeamReg::Y(0),
237 };
238 assert_eq!(emit_instr(&instr), "move x(0) y(0).");
239 }
240 #[test]
241 pub(super) fn test_emit_instr_call() {
242 let instr = BeamInstr::Call { arity: 2, label: 5 };
243 assert_eq!(emit_instr(&instr), "call 2 5.");
244 }
245 #[test]
246 pub(super) fn test_beam_function_key() {
247 let f = BeamFunction::new("factorial", 1);
248 assert_eq!(f.key(), "factorial/1");
249 }
250 #[test]
251 pub(super) fn test_emit_var_arg() {
252 let mut backend = make_backend();
253 let arg = LcnfArg::Var(LcnfVarId(7));
254 match backend.emit_arg(&arg) {
255 BeamExpr::Var(name) => assert!(name.contains('7')),
256 other => panic!("Expected Var, got {:?}", other),
257 }
258 }
259}
260#[allow(dead_code)]
262pub fn patterns_structurally_equal(a: &BeamPattern, b: &BeamPattern) -> bool {
263 match (a, b) {
264 (BeamPattern::Wildcard, BeamPattern::Wildcard) => true,
265 (BeamPattern::Var(_), BeamPattern::Var(_)) => true,
266 (BeamPattern::Nil, BeamPattern::Nil) => true,
267 (BeamPattern::LitInt(x), BeamPattern::LitInt(y)) => x == y,
268 (BeamPattern::LitAtom(x), BeamPattern::LitAtom(y)) => x == y,
269 (BeamPattern::Cons(ah, at), BeamPattern::Cons(bh, bt)) => {
270 patterns_structurally_equal(ah, bh) && patterns_structurally_equal(at, bt)
271 }
272 (BeamPattern::Tuple(ap), BeamPattern::Tuple(bp)) => {
273 ap.len() == bp.len()
274 && ap
275 .iter()
276 .zip(bp.iter())
277 .all(|(x, y)| patterns_structurally_equal(x, y))
278 }
279 _ => false,
280 }
281}
282#[allow(dead_code)]
284pub fn analyse_tail_calls(expr: &BeamExpr, func_name: &str) -> TailCallInfo {
285 let mut info = TailCallInfo::new();
286 collect_tail_calls_expr(expr, func_name, &mut info);
287 info
288}
289pub(super) fn collect_tail_calls_expr(expr: &BeamExpr, func_name: &str, info: &mut TailCallInfo) {
290 match expr {
291 BeamExpr::Apply { fun, .. } => {
292 if let BeamExpr::Var(name) = fun.as_ref() {
293 if name == func_name {
294 info.add_self_tail();
295 } else {
296 info.add_external_tail(name.clone());
297 }
298 }
299 }
300 BeamExpr::Call { func, .. } => {
301 if func == func_name {
302 info.add_self_tail();
303 } else {
304 info.add_external_tail(func.clone());
305 }
306 }
307 BeamExpr::Let { body, .. } => {
308 collect_tail_calls_expr(body, func_name, info);
309 }
310 BeamExpr::Case { clauses, .. } => {
311 for clause in clauses {
312 collect_tail_calls_expr(&clause.body, func_name, info);
313 }
314 }
315 BeamExpr::Seq(_, second) => {
316 collect_tail_calls_expr(second, func_name, info);
317 }
318 _ => {}
319 }
320}
321#[cfg(test)]
322mod extended_tests {
323 use super::*;
324 #[test]
325 pub(super) fn test_ets_table_type_display() {
326 assert_eq!(EtsType::Set.to_string(), "set");
327 assert_eq!(EtsType::OrderedSet.to_string(), "ordered_set");
328 assert_eq!(EtsType::Bag.to_string(), "bag");
329 assert_eq!(EtsType::DuplicateBag.to_string(), "duplicate_bag");
330 }
331 #[test]
332 pub(super) fn test_ets_access_display() {
333 assert_eq!(EtsAccess::Private.to_string(), "private");
334 assert_eq!(EtsAccess::Protected.to_string(), "protected");
335 assert_eq!(EtsAccess::Public.to_string(), "public");
336 }
337 #[test]
338 pub(super) fn test_ets_table_emit_new() {
339 let table = EtsTable::new_set("my_table");
340 let expr = table.emit_new();
341 match expr {
342 BeamExpr::Call { func, .. } => assert_eq!(func, "new"),
343 _ => panic!("expected Call"),
344 }
345 }
346 #[test]
347 pub(super) fn test_ets_table_emit_insert() {
348 let table = EtsTable::new_set("test");
349 let tuple = BeamExpr::Tuple(vec![BeamExpr::LitAtom("key".into()), BeamExpr::LitInt(42)]);
350 match table.emit_insert(tuple) {
351 BeamExpr::Call { func, .. } => assert_eq!(func, "insert"),
352 _ => panic!("expected Call"),
353 }
354 }
355 #[test]
356 pub(super) fn test_ets_table_emit_lookup() {
357 let table = EtsTable::new_set("test");
358 let key = BeamExpr::LitAtom("key".into());
359 match table.emit_lookup(key) {
360 BeamExpr::Call { func, .. } => assert_eq!(func, "lookup"),
361 _ => panic!("expected Call"),
362 }
363 }
364 #[test]
365 pub(super) fn test_ets_table_emit_delete() {
366 let table = EtsTable::new_set("test");
367 let key = BeamExpr::LitAtom("key".into());
368 match table.emit_delete(key) {
369 BeamExpr::Call { func, .. } => assert_eq!(func, "delete"),
370 _ => panic!("expected Call"),
371 }
372 }
373 #[test]
374 pub(super) fn test_beam_process_spawn_expr() {
375 let proc = BeamProcess::new("p1", "my_mod", "start")
376 .with_arg(BeamExpr::LitInt(0))
377 .linked();
378 let spawn = proc.emit_spawn();
379 match spawn {
380 BeamExpr::Call { func, .. } => assert_eq!(func, "spawn_link"),
381 _ => panic!("expected Call"),
382 }
383 }
384 #[test]
385 pub(super) fn test_beam_process_spawn_no_link() {
386 let proc = BeamProcess::new("p2", "mod", "init");
387 match proc.emit_spawn() {
388 BeamExpr::Call { func, .. } => assert_eq!(func, "spawn"),
389 _ => panic!("expected Call"),
390 }
391 }
392 #[test]
393 pub(super) fn test_gen_server_generate_module() {
394 let spec = GenServerSpec::new("counter", BeamExpr::LitInt(0));
395 let module = spec.generate_module();
396 assert_eq!(module.name, "counter");
397 assert!(!module.functions.is_empty());
398 assert!(module.functions.iter().any(|f| f.name == "init"));
399 assert!(module.functions.iter().any(|f| f.name == "handle_call"));
400 }
401 #[test]
402 pub(super) fn test_gen_server_emit_init() {
403 let spec = GenServerSpec::new("myserver", BeamExpr::LitAtom("idle".into()));
404 let s = spec.emit_init();
405 assert!(s.contains("init"));
406 assert!(s.contains("ok"));
407 }
408 #[test]
409 pub(super) fn test_beam_type_ctx_bind_lookup() {
410 let mut ctx = BeamTypeCtx::new();
411 ctx.bind("x", BeamType::Integer);
412 assert_eq!(ctx.lookup("x"), Some(&BeamType::Integer));
413 assert_eq!(ctx.lookup("y"), None);
414 }
415 #[test]
416 pub(super) fn test_beam_type_ctx_infer_literals() {
417 let ctx = BeamTypeCtx::new();
418 assert_eq!(ctx.infer(&BeamExpr::LitInt(1)), BeamType::Integer);
419 assert_eq!(ctx.infer(&BeamExpr::LitFloat(1.0)), BeamType::Float);
420 assert_eq!(ctx.infer(&BeamExpr::LitAtom("ok".into())), BeamType::Atom);
421 }
422 #[test]
423 pub(super) fn test_beam_type_ctx_infer_var() {
424 let mut ctx = BeamTypeCtx::new();
425 ctx.bind("n", BeamType::Integer);
426 assert_eq!(ctx.infer(&BeamExpr::Var("n".into())), BeamType::Integer);
427 assert_eq!(ctx.infer(&BeamExpr::Var("unknown".into())), BeamType::Any);
428 }
429 #[test]
430 pub(super) fn test_beam_type_ctx_infer_tuple() {
431 let ctx = BeamTypeCtx::new();
432 let expr = BeamExpr::Tuple(vec![BeamExpr::LitAtom("ok".into()), BeamExpr::LitInt(1)]);
433 let ty = ctx.infer(&expr);
434 assert!(matches!(ty, BeamType::Tuple(_)));
435 }
436 #[test]
437 pub(super) fn test_beam_type_ctx_merge() {
438 let mut a = BeamTypeCtx::new();
439 a.bind("x", BeamType::Integer);
440 let mut b = BeamTypeCtx::new();
441 b.bind("x", BeamType::Float);
442 b.bind("y", BeamType::Atom);
443 let merged = a.merge(&b);
444 assert_eq!(merged.lookup("x"), Some(&BeamType::Any));
445 assert_eq!(merged.lookup("y"), Some(&BeamType::Atom));
446 }
447 #[test]
448 pub(super) fn test_pattern_normalizer_wildcard() {
449 let mut norm = PatternNormalizer::new();
450 let p = norm.normalize(BeamPattern::Var("_Whatever".into()));
451 assert!(matches!(p, BeamPattern::Var(_)));
452 }
453 #[test]
454 pub(super) fn test_patterns_structurally_equal() {
455 let a = BeamPattern::Tuple(vec![
456 BeamPattern::LitAtom("ok".into()),
457 BeamPattern::Var("x".into()),
458 ]);
459 let b = BeamPattern::Tuple(vec![
460 BeamPattern::LitAtom("ok".into()),
461 BeamPattern::Var("y".into()),
462 ]);
463 assert!(patterns_structurally_equal(&a, &b));
464 }
465 #[test]
466 pub(super) fn test_patterns_structurally_not_equal() {
467 let a = BeamPattern::LitInt(1);
468 let b = BeamPattern::LitInt(2);
469 assert!(!patterns_structurally_equal(&a, &b));
470 }
471 #[test]
472 pub(super) fn test_beam_linker_merge() {
473 let mut m1 = BeamModule::new("mod_a");
474 m1.add_function(BeamFunction::new("foo", 0));
475 let mut m2 = BeamModule::new("mod_b");
476 m2.add_function(BeamFunction::new("bar", 1));
477 let mut linker = BeamLinker::new("combined");
478 linker.add_module(m1);
479 linker.add_module(m2);
480 let merged = linker.link();
481 assert_eq!(merged.name, "combined");
482 assert_eq!(merged.functions.len(), 2);
483 }
484 #[test]
485 pub(super) fn test_beam_linker_rename() {
486 let mut m = BeamModule::new("mod");
487 m.add_function(BeamFunction::new("helper", 1));
488 let mut linker = BeamLinker::new("output");
489 linker.rename("mod", "helper", "mod_helper");
490 linker.add_module(m);
491 let merged = linker.link();
492 assert!(merged.functions.iter().any(|f| f.name == "mod_helper"));
493 }
494 #[test]
495 pub(super) fn test_beam_dead_eliminator_seed_and_eliminate() {
496 let mut module = BeamModule::new("test");
497 let mut exported = BeamFunction::new("public_fn", 0);
498 exported.export();
499 module.add_function(exported);
500 module.add_function(BeamFunction::new("private_fn", 0));
501 let mut elim = BeamDeadEliminator::new();
502 elim.seed_exports(&module);
503 let after = elim.eliminate(module.clone());
504 assert!(!after.functions.is_empty());
505 }
506 #[test]
507 pub(super) fn test_beam_const_pool_intern() {
508 let mut pool = BeamConstPool::new();
509 let idx1 = pool.intern(BeamExpr::LitInt(42), "forty_two");
510 let idx2 = pool.intern(BeamExpr::LitInt(99), "ninety_nine");
511 let idx1_again = pool.intern(BeamExpr::LitInt(0), "forty_two");
512 assert_eq!(idx1, 0);
513 assert_eq!(idx2, 1);
514 assert_eq!(idx1_again, idx1);
515 assert_eq!(pool.len(), 2);
516 }
517 #[test]
518 pub(super) fn test_beam_const_pool_get() {
519 let mut pool = BeamConstPool::new();
520 let idx = pool.intern(BeamExpr::LitAtom("hello".into()), "greeting");
521 let retrieved = pool.get(idx);
522 assert!(retrieved.is_some());
523 assert!(
524 matches!(retrieved.expect("value should be Some/Ok"), BeamExpr::LitAtom(s) if s == "hello")
525 );
526 }
527 #[test]
528 pub(super) fn test_attribute_builder() {
529 let attrs = AttributeBuilder::new()
530 .vsn("1.0.0")
531 .author("oxilean")
532 .compile("debug_info")
533 .build();
534 assert_eq!(attrs.len(), 3);
535 assert_eq!(attrs[0], ("vsn".into(), "1.0.0".into()));
536 }
537 #[test]
538 pub(super) fn test_attribute_builder_apply() {
539 let mut module = BeamModule::new("mymod");
540 AttributeBuilder::new().author("test").apply(&mut module);
541 assert_eq!(module.attributes.len(), 1);
542 }
543 #[test]
544 pub(super) fn test_tail_call_info_self() {
545 let mut info = TailCallInfo::new();
546 info.add_self_tail();
547 assert!(info.is_tail_recursive);
548 assert_eq!(info.self_tail_calls, 1);
549 assert!(info.has_tail_calls());
550 }
551 #[test]
552 pub(super) fn test_analyse_tail_calls_apply() {
553 let expr = BeamExpr::Apply {
554 fun: Box::new(BeamExpr::Var("fact".into())),
555 args: vec![BeamExpr::LitInt(10)],
556 };
557 let info = analyse_tail_calls(&expr, "fact");
558 assert_eq!(info.self_tail_calls, 1);
559 }
560 #[test]
561 pub(super) fn test_analyse_tail_calls_let() {
562 let expr = BeamExpr::Let {
563 var: "x".into(),
564 value: Box::new(BeamExpr::LitInt(1)),
565 body: Box::new(BeamExpr::Apply {
566 fun: Box::new(BeamExpr::Var("go".into())),
567 args: vec![],
568 }),
569 };
570 let info = analyse_tail_calls(&expr, "go");
571 assert!(info.is_tail_recursive);
572 }
573 #[test]
574 pub(super) fn test_beam_printer_lit_int() {
575 let mut p = BeamPrinter::new();
576 p.print_expr(&BeamExpr::LitInt(42));
577 assert_eq!(p.finish(), "42");
578 }
579 #[test]
580 pub(super) fn test_beam_printer_var() {
581 let mut p = BeamPrinter::new();
582 p.print_expr(&BeamExpr::Var("X".into()));
583 assert_eq!(p.finish(), "X");
584 }
585 #[test]
586 pub(super) fn test_beam_printer_tuple() {
587 let mut p = BeamPrinter::new();
588 p.print_expr(&BeamExpr::Tuple(vec![
589 BeamExpr::LitAtom("ok".into()),
590 BeamExpr::LitInt(0),
591 ]));
592 let out = p.finish();
593 assert!(out.contains("'ok'"));
594 assert!(out.contains('0'));
595 }
596 #[test]
597 pub(super) fn test_x_reg_allocator_alloc() {
598 let mut alloc = XRegAllocator::new();
599 let r0 = alloc.alloc("x");
600 let r1 = alloc.alloc("y");
601 assert_eq!(r0, 0);
602 assert_eq!(r1, 1);
603 assert_eq!(alloc.get("x"), Some(0));
604 assert_eq!(alloc.registers_used(), 2);
605 }
606 #[test]
607 pub(super) fn test_x_reg_allocator_reset() {
608 let mut alloc = XRegAllocator::new();
609 alloc.alloc("a");
610 alloc.alloc("b");
611 alloc.reset();
612 assert_eq!(alloc.registers_used(), 0);
613 assert_eq!(alloc.get("a"), None);
614 }
615 #[test]
616 pub(super) fn test_beam_type_union_display() {
617 let u = BeamType::Union(vec![BeamType::Atom, BeamType::Integer]);
618 let s = u.to_string();
619 assert!(s.contains("atom()"));
620 assert!(s.contains("integer()"));
621 }
622 #[test]
623 pub(super) fn test_beam_type_named_display() {
624 let t = BeamType::Named("my_type".into());
625 assert_eq!(t.to_string(), "my_type()");
626 }
627 #[test]
628 pub(super) fn test_beam_endian_display() {
629 assert_eq!(BeamEndian::Big.to_string(), "big");
630 assert_eq!(BeamEndian::Little.to_string(), "little");
631 assert_eq!(BeamEndian::Native.to_string(), "native");
632 }
633}