1use super::{CodecTarget, Constant, Handle, Handoff, Routine, Scalar};
58use crate::adapter::elixir::type_module_path;
59use crate::encode::EncodeError;
60use zerodds_ast::{Ast, CharWidth, MemberNode, TypeKind, TypeNode};
61use zerodds_ast_core::Id;
62use zerodds_lang_cst::common::{Doc, Ident, Literal, Path};
63use zerodds_lang_cst::elixir::{self as ex, atom_name};
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub struct Elixir;
68
69const MAX_TYPE_DEPTH: usize = 64;
71
72const SIGNED_CHAR: &str = "signed char";
75
76const MAP_ENTRY: &str = "_zd_map_entry";
81
82const W_MARK: &str = "ZdW";
87const R_MARK: &str = "ZdR";
89
90thread_local! {
91 static NEXT_TEMPORARY: core::cell::Cell<u64> = const { core::cell::Cell::new(0) };
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98enum OpShape {
99 Pure,
101 Mutates,
103 MutatesWithValue,
105}
106
107fn writer_op(method: &str) -> OpShape {
109 match method {
110 "bitfield" => OpShape::Pure,
111 "write_dheader"
112 | "write_emheader"
113 | "write_pid"
114 | "write_pid_extended"
115 | "begin_packed_element" => OpShape::MutatesWithValue,
116 _ => OpShape::Mutates,
117 }
118}
119
120fn reader_op(method: &str) -> OpShape {
122 match method {
123 "reader_status" | "reader_position" | "reader_remaining" | "reader_save" | "trim_utf8"
124 | "trim_utf16" => OpShape::Pure,
125 "reader_refuse"
126 | "reader_restore"
127 | "reader_skip_to"
128 | "finish_pid_read"
129 | "reader_finish_packed_element" => OpShape::Mutates,
130 _ => OpShape::MutatesWithValue,
132 }
133}
134
135impl Elixir {
136 fn runtime_path(marker: &str, method: &str) -> Path {
138 let module = if marker == W_MARK { "Writer" } else { "Reader" };
139 Path::new(&["ZeroddsWire", module, method])
140 }
141
142 fn receiver(marker: &str) -> &'static str {
144 if marker == W_MARK { "w" } else { "r" }
145 }
146
147 fn fresh(prefix: &str) -> Ident {
163 let number = NEXT_TEMPORARY.with(|next| {
164 let number = next.get();
165 next.set(number.wrapping_add(1));
166 number
167 });
168 Ident::new(&format!("{prefix}{number}"))
169 }
170
171 fn reset_temporaries() {
173 NEXT_TEMPORARY.with(|next| next.set(0));
174 }
175
176 fn lower(expr: ex::Expr) -> (Vec<ex::Statement>, ex::Expr) {
181 let mut statements = Vec::new();
182 let mut temporaries = 0usize;
183 let value = Self::lower_into(expr, &mut statements, &mut temporaries);
184 (statements, value)
185 }
186
187 fn lower_into(
188 expr: ex::Expr,
189 statements: &mut Vec<ex::Statement>,
190 temporaries: &mut usize,
191 ) -> ex::Expr {
192 match expr {
193 ex::Expr::Call { func, args } => {
194 let marker = func
195 .segments()
196 .first()
197 .map(|segment| segment.as_str().to_string());
198 let args: Vec<ex::Expr> = args
199 .into_iter()
200 .map(|argument| Self::lower_into(argument, statements, temporaries))
201 .collect();
202 let Some(marker) = marker.filter(|m| m == W_MARK || m == R_MARK) else {
203 return ex::Expr::Call { func, args };
204 };
205 let method = func
206 .segments()
207 .get(1)
208 .map(Ident::as_str)
209 .unwrap_or_default()
210 .to_string();
211 let shape = if marker == W_MARK {
212 writer_op(&method)
213 } else {
214 reader_op(&method)
215 };
216 let receiver = Self::receiver(&marker);
217 let mut full_args = vec![ex::Expr::Var(Ident::new(receiver))];
218 full_args.extend(args);
219 let call = ex::Expr::Call {
220 func: Self::runtime_path(&marker, &method),
221 args: full_args,
222 };
223 match shape {
224 OpShape::Pure => call,
225 OpShape::Mutates => ex::Expr::Call {
237 func: Path::single(&format!("zd_unlowered_{method}")),
238 args: Vec::new(),
239 },
240 OpShape::MutatesWithValue => {
241 *temporaries += 1;
242 let name = Self::fresh("zd_rv");
243 statements.push(ex::Statement::BindTuple {
244 names: vec![name.clone(), Ident::new(receiver)],
245 value: call,
246 });
247 ex::Expr::Var(name)
248 }
249 }
250 }
251 ex::Expr::Binary { op, lhs, rhs } => {
252 let lhs = Self::lower_into(*lhs, statements, temporaries);
253 let rhs = Self::lower_into(*rhs, statements, temporaries);
254 ex::Expr::Binary {
255 op,
256 lhs: Box::new(lhs),
257 rhs: Box::new(rhs),
258 }
259 }
260 ex::Expr::Unary { op, value } => ex::Expr::Unary {
261 op,
262 value: Box::new(Self::lower_into(*value, statements, temporaries)),
263 },
264 ex::Expr::Paren(value) => {
265 ex::Expr::Paren(Box::new(Self::lower_into(*value, statements, temporaries)))
266 }
267 ex::Expr::Field { value, name } => ex::Expr::Field {
268 value: Box::new(Self::lower_into(*value, statements, temporaries)),
269 name,
270 },
271 ex::Expr::Index { value, index } => ex::Expr::Index {
272 value: Box::new(Self::lower_into(*value, statements, temporaries)),
273 index: Box::new(Self::lower_into(*index, statements, temporaries)),
274 },
275 ex::Expr::IfElse {
276 condition,
277 then,
278 otherwise,
279 } => ex::Expr::IfElse {
280 condition: Box::new(Self::lower_into(*condition, statements, temporaries)),
281 then: Box::new(Self::lower_into(*then, statements, temporaries)),
282 otherwise: Box::new(Self::lower_into(*otherwise, statements, temporaries)),
283 },
284 other @ (ex::Expr::Literal(_)
285 | ex::Expr::Atom(_)
286 | ex::Expr::Var(_)
287 | ex::Expr::Attr(_)
288 | ex::Expr::Module(_)
289 | ex::Expr::List(_)
290 | ex::Expr::Tuple(_)
291 | ex::Expr::Keyword(_)
292 | ex::Expr::StructLiteral { .. }) => other,
293 }
294 }
295
296 fn place_of(place: &ex::Expr) -> Option<(Ident, Vec<ex::Access>)> {
299 match place {
300 ex::Expr::Var(name) => Some((name.clone(), Vec::new())),
301 ex::Expr::Field { value, name } => {
302 let (base, mut path) = Self::place_of(value)?;
303 path.push(ex::Access::Key(name.clone()));
304 Some((base, path))
305 }
306 ex::Expr::Index { value, index } => {
307 let (base, mut path) = Self::place_of(value)?;
308 path.push(ex::Access::At((**index).clone()));
309 Some((base, path))
310 }
311 _ => None,
312 }
313 }
314
315 fn refused_place(place: ex::Expr, value: ex::Expr) -> ex::Statement {
319 ex::Statement::Bind {
320 name: Ident::new("_"),
321 value: ex::Expr::Call {
322 func: Path::single("zd_refused_not_a_place"),
323 args: vec![place, value],
324 },
325 }
326 }
327
328 fn assign(place: ex::Expr, value: ex::Expr) -> Vec<ex::Statement> {
330 let (mut statements, value) = Self::lower(value);
331 let Some((base, path)) = Self::place_of(&place) else {
332 statements.push(Self::refused_place(place, value));
333 return statements;
334 };
335 if path.is_empty() {
336 statements.push(ex::Statement::Bind { name: base, value });
337 } else {
338 statements.push(ex::Statement::Update { base, path, value });
339 }
340 statements
341 }
342
343 fn seq(mut statements: Vec<ex::Statement>) -> ex::Statement {
345 if statements.len() == 1 {
346 return statements.remove(0);
347 }
348 ex::Statement::Seq(statements)
349 }
350
351 fn is_encoder_call(func: &Path) -> bool {
354 func.segments().len() == 1
355 && func.segments().first().is_some_and(|name| {
356 let name = name.as_str();
357 name.starts_with("zd_enc_")
358 || name.starts_with("zd_kenc_")
359 || name.starts_with("zd_keyholder_")
360 })
361 }
362}
363
364impl CodecTarget for Elixir {
365 type Expr = ex::Expr;
366 type Stmt = ex::Statement;
367 type Block = Vec<ex::Statement>;
368 type Type = ex::Type;
369 type Decl = ex::CodecFunction;
370
371 fn scalar(kind: Scalar) -> Self::Type {
380 match kind {
381 Scalar::Bool => ex::Type::Basic(ex::Basic::Boolean),
382 Scalar::Char => ex::Type::Atom("char".to_string()),
383 Scalar::Float | Scalar::Double => ex::Type::Basic(ex::Basic::Float),
384 Scalar::LongDouble => ex::Type::Atom("float128".to_string()),
385 Scalar::Int8
386 | Scalar::UInt8
387 | Scalar::Int16
388 | Scalar::UInt16
389 | Scalar::Int32
390 | Scalar::UInt32
391 | Scalar::Int64
392 | Scalar::UInt64
393 | Scalar::Index
394 | Scalar::Size => ex::Type::Basic(ex::Basic::Integer),
395 }
396 }
397
398 fn named(name: &str) -> Self::Type {
399 ex::Type::Atom(name.to_string())
400 }
401
402 fn nothing() -> Self::Type {
403 ex::Type::Basic(ex::Basic::Nil)
404 }
405
406 fn handle(kind: Handle) -> Self::Type {
410 ex::Type::Atom(
411 match kind {
412 Handle::DHeader => "dheader",
413 Handle::EmHeader => "emheader",
414 Handle::Pid => "pid",
415 Handle::TryConstruct => "try_construct",
416 Handle::ReaderMark => "reader_mark",
417 Handle::PidRead => "pid_read",
418 Handle::EmHeaderRead => "emheader_read",
419 }
420 .to_string(),
421 )
422 }
423
424 fn code_point(value: Self::Expr, _narrow: bool) -> Self::Expr {
427 value
428 }
429
430 fn constant(which: Constant) -> Self::Expr {
439 let atom = |name: &str| ex::Expr::Atom(name.to_string());
440 match which {
441 Constant::WcharWidth2 => ex::Expr::int(2),
442 Constant::WcharWidth4 => ex::Expr::int(4),
443 Constant::WcharFixed => atom("fixed"),
444 Constant::WcharGiopLengthPrefixed => atom("giop-length-prefixed"),
445 Constant::WstringUnitsStream => atom("stream"),
446 Constant::WstringUnitsBigEndianAlways => atom("big-endian-always"),
447 Constant::WstringUnitsByteOrderMark => atom("byte-order-mark"),
448 Constant::WstringUnitsHostUnswapped => atom("host-unswapped"),
449 Constant::WstringSpecification => atom("specification"),
450 Constant::WstringCharacterCount => atom("character-count"),
451 Constant::WstringCharacterCountTerminated => atom("character-count-terminated"),
452 Constant::WstringSpecificationWideUnits => atom("specification-wide-units"),
453 Constant::WstringRosTypeSupport => atom("ros-type-support"),
454 Constant::WstringOctetsTerminated2 => atom("octets/terminated/2"),
455 Constant::WstringOctetsTerminated4 => atom("octets/terminated/4"),
456 Constant::WstringCharactersTerminated2 => atom("characters/terminated/2"),
457 Constant::FixedDeclared => atom("declared"),
458 Constant::FixedValuePrecision => atom("value-precision"),
459 Constant::FixedValueStringDigits => atom("value-string-digits"),
460 Constant::ParameterMember => atom("member"),
461 Constant::ParameterOptional => atom("optional"),
462 Constant::TryConstructDiscard => atom("discard"),
463 Constant::TryConstructTrim => atom("trim"),
464 Constant::TryConstructUseDefault => atom("use_default"),
465 Constant::StatusOk => atom("ok"),
466 Constant::StatusDiscarded => atom("discarded"),
467 Constant::StatusImpossibleLength => atom("impossible_length"),
468 Constant::StatusOutOfRange => atom("out_of_range"),
469 Constant::StatusTruncated => atom("truncated"),
470 }
471 }
472
473 fn unsigned_holder(_bits: u16) -> Self::Type {
475 ex::Type::Basic(ex::Basic::Integer)
476 }
477
478 fn value_type(ast: &Ast, id: Id<TypeNode>) -> Self::Type {
479 value_type_of(ast, id, 0)
480 }
481
482 fn type_name(ast: &Ast, id: Id<TypeNode>) -> String {
483 type_tag(ast, id, 0)
484 }
485
486 fn map_entry_name(_ast: &Ast, _id: Id<TypeNode>) -> String {
489 MAP_ENTRY.to_string()
490 }
491
492 fn module_prefix(_ast: &Ast, _id: Id<TypeNode>) -> String {
495 String::new()
496 }
497
498 fn escape(name: &str) -> String {
499 atom_name(name)
500 }
501
502 fn name(text: &str) -> Self::Expr {
505 ex::Expr::Var(Ident::new(text))
506 }
507
508 fn int(value: i128) -> Self::Expr {
509 ex::Expr::int(value)
510 }
511
512 fn wide_int(value: i128) -> Self::Expr {
514 ex::Expr::int(value)
515 }
516
517 fn wide_unsigned(value: u64) -> Self::Expr {
519 ex::Expr::int(i128::from(value))
520 }
521
522 fn wide_signed(value: i128) -> Self::Expr {
524 ex::Expr::int(value)
525 }
526
527 fn unsigned_int(value: i128) -> Self::Expr {
529 ex::Expr::int(value)
530 }
531
532 fn literal(value: Literal) -> Self::Expr {
537 let Literal::Int(text) = &value else {
538 return ex::Expr::Literal(value);
539 };
540 match text.as_str() {
541 "true" => ex::Expr::Literal(Literal::Bool(true)),
542 "false" => ex::Expr::Literal(Literal::Bool(false)),
543 _ => ex::Expr::Literal(value),
544 }
545 }
546
547 fn call(callee: &str, args: Vec<Self::Expr>) -> Self::Expr {
554 if callee == "strcmp" {
555 return strcmp_sign(args);
556 }
557 ex::Expr::Call {
558 func: Path::single(callee),
559 args,
560 }
561 }
562
563 fn runtime(receiver: &str, method: &str, args: Vec<Self::Expr>) -> Self::Expr {
566 let marker = if receiver == "w" { W_MARK } else { R_MARK };
567 ex::Expr::Call {
568 func: Path::new(&[marker, method]),
569 args,
570 }
571 }
572
573 fn runtime_static(method: &str, args: Vec<Self::Expr>) -> Self::Expr {
576 ex::Expr::Call {
577 func: Path::new(&["ZeroddsWire", method]),
578 args,
579 }
580 }
581
582 fn carries_hdds_axes() -> bool {
588 true
589 }
590
591 fn member(base: &str, field: &str) -> Self::Expr {
595 let mut place = Self::name(base);
596 for segment in field.split('.') {
597 place = ex::Expr::Field {
598 value: Box::new(place),
599 name: Ident::new(segment),
600 };
601 }
602 place
603 }
604
605 fn inherited_path(depth: usize) -> Option<String> {
607 Some(format!("{}.", atom_name("parent")).repeat(depth))
608 }
609
610 fn whole(base: &str) -> Self::Expr {
611 Self::name(base)
612 }
613
614 fn field_of(value: Self::Expr, field: &str) -> Self::Expr {
615 ex::Expr::Field {
616 value: Box::new(value),
617 name: Ident::new(field),
618 }
619 }
620
621 fn header_field(value: Self::Expr, field: &str) -> Self::Expr {
626 let named = if field == "end" { "list_end" } else { field };
627 Self::field_of(value, named)
628 }
629
630 fn index(value: Self::Expr, at: Self::Expr) -> Self::Expr {
631 ex::Expr::Index {
632 value: Box::new(value),
633 index: Box::new(at),
634 }
635 }
636
637 fn count_of(base: &str, fixed: Option<u64>) -> Self::Expr {
639 match fixed {
640 Some(bound) => ex::Expr::int(i128::from(bound)),
641 None => ex::Expr::Call {
642 func: Path::single("length"),
643 args: vec![Self::name(base)],
644 },
645 }
646 }
647
648 fn elements(base: &str) -> Self::Expr {
649 Self::name(base)
650 }
651
652 fn case_member(base: &str, field: &str) -> Self::Expr {
655 Self::member(base, field)
656 }
657
658 fn entry_half(entry: Self::Expr, key: bool) -> Self::Expr {
662 ex::Expr::Index {
663 value: Box::new(entry),
664 index: Box::new(ex::Expr::int(i128::from(!key))),
665 }
666 }
667
668 fn absent() -> Self::Expr {
669 ex::Expr::nil()
670 }
671
672 fn materialised(_value: Self::Expr) -> Option<Self::Expr> {
675 None
676 }
677
678 fn optional_value(_ast: &Ast, _id: Id<TypeNode>, base: &str, field: &str) -> Self::Expr {
681 Self::member(base, field)
682 }
683
684 fn present(base: &str, field: &str) -> Self::Expr {
690 ex::Expr::Binary {
691 op: "!=",
692 lhs: Box::new(Self::member(base, field)),
693 rhs: Box::new(Self::absent()),
694 }
695 }
696
697 fn open_array(base: &str, lengths: &[i128]) -> Vec<Self::Stmt> {
702 vec![ex::Statement::Bind {
703 name: Ident::new(base),
704 value: Self::runtime_static(
705 "array_to_lists",
706 vec![Self::whole(base), ex::Expr::int(lengths.len() as i128)],
707 ),
708 }]
709 }
710
711 fn close_array(base: &str, lengths: &[i128]) -> Vec<Self::Stmt> {
717 vec![ex::Statement::Bind {
718 name: Ident::new(base),
719 value: Self::runtime_static(
720 "array_to_tuples",
721 vec![Self::whole(base), ex::Expr::int(lengths.len() as i128)],
722 ),
723 }]
724 }
725
726 fn set_present(base: &str, field: &str, present: bool) -> Vec<Self::Stmt> {
729 if present {
730 return Vec::new();
731 }
732 vec![ex::Statement::Update {
733 base: Ident::new(base),
734 path: vec![ex::Access::Key(Ident::new(field))],
735 value: Self::absent(),
736 }]
737 }
738
739 fn clear_union_cases(base: &str, fields: &[String]) -> Vec<Self::Stmt> {
744 fields
745 .iter()
746 .flat_map(|field| Self::set_present(base, field, false))
747 .collect()
748 }
749
750 fn optional_destination(base: &str, field: &str) -> (Vec<Self::Stmt>, Self::Expr) {
753 (Vec::new(), Self::member(base, field))
754 }
755
756 fn binary(op: &'static str, lhs: Self::Expr, rhs: Self::Expr) -> Self::Expr {
764 ex::Expr::Binary {
765 op: match op {
766 "&&" => "and",
767 "||" => "or",
768 "&" => "&&&",
769 "|" => "|||",
770 ">>" => ">>>",
771 "<<" => "<<<",
772 other => other,
773 },
774 lhs: Box::new(lhs),
775 rhs: Box::new(rhs),
776 }
777 }
778
779 fn unary(op: &'static str, value: Self::Expr) -> Self::Expr {
782 if op == "~" {
783 return ex::Expr::Call {
784 func: Path::new(&["Bitwise", "bnot"]),
785 args: vec![value],
786 };
787 }
788 ex::Expr::Unary {
789 op: match op {
790 "!" => "not ",
791 other => other,
792 },
793 value: Box::new(value),
794 }
795 }
796
797 fn paren(value: Self::Expr) -> Self::Expr {
798 ex::Expr::Paren(Box::new(value))
799 }
800
801 fn cast(type_: Self::Type, value: Self::Expr) -> Self::Expr {
805 if type_ == Self::named(SIGNED_CHAR) {
806 return signed_char(value);
807 }
808 let ex::Expr::Literal(Literal::Int(text)) = &value else {
809 return value;
810 };
811 let digits = text.strip_prefix('-').unwrap_or(text);
812 if digits.is_empty() || !digits.bytes().all(|byte| byte.is_ascii_digit()) {
813 return value;
814 }
815 if type_ == Self::scalar(Scalar::Char) {
816 return text
822 .parse::<u32>()
823 .ok()
824 .filter(|code| *code <= u32::from(u16::MAX))
825 .map_or_else(
826 || value.clone(),
827 |code| match char::from_u32(code) {
828 Some(point) => ex::Expr::Literal(Literal::Str(point.to_string())),
829 None => ex::Expr::Atom(format!("zd_surrogate_{code:x}")),
830 },
831 );
832 }
833 if type_ == Self::scalar(Scalar::Bool) {
834 return ex::Expr::Literal(Literal::Bool(text != "0"));
835 }
836 value
837 }
838
839 fn handoff(value: Self::Expr, _form: Handoff) -> Self::Expr {
842 value
843 }
844
845 fn declare(_type: Self::Type, name: &str, value: Option<Self::Expr>) -> Self::Stmt {
851 match value {
852 Some(value) => Self::seq(Self::assign(Self::name(name), value)),
853 None => ex::Statement::Bind {
854 name: Ident::new(name),
855 value: Self::absent(),
856 },
857 }
858 }
859
860 fn perform(value: Self::Expr) -> Self::Stmt {
865 if let ex::Expr::Binary { op: "=", lhs, rhs } = value {
866 return Self::seq(Self::assign(*lhs, *rhs));
867 }
868 if let ex::Expr::Call { func, args } = &value {
869 let head = func.segments().first().map(Ident::as_str);
870 if head == Some(W_MARK) || head == Some(R_MARK) {
871 let marker = head.unwrap_or_default().to_string();
872 let method = func
873 .segments()
874 .get(1)
875 .map(Ident::as_str)
876 .unwrap_or_default()
877 .to_string();
878 let shape = if marker == W_MARK {
879 writer_op(&method)
880 } else {
881 reader_op(&method)
882 };
883 let receiver = Self::receiver(&marker);
884 let mut statements = Vec::new();
885 let mut temporaries = 0usize;
886 let args: Vec<ex::Expr> = args
887 .iter()
888 .cloned()
889 .map(|argument| Self::lower_into(argument, &mut statements, &mut temporaries))
890 .collect();
891 let mut full_args = vec![ex::Expr::Var(Ident::new(receiver))];
892 full_args.extend(args);
893 let call = ex::Expr::Call {
894 func: Self::runtime_path(&marker, &method),
895 args: full_args,
896 };
897 statements.push(match shape {
898 OpShape::Mutates => ex::Statement::Bind {
899 name: Ident::new(receiver),
900 value: call,
901 },
902 OpShape::MutatesWithValue => ex::Statement::BindTuple {
906 names: vec![Ident::new("_"), Ident::new(receiver)],
907 value: call,
908 },
909 OpShape::Pure => ex::Statement::Bind {
910 name: Ident::new("_"),
911 value: call,
912 },
913 });
914 return Self::seq(statements);
915 }
916 if Self::is_encoder_call(func) {
917 let (mut statements, lowered) = Self::lower(value.clone());
920 statements.push(ex::Statement::Bind {
921 name: Ident::new("w"),
922 value: lowered,
923 });
924 return Self::seq(statements);
925 }
926 }
927 let (mut statements, lowered) = Self::lower(value);
928 statements.push(ex::Statement::Bind {
929 name: Ident::new("_"),
930 value: lowered,
931 });
932 Self::seq(statements)
933 }
934
935 fn give_back(value: Option<Self::Expr>) -> Self::Stmt {
941 ex::Statement::Return(ex::Expr::Tuple(vec![
942 value.unwrap_or_else(|| Self::name("v")),
943 Self::name("r"),
944 ]))
945 }
946
947 fn when(
948 condition: Self::Expr,
949 then: Self::Block,
950 otherwise: Option<Self::Block>,
951 ) -> Self::Stmt {
952 let (mut statements, condition) = Self::lower(condition);
956 statements.push(ex::Statement::If {
957 condition,
958 then,
959 otherwise: otherwise.unwrap_or_default(),
960 });
961 Self::seq(statements)
962 }
963
964 fn count_up(counter: &str, bound: Self::Expr, body: Self::Block) -> Self::Stmt {
965 let (mut statements, bound) = Self::lower(bound);
966 statements.push(ex::Statement::For {
967 counter: Ident::new(counter),
968 bound,
969 body,
970 });
971 Self::seq(statements)
972 }
973
974 fn until(condition: Self::Expr, body: Self::Block) -> Self::Stmt {
982 let (mut statements, condition) = Self::lower(condition);
983 if !statements.is_empty() {
984 statements.push(ex::Statement::Bind {
985 name: Ident::new("_"),
986 value: ex::Expr::Call {
987 func: Path::single("zd_refused_stateful_loop_condition"),
988 args: Vec::new(),
989 },
990 });
991 }
992 statements.push(ex::Statement::While { condition, body });
993 Self::seq(statements)
994 }
995
996 fn leave() -> Self::Stmt {
997 ex::Statement::Break
998 }
999
1000 fn note(doc: Doc) -> Self::Stmt {
1001 ex::Statement::Comment(doc)
1002 }
1003
1004 fn drop_value(value: Self::Expr) -> Self::Stmt {
1010 Self::perform(value)
1011 }
1012
1013 fn mark_used(name: &str) -> Vec<Self::Stmt> {
1017 vec![ex::Statement::Bind {
1018 name: Ident::new("_"),
1019 value: Self::name(name),
1020 }]
1021 }
1022
1023 fn read_call(
1027 callee: &str,
1028 place: Self::Expr,
1029 _form: Handoff,
1030 behaviour: Self::Expr,
1031 ) -> Self::Stmt {
1032 let call = ex::Expr::Call {
1033 func: Path::single(callee),
1034 args: vec![Self::name("r"), behaviour],
1035 };
1036 let Some((base, path)) = Self::place_of(&place) else {
1037 return Self::refused_place(place, call);
1038 };
1039 if path.is_empty() {
1040 return ex::Statement::BindTuple {
1041 names: vec![base, Ident::new("r")],
1042 value: call,
1043 };
1044 }
1045 let temporary = Self::fresh("zd_rd");
1046 Self::seq(vec![
1047 ex::Statement::BindTuple {
1048 names: vec![temporary.clone(), Ident::new("r")],
1049 value: call,
1050 },
1051 ex::Statement::Update {
1052 base,
1053 path,
1054 value: ex::Expr::Var(temporary),
1055 },
1056 ])
1057 }
1058
1059 fn read_forward(callee: &str) -> Self::Stmt {
1061 ex::Statement::BindTuple {
1062 names: vec![Ident::new("v"), Ident::new("r")],
1063 value: ex::Expr::Call {
1064 func: Path::single(callee),
1065 args: vec![Self::name("r"), Self::name("zd_tc")],
1066 },
1067 }
1068 }
1069
1070 fn block(statements: Vec<Self::Stmt>) -> Self::Block {
1071 statements
1072 }
1073
1074 fn into_statements(block: Self::Block) -> Vec<Self::Stmt> {
1075 block
1076 }
1077
1078 fn make_room(
1087 base: &str,
1088 _element: Self::Type,
1089 count: Self::Expr,
1090 assigned_whole: bool,
1091 fill: Vec<Self::Stmt>,
1092 ) -> Vec<Self::Stmt> {
1093 let slot = if assigned_whole {
1094 Self::absent()
1095 } else {
1096 ex::Expr::List(vec![Self::absent(), Self::absent()])
1097 };
1098 let mut out = vec![ex::Statement::Bind {
1099 name: Ident::new(base),
1100 value: ex::Expr::Call {
1101 func: Path::new(&["List", "duplicate"]),
1102 args: vec![slot, count],
1103 },
1104 }];
1105 out.extend(fill);
1106 out
1107 }
1108
1109 fn keep_first(base: &str, count: Self::Expr) -> Vec<Self::Stmt> {
1111 vec![ex::Statement::Bind {
1112 name: Ident::new(base),
1113 value: ex::Expr::Call {
1114 func: Path::new(&["Enum", "take"]),
1115 args: vec![Self::name(base), count],
1116 },
1117 }]
1118 }
1119
1120 fn sequence_element(
1123 base: &str,
1124 index: Self::Expr,
1125 _element: Self::Type,
1126 ) -> (Vec<Self::Stmt>, Self::Expr, Vec<Self::Stmt>) {
1127 (
1128 Vec::new(),
1129 Self::index(Self::elements(base), index),
1130 Vec::new(),
1131 )
1132 }
1133
1134 fn storage(type_: Self::Type, name: &str) -> Self::Stmt {
1137 if type_ == ex::Type::Atom(MAP_ENTRY.to_string()) {
1138 return ex::Statement::Bind {
1139 name: Ident::new(name),
1140 value: ex::Expr::List(vec![Self::absent(), Self::absent()]),
1141 };
1142 }
1143 Self::declare(type_, name, None)
1144 }
1145
1146 fn routine(spec: Routine<Self>) -> Option<Self::Decl> {
1155 Self::reset_temporaries();
1161 let body = spec.body?;
1162 let mut params = Vec::new();
1163 if spec.writing {
1164 params.push(Ident::new("w"));
1165 params.push(Ident::new("v"));
1166 } else {
1167 params.push(Ident::new("r"));
1168 }
1169 params.extend(spec.extra.iter().map(|(_, name)| Ident::new(name)));
1170 let (body, result) = if spec.writing {
1171 let mut statements = vec![ex::Statement::Bind {
1176 name: Ident::new("_"),
1177 value: Self::name("v"),
1178 }];
1179 statements.extend(body);
1180 (statements, Self::name("w"))
1181 } else {
1182 let defaulter = crate::default_elixir::defaulter_from_decoder(&spec.name);
1183 let mut statements = vec![
1184 ex::Statement::Bind {
1185 name: Ident::new("v"),
1186 value: ex::Expr::Call {
1187 func: Path::single(&defaulter),
1188 args: Vec::new(),
1189 },
1190 },
1191 ex::Statement::Bind {
1196 name: Ident::new("_"),
1197 value: Self::name("v"),
1198 },
1199 ];
1200 statements.extend(body);
1201 (
1202 statements,
1203 ex::Expr::Tuple(vec![Self::name("v"), Self::name("r")]),
1204 )
1205 };
1206 Some(ex::CodecFunction {
1207 doc: spec.doc,
1208 name: Ident::new(&spec.name),
1209 params,
1210 body,
1211 result,
1212 })
1213 }
1214
1215 fn default_into(
1220 ast: &Ast,
1221 id: Id<TypeNode>,
1222 place: Self::Expr,
1223 ) -> Result<Self::Stmt, EncodeError> {
1224 Ok(crate::default_elixir::default_into(ast, id, place))
1225 }
1226
1227 fn member_default(
1232 ast: &Ast,
1233 node: &MemberNode,
1234 type_: Id<TypeNode>,
1235 place: Self::Expr,
1236 ) -> Result<Vec<Self::Stmt>, EncodeError> {
1237 crate::default_elixir::member_default(ast, node, type_, place)
1238 }
1239
1240 fn enumerator_constant(ast: &Ast, id: Id<TypeNode>, enumerator: &str) -> Self::Expr {
1244 let module = type_module_path(ast, id);
1245 let mut segments: Vec<String> = module
1246 .segments()
1247 .iter()
1248 .map(|segment| segment.as_str().to_string())
1249 .collect();
1250 segments.push(atom_name(enumerator));
1251 ex::Expr::Call {
1252 func: Path::new(&segments.iter().map(String::as_str).collect::<Vec<&str>>()),
1253 args: Vec::new(),
1254 }
1255 }
1256}
1257
1258fn signed_char(value: ex::Expr) -> ex::Expr {
1268 let point = ex::Expr::Call {
1269 func: Path::single("hd"),
1270 args: vec![ex::Expr::Call {
1271 func: Path::new(&["String", "to_charlist"]),
1272 args: vec![value],
1273 }],
1274 };
1275 let wrapped = ex::Expr::Call {
1276 func: Path::single("rem"),
1277 args: vec![
1278 ex::Expr::Binary {
1279 op: "+",
1280 lhs: Box::new(point),
1281 rhs: Box::new(ex::Expr::int(128)),
1282 },
1283 ex::Expr::int(256),
1284 ],
1285 };
1286 ex::Expr::Binary {
1287 op: "-",
1288 lhs: Box::new(ex::Expr::Paren(Box::new(wrapped))),
1289 rhs: Box::new(ex::Expr::int(128)),
1290 }
1291}
1292
1293fn strcmp_sign(args: Vec<ex::Expr>) -> ex::Expr {
1296 let [left, right] = <[ex::Expr; 2]>::try_from(args).unwrap_or_else(|args| {
1297 [
1301 ex::Expr::Call {
1302 func: Path::single("strcmp"),
1303 args,
1304 },
1305 ex::Expr::int(0),
1306 ]
1307 });
1308 let side = |op: &'static str| {
1309 ex::Expr::Paren(Box::new(ex::Expr::IfElse {
1310 condition: Box::new(ex::Expr::Binary {
1311 op,
1312 lhs: Box::new(left.clone()),
1313 rhs: Box::new(right.clone()),
1314 }),
1315 then: Box::new(ex::Expr::int(1)),
1316 otherwise: Box::new(ex::Expr::int(0)),
1317 }))
1318 };
1319 ex::Expr::Paren(Box::new(ex::Expr::Binary {
1320 op: "-",
1321 lhs: Box::new(side(">")),
1322 rhs: Box::new(side("<")),
1323 }))
1324}
1325
1326fn bound_tag(ast: &Ast, bound: &zerodds_ast::Bound) -> String {
1346 match bound.expr() {
1347 None => String::new(),
1348 Some(expr) => match ast.eval_integral(expr) {
1352 Ok(limit) => format!("_b{limit}"),
1353 Err(_) => "_bx".to_string(),
1357 },
1358 }
1359}
1360
1361pub(crate) fn type_tag(ast: &Ast, id: Id<TypeNode>, depth: usize) -> String {
1372 if depth > MAX_TYPE_DEPTH {
1373 return "unknown".to_string();
1374 }
1375 let Ok(node) = ast.type_(id) else {
1376 return "unknown".to_string();
1377 };
1378 match &node.kind {
1379 TypeKind::Primitive(kind) => kind.as_str().replace(' ', "_"),
1380 TypeKind::String {
1381 width: CharWidth::Char,
1382 bound,
1383 } => format!("string{}", bound_tag(ast, bound)),
1384 TypeKind::String {
1385 width: CharWidth::WChar,
1386 bound,
1387 } => format!("wstring{}", bound_tag(ast, bound)),
1388 TypeKind::Fixed { digits, scale } => format!("fixed_{digits}_{scale}"),
1389 TypeKind::Sequence { element, bound } => match element.resolved() {
1390 Some(element) => format!(
1391 "seq{}_{}",
1392 bound_tag(ast, bound),
1393 type_tag(ast, element, depth + 1)
1394 ),
1395 None => format!("seq_unknown{}", bound_tag(ast, bound)),
1396 },
1397 TypeKind::Array { element, dims } => {
1398 let mut name = match element.resolved() {
1399 Some(element) => format!("arr_{}", type_tag(ast, element, depth + 1)),
1400 None => "arr_unknown".to_string(),
1401 };
1402 for dim in dims {
1403 match ast.eval_integral(*dim) {
1404 Ok(length) => name.push_str(&format!("_{length}")),
1405 Err(_) => name.push_str("_x"),
1406 }
1407 }
1408 name
1409 }
1410 TypeKind::Map { key, value, bound } => {
1411 let half = |half: Option<Id<TypeNode>>| match half {
1412 Some(target) => type_tag(ast, target, depth + 1),
1413 None => "unknown".to_string(),
1414 };
1415 format!(
1416 "map{}_{}_{}",
1417 bound_tag(ast, bound),
1418 half(key.resolved()),
1419 half(value.resolved())
1420 )
1421 }
1422 TypeKind::Alias { aliased } => match aliased.resolved() {
1423 Some(target) => type_tag(ast, target, depth + 1),
1424 None => "unknown".to_string(),
1425 },
1426 TypeKind::ValueBox { boxed } => match boxed.resolved() {
1427 Some(target) => format!("box_{}", type_tag(ast, target, depth + 1)),
1428 None => "box_unknown".to_string(),
1429 },
1430 TypeKind::Structure { .. }
1431 | TypeKind::Union { .. }
1432 | TypeKind::Enumeration { .. }
1433 | TypeKind::Bitmask { .. }
1434 | TypeKind::Bitset { .. }
1435 | TypeKind::Valuetype { .. }
1436 | TypeKind::Native => type_module_path(ast, id)
1437 .segments()
1438 .iter()
1439 .map(|segment| segment.as_str())
1440 .collect::<Vec<&str>>()
1441 .join("_"),
1442 TypeKind::Any => "any".to_string(),
1443 TypeKind::ValueBase => "valuebase".to_string(),
1444 TypeKind::ObjectRef { .. } => "objectref".to_string(),
1445 TypeKind::AnnotationType { .. } => "annotation".to_string(),
1446 }
1447}
1448
1449fn value_type_of(ast: &Ast, id: Id<TypeNode>, depth: usize) -> ex::Type {
1458 use zerodds_ast::PrimitiveKind;
1459 let unknown = || ex::Type::Basic(ex::Basic::Any);
1460 if depth > MAX_TYPE_DEPTH {
1461 return unknown();
1462 }
1463 let Ok(node) = ast.type_(id) else {
1464 return unknown();
1465 };
1466 let inner = |half: Option<Id<TypeNode>>| {
1467 half.map_or_else(unknown, |target| value_type_of(ast, target, depth + 1))
1468 };
1469 match &node.kind {
1470 TypeKind::Primitive(kind) => match kind {
1471 PrimitiveKind::Boolean => Elixir::scalar(Scalar::Bool),
1472 PrimitiveKind::Char | PrimitiveKind::WChar => Elixir::scalar(Scalar::Char),
1473 PrimitiveKind::Float32 | PrimitiveKind::Float64 => Elixir::scalar(Scalar::Double),
1474 PrimitiveKind::Float128 => Elixir::scalar(Scalar::LongDouble),
1475 _ => Elixir::scalar(Scalar::Int32),
1476 },
1477 TypeKind::String { .. } | TypeKind::Fixed { .. } => ex::Type::Basic(ex::Basic::Binary),
1478 TypeKind::Enumeration { .. } | TypeKind::Bitmask { .. } => {
1482 ex::Type::Basic(ex::Basic::Integer)
1483 }
1484 TypeKind::Bitset { .. }
1485 | TypeKind::Structure { .. }
1486 | TypeKind::Union { .. }
1487 | TypeKind::Valuetype { .. }
1488 | TypeKind::Native => ex::Type::Atom(type_tag(ast, id, depth)),
1489 TypeKind::Sequence { element, .. } => ex::Type::list(inner(element.resolved())),
1490 TypeKind::Array { element, dims } => {
1491 let mut out = inner(element.resolved());
1492 for _ in dims {
1493 out = ex::Type::list(out);
1494 }
1495 out
1496 }
1497 TypeKind::Map { key, value, .. } => ex::Type::list(ex::Type::Tuple(vec![
1498 inner(key.resolved()),
1499 inner(value.resolved()),
1500 ])),
1501 TypeKind::Alias { aliased } => inner(aliased.resolved()),
1502 TypeKind::ValueBox { boxed } => inner(boxed.resolved()).or_nil(),
1503 TypeKind::Any
1504 | TypeKind::ValueBase
1505 | TypeKind::ObjectRef { .. }
1506 | TypeKind::AnnotationType { .. } => unknown(),
1507 }
1508}