1use crate::ast::*;
7use crate::naming::{module_file_stem, to_pascal_case, to_screaming_snake_case, to_snake_case};
8use std::fmt::Write;
9
10#[derive(Debug, Clone, Default)]
12pub struct CCodeGenConfig {
13 pub synta_header_path: Option<String>,
15 pub generate_helpers: bool,
17 pub arena_mode: bool,
19}
20
21impl CCodeGenConfig {
22 pub fn with_header_path(path: impl Into<String>) -> Self {
24 Self {
25 synta_header_path: Some(path.into()),
26 generate_helpers: false,
27 arena_mode: false,
28 }
29 }
30
31 pub fn with_helpers(mut self) -> Self {
33 self.generate_helpers = true;
34 self
35 }
36
37 pub fn with_arena(mut self) -> Self {
39 self.arena_mode = true;
40 self
41 }
42}
43
44pub fn generate_c(module: &Module) -> Result<String, Box<dyn std::error::Error>> {
46 generate_c_with_config(module, CCodeGenConfig::default())
47}
48
49pub fn generate_c_with_config(
51 module: &Module,
52 config: CCodeGenConfig,
53) -> Result<String, Box<dyn std::error::Error>> {
54 let mut output = String::new();
55
56 writeln!(
58 &mut output,
59 "/* Generated from ASN.1 module {} */",
60 module.name
61 )?;
62 writeln!(&mut output, "/* DO NOT EDIT - auto-generated code */")?;
63 writeln!(&mut output)?;
64
65 let guard_name = format!("{}_H", to_screaming_snake_case(&module.name));
67 writeln!(&mut output, "#ifndef {}", guard_name)?;
68 writeln!(&mut output, "#define {}", guard_name)?;
69 writeln!(&mut output)?;
70
71 writeln!(&mut output, "#include <stdint.h>")?;
73 writeln!(&mut output, "#include <stdbool.h>")?;
74 writeln!(&mut output, "#include <stdlib.h>")?;
75 writeln!(&mut output, "#include <string.h>")?;
76 if config.generate_helpers {
77 writeln!(&mut output, "#include <stdio.h>")?;
78 }
79 let header_path = config.synta_header_path.as_deref().unwrap_or("synta.h");
80 writeln!(&mut output, "#include \"{}\"", header_path)?;
81 writeln!(&mut output)?;
82
83 if !module.imports.is_empty() {
85 writeln!(&mut output, "/* Imported module headers */")?;
86 for import in &module.imports {
87 let stem = module_file_stem(&import.module_name);
88 writeln!(&mut output, "#include \"{}.h\"", stem)?;
89 }
90 writeln!(&mut output)?;
91 }
92
93 if config.arena_mode {
95 writeln!(&mut output, "/* Arena/bump allocator support */")?;
96 writeln!(&mut output)?;
97 writeln!(&mut output, "#ifndef SYNTA_ARENA_MAX_HANDLES")?;
98 writeln!(&mut output, "#define SYNTA_ARENA_MAX_HANDLES 256")?;
99 writeln!(&mut output, "#endif")?;
100 writeln!(&mut output)?;
101 writeln!(&mut output, "typedef struct {{")?;
102 writeln!(&mut output, " void *_ptrs[SYNTA_ARENA_MAX_HANDLES];")?;
103 writeln!(
104 &mut output,
105 " void (*_fns[SYNTA_ARENA_MAX_HANDLES])(void*);"
106 )?;
107 writeln!(&mut output, " size_t _n;")?;
108 writeln!(&mut output, "}} SyntaArena;")?;
109 writeln!(&mut output)?;
110 writeln!(
111 &mut output,
112 "static inline void synta_arena_init(SyntaArena *a) {{ a->_n = 0; }}"
113 )?;
114 writeln!(&mut output)?;
115 writeln!(
116 &mut output,
117 "static inline int _synta_arena_track(SyntaArena *a, void *p, void (*fn)(void*)) {{"
118 )?;
119 writeln!(
120 &mut output,
121 " if (!p || a->_n >= SYNTA_ARENA_MAX_HANDLES) return 0;"
122 )?;
123 writeln!(
124 &mut output,
125 " a->_ptrs[a->_n] = p; a->_fns[a->_n] = fn; a->_n++; return 1;"
126 )?;
127 writeln!(&mut output, "}}")?;
128 writeln!(&mut output)?;
129 writeln!(
130 &mut output,
131 "static inline void synta_arena_free_all(SyntaArena *a) {{"
132 )?;
133 writeln!(
134 &mut output,
135 " for (size_t i = 0; i < a->_n; i++) a->_fns[i](a->_ptrs[i]);"
136 )?;
137 writeln!(&mut output, " a->_n = 0;")?;
138 writeln!(&mut output, "}}")?;
139 writeln!(&mut output)?;
140 }
141
142 writeln!(&mut output, "#ifdef __cplusplus")?;
143 writeln!(&mut output, "extern \"C\" {{")?;
144 writeln!(&mut output, "#endif")?;
145 writeln!(&mut output)?;
146
147 writeln!(&mut output, "/* BitString support */")?;
149 writeln!(&mut output)?;
150 writeln!(&mut output, "typedef struct {{")?;
151 writeln!(&mut output, " SyntaByteArray data;")?;
152 writeln!(&mut output, " uint8_t unused_bits;")?;
153 writeln!(&mut output, "}} SyntaBitString;")?;
154 writeln!(&mut output)?;
155
156 generate_value_constants(&mut output, module)?;
158
159 writeln!(&mut output, "/* Forward declarations */")?;
161 writeln!(&mut output)?;
162 for def in &module.definitions {
163 match &def.ty {
164 Type::Sequence(_)
165 | Type::Set(_)
166 | Type::Choice(_)
167 | Type::SequenceOf(_, _)
168 | Type::SetOf(_, _) => {
169 let c_name = to_pascal_case(&def.name);
170 writeln!(&mut output, "typedef struct {} {};", c_name, c_name)?;
171 }
172 Type::Integer(_, named_numbers) if !named_numbers.is_empty() => {
173 let c_name = to_pascal_case(&def.name);
174 writeln!(&mut output, "typedef int64_t {};", c_name)?;
175 }
176 Type::Enumerated(_) => {
177 let c_name = to_pascal_case(&def.name);
178 writeln!(&mut output, "typedef enum {} {};", c_name, c_name)?;
179 }
180 _ => {}
181 }
182 }
183 writeln!(&mut output)?;
184
185 writeln!(&mut output, "/* Type definitions */")?;
188 writeln!(&mut output)?;
189 for idx in topo_order(&module.definitions) {
190 generate_type_definition(
191 &mut output,
192 &module.definitions[idx],
193 config.generate_helpers,
194 )?;
195 writeln!(&mut output)?;
196 }
197
198 writeln!(&mut output, "/* Encoder/Decoder functions */")?;
200 writeln!(&mut output)?;
201 for def in &module.definitions {
202 generate_encoder_decoder_prototypes(&mut output, def, config.arena_mode)?;
203 writeln!(&mut output)?;
204 }
205
206 if config.generate_helpers {
208 writeln!(&mut output, "/* Helper functions */")?;
209 writeln!(&mut output)?;
210 for def in &module.definitions {
211 generate_helper_functions(&mut output, def)?;
212 writeln!(&mut output)?;
213 }
214 }
215
216 writeln!(&mut output, "#ifdef __cplusplus")?;
217 writeln!(&mut output, "}}")?;
218 writeln!(&mut output, "#endif")?;
219 writeln!(&mut output)?;
220
221 writeln!(&mut output, "#endif /* {} */", guard_name)?;
222
223 Ok(output)
224}
225
226fn value_deps_of_type(ty: &Type, deps: &mut Vec<String>) {
229 match ty {
230 Type::Sequence(fields) | Type::Set(fields) => {
231 for field in fields {
232 value_deps_of_field_type(&field.ty, deps);
233 }
234 }
235 Type::Choice(variants) => {
236 for variant in variants {
239 value_deps_of_field_type(&variant.ty, deps);
240 }
241 }
242 _ => {}
244 }
245}
246
247fn value_deps_of_field_type(ty: &Type, deps: &mut Vec<String>) {
257 match ty {
258 Type::TypeRef(name) => deps.push(name.clone()),
259 Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
261 for f in inner_fields {
262 value_deps_of_field_type(&f.ty, deps);
263 }
264 }
265 Type::Tagged { inner, .. }
267 | Type::Constrained {
268 base_type: inner, ..
269 } => {
270 value_deps_of_field_type(inner, deps);
271 }
272 _ => {}
274 }
275}
276
277fn topo_order(defs: &[Definition]) -> Vec<usize> {
280 use std::collections::{HashMap, VecDeque};
281
282 let index: HashMap<&str, usize> = defs
283 .iter()
284 .enumerate()
285 .map(|(i, d)| (d.name.as_str(), i))
286 .collect();
287
288 let mut in_degree = vec![0usize; defs.len()];
289 let mut adj: Vec<Vec<usize>> = vec![Vec::new(); defs.len()];
291
292 for (i, def) in defs.iter().enumerate() {
293 let mut raw_deps = Vec::new();
294 value_deps_of_type(&def.ty, &mut raw_deps);
295
296 let mut seen = std::collections::HashSet::new();
298 for dep in raw_deps {
299 if !seen.insert(dep.clone()) {
300 continue;
301 }
302 if let Some(&j) = index.get(dep.as_str()) {
303 if j != i && !adj[j].contains(&i) {
304 adj[j].push(i);
305 in_degree[i] += 1;
306 }
307 }
308 }
309 }
310
311 let mut queue: VecDeque<usize> = (0..defs.len()).filter(|&i| in_degree[i] == 0).collect();
313 let mut result = Vec::with_capacity(defs.len());
314
315 while let Some(i) = queue.pop_front() {
316 result.push(i);
317 for &j in &adj[i] {
318 in_degree[j] -= 1;
319 if in_degree[j] == 0 {
320 queue.push_back(j);
321 }
322 }
323 }
324
325 for i in 0..defs.len() {
327 if !result.contains(&i) {
328 result.push(i);
329 }
330 }
331
332 result
333}
334
335fn format_c_constraint_display(constraint: &SubtypeConstraint) -> String {
337 match constraint {
338 SubtypeConstraint::SingleValue(val) => match val {
339 ConstraintValue::Integer(n) => n.to_string(),
340 ConstraintValue::Min => "MIN".to_string(),
341 ConstraintValue::Max => "MAX".to_string(),
342 ConstraintValue::NamedValue(name) => name.clone(),
343 },
344 SubtypeConstraint::ValueRange { min, max } => {
345 let min_str = match min {
346 ConstraintValue::Integer(n) => n.to_string(),
347 ConstraintValue::Min => "MIN".to_string(),
348 ConstraintValue::Max => "MAX".to_string(),
349 ConstraintValue::NamedValue(n) => n.clone(),
350 };
351 let max_str = match max {
352 ConstraintValue::Integer(n) => n.to_string(),
353 ConstraintValue::Max => "MAX".to_string(),
354 ConstraintValue::Min => "MIN".to_string(),
355 ConstraintValue::NamedValue(n) => n.clone(),
356 };
357 format!("{}..{}", min_str, max_str)
358 }
359 SubtypeConstraint::Union(elements) => {
360 let parts: Vec<String> = elements.iter().map(format_c_constraint_display).collect();
361 parts.join(" | ")
362 }
363 SubtypeConstraint::Intersection(elements) => {
364 let parts: Vec<String> = elements
365 .iter()
366 .map(|e| format!("({})", format_c_constraint_display(e)))
367 .collect();
368 parts.join(" ^ ")
369 }
370 SubtypeConstraint::Complement(inner) => {
371 format!("ALL EXCEPT {}", format_c_constraint_display(inner))
372 }
373 SubtypeConstraint::SizeConstraint(inner) => {
374 format!("SIZE ({})", format_c_constraint_display(inner))
375 }
376 SubtypeConstraint::Pattern(p) => format!("PATTERN \"{}\"", p),
377 SubtypeConstraint::PermittedAlphabet(ranges) => {
378 let parts: Vec<String> = ranges
379 .iter()
380 .map(|r| {
381 if r.min == r.max {
382 format!("\"{}\"", r.min)
383 } else {
384 format!("\"{}\"..\"{}\"", r.min, r.max)
385 }
386 })
387 .collect();
388 format!("FROM ({})", parts.join(" | "))
389 }
390 _ => "constraint".to_string(),
391 }
392}
393
394fn constrained_integer_c_type(constraint: &SubtypeConstraint) -> &'static str {
407 let (lo, hi) = match constraint {
408 SubtypeConstraint::SingleValue(ConstraintValue::Integer(n)) => (*n, *n),
409 SubtypeConstraint::ValueRange {
410 min: ConstraintValue::Integer(lo),
411 max: ConstraintValue::Integer(hi),
412 } => (*lo, *hi),
413 _ => return "int64_t",
414 };
415 if lo >= 0 {
416 if hi <= u8::MAX as i64 {
417 "uint8_t"
418 } else if hi <= u16::MAX as i64 {
419 "uint16_t"
420 } else if hi <= u32::MAX as i64 {
421 "uint32_t"
422 } else {
423 "uint64_t"
424 }
425 } else if lo >= i8::MIN as i64 && hi <= i8::MAX as i64 {
426 "int8_t"
427 } else if lo >= i16::MIN as i64 && hi <= i16::MAX as i64 {
428 "int16_t"
429 } else if lo >= i32::MIN as i64 && hi <= i32::MAX as i64 {
430 "int32_t"
431 } else {
432 "int64_t"
433 }
434}
435
436fn generate_c_constraint_check(var: &str, constraint: &SubtypeConstraint, c_type: &str) -> String {
447 let is_unsigned = c_type.starts_with("uint");
448 match constraint {
449 SubtypeConstraint::SingleValue(val) => match val {
450 ConstraintValue::Integer(n) => format!("{} == {}LL", var, n),
451 ConstraintValue::Min => format!("{} == INT64_MIN", var),
452 ConstraintValue::Max => format!("{} == INT64_MAX", var),
453 ConstraintValue::NamedValue(name) => format!("{} == {}", var, name),
454 },
455 SubtypeConstraint::ValueRange { min, max } => {
456 let mut parts: Vec<String> = Vec::new();
457 match min {
458 ConstraintValue::Integer(n) if is_unsigned && *n <= 0 => {}
460 ConstraintValue::Integer(n) => parts.push(format!("{} >= {}LL", var, n)),
461 ConstraintValue::Min => {}
462 ConstraintValue::Max => parts.push(format!("{} >= INT64_MAX", var)),
463 ConstraintValue::NamedValue(name) => parts.push(format!("{} >= {}", var, name)),
464 }
465 match max {
466 ConstraintValue::Integer(n) => parts.push(format!("{} <= {}LL", var, n)),
467 ConstraintValue::Max => {}
468 ConstraintValue::Min => parts.push(format!("{} <= INT64_MIN", var)),
469 ConstraintValue::NamedValue(name) => parts.push(format!("{} <= {}", var, name)),
470 }
471 if parts.is_empty() {
472 "1".to_string()
473 } else {
474 format!("({})", parts.join(" && "))
475 }
476 }
477 SubtypeConstraint::Union(elements) => {
478 let checks: Vec<String> = elements
479 .iter()
480 .map(|e| generate_c_constraint_check(var, e, c_type))
481 .collect();
482 format!("({})", checks.join(" || "))
483 }
484 SubtypeConstraint::Intersection(elements) => {
485 let checks: Vec<String> = elements
486 .iter()
487 .map(|e| generate_c_constraint_check(var, e, c_type))
488 .collect();
489 format!("({})", checks.join(" && "))
490 }
491 SubtypeConstraint::Complement(inner) => {
492 let inner_check = generate_c_constraint_check(var, inner, c_type);
493 format!("!({})", inner_check)
494 }
495 _ => "1 /* unsupported constraint */".to_string(),
496 }
497}
498
499fn generate_constrained_integer_c(
520 output: &mut String,
521 name: &str,
522 constraint: &SubtypeConstraint,
523 named_numbers: &[NamedNumber],
524) -> Result<(), Box<dyn std::error::Error>> {
525 let c_name = to_pascal_case(name);
526 let fn_prefix = to_snake_case(name);
527 let c_type = constrained_integer_c_type(constraint);
528 let display = format_c_constraint_display(constraint);
529 let check = generate_c_constraint_check("v", constraint, c_type);
530
531 writeln!(output, "/* INTEGER ({}) */", display)?;
533 writeln!(output, "typedef struct {{ {} value; }} {};", c_type, c_name)?;
534 writeln!(output)?;
535
536 if !named_numbers.is_empty() {
538 writeln!(output, "/* Named values for {} */", c_name)?;
539 for nn in named_numbers {
540 let const_name = to_screaming_snake_case(&nn.name);
541 writeln!(
542 output,
543 "#define {}_{} (({}){})",
544 c_name, const_name, c_type, nn.value
545 )?;
546 }
547 writeln!(output)?;
548 }
549
550 writeln!(
552 output,
553 "/** Create {}: validates INTEGER ({}). Returns false if out of range. */",
554 c_name, display
555 )?;
556 writeln!(
557 output,
558 "static inline bool {}_new({} v, {}* out) {{",
559 fn_prefix, c_type, c_name
560 )?;
561 writeln!(output, " if (!({check})) return false;", check = check)?;
562 writeln!(output, " out->value = v;")?;
563 writeln!(output, " return true;")?;
564 writeln!(output, "}}")?;
565 writeln!(output)?;
566
567 writeln!(
569 output,
570 "/** Create {} without validation (use with caution). */",
571 c_name
572 )?;
573 writeln!(
574 output,
575 "static inline {} {}_new_unchecked({} v) {{",
576 c_name, fn_prefix, c_type
577 )?;
578 writeln!(output, " {} out; out.value = v; return out;", c_name)?;
579 writeln!(output, "}}")?;
580 writeln!(output)?;
581
582 writeln!(
584 output,
585 "/** Get the inner {} value of {}. */",
586 c_type, c_name
587 )?;
588 writeln!(
589 output,
590 "static inline {} {}_get(const {}* self) {{ return self->value; }}",
591 c_type, fn_prefix, c_name
592 )?;
593 writeln!(output)?;
594
595 writeln!(
597 output,
598 "/** Check that {} satisfies INTEGER ({}). */",
599 c_name, display
600 )?;
601 writeln!(
602 output,
603 "static inline bool {}_validate(const {}* self) {{",
604 fn_prefix, c_name
605 )?;
606 writeln!(output, " {} v = self->value;", c_type)?;
607 writeln!(output, " return {check};", check = check)?;
608 writeln!(output, "}}")?;
609
610 Ok(())
611}
612
613fn string_base_type_name(ty: &Type) -> &'static str {
615 match ty {
616 Type::IA5String(_) => "IA5String",
617 Type::PrintableString(_) => "PrintableString",
618 Type::Utf8String(_) => "UTF8String",
619 Type::OctetString(_) => "OCTET STRING",
620 Type::BitString(_) => "BIT STRING",
621 _ => "STRING",
622 }
623}
624
625fn generate_c_length_check(len_var: &str, size_constraint: &SubtypeConstraint) -> String {
631 match size_constraint {
632 SubtypeConstraint::SingleValue(val) => match val {
633 ConstraintValue::Integer(n) => format!("{} == {}U", len_var, n),
634 ConstraintValue::Min | ConstraintValue::Max => "1".to_string(),
635 ConstraintValue::NamedValue(name) => format!("{} == {}", len_var, name),
636 },
637 SubtypeConstraint::ValueRange { min, max } => {
638 let mut parts: Vec<String> = Vec::new();
639 match min {
640 ConstraintValue::Integer(n) if *n > 0 => {
641 parts.push(format!("{} >= {}U", len_var, n));
642 }
643 ConstraintValue::Integer(_) | ConstraintValue::Min => {}
645 ConstraintValue::Max => parts.push(format!("{} >= UINT32_MAX", len_var)),
646 ConstraintValue::NamedValue(name) => {
647 parts.push(format!("{} >= {}", len_var, name));
648 }
649 }
650 match max {
651 ConstraintValue::Integer(n) => parts.push(format!("{} <= {}U", len_var, n)),
652 ConstraintValue::Max => {}
653 ConstraintValue::Min => parts.push(format!("{} == 0", len_var)),
654 ConstraintValue::NamedValue(name) => {
655 parts.push(format!("{} <= {}", len_var, name));
656 }
657 }
658 if parts.is_empty() {
659 "1".to_string()
660 } else {
661 format!("({})", parts.join(" && "))
662 }
663 }
664 SubtypeConstraint::Union(elements) => {
665 let checks: Vec<String> = elements
666 .iter()
667 .map(|e| generate_c_length_check(len_var, e))
668 .collect();
669 format!("({})", checks.join(" || "))
670 }
671 SubtypeConstraint::Intersection(elements) => {
672 let checks: Vec<String> = elements
673 .iter()
674 .map(|e| generate_c_length_check(len_var, e))
675 .collect();
676 format!("({})", checks.join(" && "))
677 }
678 SubtypeConstraint::Complement(inner) => {
679 let inner_check = generate_c_length_check(len_var, inner);
680 format!("!({})", inner_check)
681 }
682 _ => "1 /* unsupported size constraint */".to_string(),
683 }
684}
685
686pub(crate) fn format_c_char_literal(c: char) -> String {
688 if c == '\'' || c == '\\' {
689 format!("'\\{}'", c)
690 } else if c.is_ascii() && (c as u8) >= 0x20 && (c as u8) < 0x7f {
691 format!("'{}'", c)
692 } else {
693 format!("'\\x{:02x}'", c as u32)
694 }
695}
696
697pub(crate) fn generate_c_alphabet_expr(ranges: &[CharRange]) -> String {
702 if ranges.is_empty() {
703 return "1 /* no alphabet constraint */".to_string();
704 }
705 let parts: Vec<String> = ranges
706 .iter()
707 .map(|r| {
708 if r.min == r.max {
709 format!("_c == {}", format_c_char_literal(r.min))
710 } else {
711 format!(
712 "(_c >= {} && _c <= {})",
713 format_c_char_literal(r.min),
714 format_c_char_literal(r.max)
715 )
716 }
717 })
718 .collect();
719 parts.join(" || ")
720}
721
722fn emit_c_string_validation_stmts(
728 out: &mut String,
729 indent: &str,
730 constraint: &SubtypeConstraint,
731 len_var: &str,
732 value_expr: &str,
733) -> Result<(), Box<dyn std::error::Error>> {
734 match constraint {
735 SubtypeConstraint::SizeConstraint(inner) => {
736 let check = generate_c_length_check(len_var, inner);
737 writeln!(
738 out,
739 "{}if (!({check})) return false;",
740 indent,
741 check = check
742 )?;
743 }
744 SubtypeConstraint::PermittedAlphabet(ranges) => {
745 let alpha_expr = generate_c_alphabet_expr(ranges);
746 writeln!(out, "{}{{", indent)?;
747 writeln!(out, "{} uint32_t _i;", indent)?;
748 writeln!(
749 out,
750 "{} const unsigned char *_ap = (const unsigned char *){}.data;",
751 indent, value_expr
752 )?;
753 writeln!(out, "{} bool _ok = true;", indent)?;
754 writeln!(
755 out,
756 "{} for (_i = 0; _i < {lv} && _ok; _i++) {{",
757 indent,
758 lv = len_var
759 )?;
760 writeln!(out, "{} unsigned char _c = _ap[_i];", indent)?;
761 writeln!(out, "{} _ok = {alpha};", indent, alpha = alpha_expr)?;
762 writeln!(out, "{} }}", indent)?;
763 writeln!(out, "{} if (!_ok) return false;", indent)?;
764 writeln!(out, "{}}}", indent)?;
765 }
766 SubtypeConstraint::Pattern(p) => {
767 writeln!(
768 out,
769 "{}/* PATTERN constraint \"{}\" not enforced at runtime; use --with-regex or --with-pcre with --impl (single-file) or --emit impl/both (multi-file) to enable */",
770 indent, p
771 )?;
772 }
773 SubtypeConstraint::ContainedSubtype(_ty) => {
774 writeln!(
775 out,
776 "{}/* CONTAINING constraint not enforced at runtime; use --with-containing with --impl (single-file) or --emit impl/both (multi-file) to enable */",
777 indent
778 )?;
779 }
780 SubtypeConstraint::Intersection(parts) => {
781 for part in parts {
782 emit_c_string_validation_stmts(out, indent, part, len_var, value_expr)?;
783 }
784 }
785 SubtypeConstraint::Union(_) => {
786 writeln!(
787 out,
788 "{}/* Union constraint: complex, treated as unchecked */",
789 indent
790 )?;
791 }
792 SubtypeConstraint::Complement(_) => {
793 writeln!(
794 out,
795 "{}/* Complement constraint: complex, treated as unchecked */",
796 indent
797 )?;
798 }
799 _ => {
800 writeln!(out, "{}/* unsupported constraint: skipped */", indent)?;
801 }
802 }
803 Ok(())
804}
805
806fn generate_named_bit_string_c(
814 output: &mut String,
815 name: &str,
816 bits: &[NamedNumber],
817 generate_helpers: bool,
818) -> Result<(), Box<dyn std::error::Error>> {
819 let struct_name = to_pascal_case(name);
820 let prefix = to_screaming_snake_case(name);
823
824 writeln!(output, "/* {} — BIT STRING with named bits */", struct_name)?;
825 writeln!(output, "typedef SyntaBitString {};", struct_name)?;
826
827 if bits.is_empty() {
828 return Ok(());
829 }
830
831 writeln!(output)?;
832 writeln!(output, "/* Named bit positions for {} */", struct_name)?;
833
834 let macro_names: Vec<String> = bits
836 .iter()
837 .map(|b| {
838 let bit_upper = to_snake_case(&b.name).to_uppercase();
839 format!("{prefix}_{bit_upper}_BIT")
840 })
841 .collect();
842 let max_len = macro_names.iter().map(|s| s.len()).max().unwrap_or(0);
843
844 for (bit, macro_name) in bits.iter().zip(¯o_names) {
845 writeln!(
846 output,
847 "#define {macro_name:width$} {val}",
848 width = max_len,
849 val = bit.value
850 )?;
851 }
852
853 if generate_helpers {
854 writeln!(output)?;
855 writeln!(output, "/* Bit-operation helpers for {} */", struct_name)?;
856 writeln!(
857 output,
858 "/* (requires synta_bitstring_is_set/set/clear from synta.h) */"
859 )?;
860 let is_set = format!("{prefix}_IS_SET(bs, bit)");
861 let set = format!("{prefix}_SET(bs, bit)");
862 let clear = format!("{prefix}_CLEAR(bs, bit)");
863 let helper_max = [is_set.len(), set.len(), clear.len()]
864 .iter()
865 .copied()
866 .max()
867 .unwrap_or(0);
868 writeln!(
869 output,
870 "#define {is_set:width$} synta_bitstring_is_set(&(bs).data, (bit))",
871 width = helper_max
872 )?;
873 writeln!(
874 output,
875 "#define {set:width$} synta_bitstring_set(&(bs).data, (bit))",
876 width = helper_max
877 )?;
878 writeln!(
879 output,
880 "#define {clear:width$} synta_bitstring_clear(&(bs).data, (bit))",
881 width = helper_max
882 )?;
883 }
884
885 Ok(())
886}
887
888fn generate_constrained_string_c(
898 output: &mut String,
899 name: &str,
900 base_type_str: &str,
901 constraint: &SubtypeConstraint,
902) -> Result<(), Box<dyn std::error::Error>> {
903 let c_name = to_pascal_case(name);
904 let fn_prefix = to_snake_case(name);
905 let display = format_c_constraint_display(constraint);
906
907 writeln!(output, "/* {} ({}) */", base_type_str, display)?;
909 writeln!(
910 output,
911 "typedef struct {{ SyntaByteArray value; }} {};",
912 c_name
913 )?;
914 writeln!(output)?;
915
916 writeln!(
918 output,
919 "/** Create {c}: validates {bt} ({d}). Returns false if constraint violated. */",
920 c = c_name,
921 bt = base_type_str,
922 d = display
923 )?;
924 writeln!(
925 output,
926 "static inline bool {fn}_new(SyntaByteArray value, {c}* out) {{",
927 fn = fn_prefix,
928 c = c_name
929 )?;
930 writeln!(output, " uint32_t _len = value.len;")?;
931 emit_c_string_validation_stmts(output, " ", constraint, "_len", "value")?;
932 writeln!(output, " out->value = value;")?;
933 writeln!(output, " return true;")?;
934 writeln!(output, "}}")?;
935 writeln!(output)?;
936
937 writeln!(
939 output,
940 "/** Create {} without validation (use with caution). */",
941 c_name
942 )?;
943 writeln!(
944 output,
945 "static inline {c} {fn}_new_unchecked(SyntaByteArray value) {{",
946 c = c_name,
947 fn = fn_prefix
948 )?;
949 writeln!(
950 output,
951 " {c} out; out.value = value; return out;",
952 c = c_name
953 )?;
954 writeln!(output, "}}")?;
955 writeln!(output)?;
956
957 writeln!(
959 output,
960 "/** Get a borrowed view of the {} value (owned flag cleared). */",
961 c_name
962 )?;
963 writeln!(
964 output,
965 "static inline SyntaByteArray {fn}_get(const {c}* self) {{",
966 fn = fn_prefix,
967 c = c_name
968 )?;
969 writeln!(
970 output,
971 " SyntaByteArray r = self->value; r.owned = 0; return r;"
972 )?;
973 writeln!(output, "}}")?;
974 writeln!(output)?;
975
976 writeln!(
978 output,
979 "/** Check that {} satisfies {} ({}). */",
980 c_name, base_type_str, display
981 )?;
982 writeln!(
983 output,
984 "static inline bool {fn}_validate(const {c}* self) {{",
985 fn = fn_prefix,
986 c = c_name
987 )?;
988 writeln!(output, " uint32_t _len = self->value.len;")?;
989 emit_c_string_validation_stmts(output, " ", constraint, "_len", "self->value")?;
990 writeln!(output, " return true;")?;
991 writeln!(output, "}}")?;
992 writeln!(output)?;
993
994 writeln!(
996 output,
997 "/** Free {} if its value buffer is owned. */",
998 c_name
999 )?;
1000 writeln!(
1001 output,
1002 "static inline void {fn}_free({c}* self) {{",
1003 fn = fn_prefix,
1004 c = c_name
1005 )?;
1006 writeln!(
1007 output,
1008 " if (self->value.owned != 0) {{ synta_byte_array_free(&self->value); self->value.owned = 0; }}"
1009 )?;
1010 writeln!(output, "}}")?;
1011
1012 Ok(())
1013}
1014
1015fn generate_type_definition(
1017 output: &mut String,
1018 def: &Definition,
1019 generate_helpers: bool,
1020) -> Result<(), Box<dyn std::error::Error>> {
1021 match &def.ty {
1022 Type::Sequence(fields) | Type::Set(fields) => {
1023 generate_sequence_struct(output, &def.name, fields)?;
1024 }
1025 Type::Choice(variants) => {
1026 generate_choice_struct(output, &def.name, variants)?;
1027 }
1028 Type::Integer(_, named_numbers) if !named_numbers.is_empty() => {
1029 generate_defines_for_integer(output, &def.name, named_numbers)?;
1030 }
1031 Type::Enumerated(named_values) => {
1032 generate_enum_for_integer(output, &def.name, named_values)?;
1033 }
1034 Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
1035 let struct_name = to_pascal_case(&def.name);
1039 let elem_type = get_c_type(inner);
1040 writeln!(output, "struct {} {{", struct_name)?;
1041 writeln!(output, " size_t count;")?;
1042 writeln!(output, " {}* items;", elem_type)?;
1043 writeln!(output, "}};")?;
1044 }
1045 Type::Constrained {
1046 base_type,
1047 constraint,
1048 } => {
1049 match (base_type.as_ref(), &constraint.spec) {
1050 (Type::Integer(_, named_numbers), ConstraintSpec::Subtype(subtype)) => {
1051 generate_constrained_integer_c(output, &def.name, subtype, named_numbers)?;
1052 }
1053 (
1055 Type::BitString(_),
1056 ConstraintSpec::Subtype(SubtypeConstraint::NamedBitList(bits)),
1057 ) => {
1058 generate_named_bit_string_c(output, &def.name, bits, generate_helpers)?;
1059 }
1060 (
1066 Type::BitString(_),
1067 ConstraintSpec::Subtype(SubtypeConstraint::Intersection(parts)),
1068 ) if parts
1069 .iter()
1070 .any(|p| matches!(p, SubtypeConstraint::NamedBitList(_))) =>
1071 {
1072 let bits = parts
1073 .iter()
1074 .find_map(|p| {
1075 if let SubtypeConstraint::NamedBitList(b) = p {
1076 Some(b.as_slice())
1077 } else {
1078 None
1079 }
1080 })
1081 .unwrap_or(&[]);
1082 generate_named_bit_string_c(output, &def.name, bits, generate_helpers)?;
1083 }
1084 (
1085 Type::IA5String(_)
1086 | Type::PrintableString(_)
1087 | Type::Utf8String(_)
1088 | Type::OctetString(_)
1089 | Type::BitString(_),
1090 ConstraintSpec::Subtype(subtype),
1091 ) => {
1092 let base_name = string_base_type_name(base_type);
1093 generate_constrained_string_c(output, &def.name, base_name, subtype)?;
1094 }
1095 _ => {
1096 let c_name = to_pascal_case(&def.name);
1098 let base_c_type = get_c_type(base_type);
1099 writeln!(
1100 output,
1101 "/* Constrained type: constraint validation not yet implemented */"
1102 )?;
1103 writeln!(output, "typedef {} {};", base_c_type, c_name)?;
1104 }
1105 }
1106 }
1107 Type::TypeRef(_) => {
1108 let c_name = to_pascal_case(&def.name);
1110 let base_type = get_c_type(&def.ty);
1111 writeln!(output, "typedef {} {};", base_type, c_name)?;
1112 }
1113 _ => {
1114 let c_name = to_pascal_case(&def.name);
1116 let base_type = get_c_type(&def.ty);
1117 writeln!(output, "typedef {} {};", base_type, c_name)?;
1118 }
1119 }
1120 Ok(())
1121}
1122
1123fn tag_annotation_comment(ty: &Type) -> Option<String> {
1127 if let Type::Tagged { tag: tag_info, .. } = ty {
1128 let cls = match tag_info.class {
1129 TagClass::Universal => format!("UNIVERSAL {}", tag_info.number),
1130 TagClass::Application => format!("APPLICATION {}", tag_info.number),
1131 TagClass::ContextSpecific => tag_info.number.to_string(),
1132 TagClass::Private => format!("PRIVATE {}", tag_info.number),
1133 };
1134 let mode = match tag_info.tagging {
1135 Tagging::Explicit => "EXPLICIT",
1136 Tagging::Implicit => "IMPLICIT",
1137 };
1138 Some(format!("[{}] {}", cls, mode))
1139 } else {
1140 None
1141 }
1142}
1143
1144fn generate_sequence_struct(
1146 output: &mut String,
1147 name: &str,
1148 fields: &[SequenceField],
1149) -> Result<(), Box<dyn std::error::Error>> {
1150 let struct_name = to_pascal_case(name);
1151 writeln!(output, "struct {} {{", struct_name)?;
1152
1153 for field in fields {
1154 if matches!(field.ty, Type::Null) {
1156 continue;
1157 }
1158
1159 let field_name = to_snake_case(&field.name);
1160
1161 match &field.ty {
1163 Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
1164 if field.optional {
1166 writeln!(output, " bool has_{};", field_name)?;
1167 }
1168 writeln!(output, " struct {{")?;
1169 for inner_field in inner_fields {
1170 if matches!(inner_field.ty, Type::Null) {
1171 continue;
1172 }
1173 let inner_name = to_snake_case(&inner_field.name);
1174 let inner_type = get_c_type_for_field(&inner_field.ty);
1175 if inner_field.optional {
1176 writeln!(output, " bool has_{};", inner_name)?;
1177 writeln!(output, " {} {};", inner_type, inner_name)?;
1178 } else {
1179 writeln!(output, " {} {};", inner_type, inner_name)?;
1180 }
1181 }
1182 writeln!(output, " }} {};", field_name)?;
1183 }
1184 _ => {
1185 let field_type = get_c_type_for_field(&field.ty);
1186 let tag_note = tag_annotation_comment(&field.ty);
1187
1188 if matches!(field.ty, Type::SequenceOf(_, _) | Type::SetOf(_, _)) {
1190 writeln!(output, " size_t {}_count;", field_name)?;
1191 writeln!(output, " {} {};", field_type, field_name)?;
1192 } else if field.optional {
1193 writeln!(output, " bool has_{};", field_name)?;
1194 if let Some(ref tn) = tag_note {
1195 writeln!(output, " {} {}; /* {} */", field_type, field_name, tn)?;
1196 } else {
1197 writeln!(output, " {} {};", field_type, field_name)?;
1198 }
1199 } else if let Some(ref dv) = field.default {
1200 if let Some(ref tn) = tag_note {
1201 writeln!(
1202 output,
1203 " {} {}; /* {} DEFAULT {} */",
1204 field_type, field_name, tn, dv
1205 )?;
1206 } else {
1207 writeln!(
1208 output,
1209 " {} {}; /* DEFAULT {} */",
1210 field_type, field_name, dv
1211 )?;
1212 }
1213 } else if let Some(ref tn) = tag_note {
1214 writeln!(output, " {} {}; /* {} */", field_type, field_name, tn)?;
1215 } else {
1216 writeln!(output, " {} {};", field_type, field_name)?;
1217 }
1218 }
1219 }
1220 }
1221
1222 writeln!(output, "}};")?;
1223 Ok(())
1224}
1225
1226fn get_c_type_for_field(ty: &Type) -> String {
1228 match ty {
1229 Type::TypeRef(name) => to_pascal_case(name),
1230 Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
1231 format!("{}*", get_c_type(inner))
1233 }
1234 _ => get_c_type(ty),
1235 }
1236}
1237
1238fn generate_choice_struct(
1240 output: &mut String,
1241 name: &str,
1242 variants: &[ChoiceVariant],
1243) -> Result<(), Box<dyn std::error::Error>> {
1244 let struct_name = to_pascal_case(name);
1245 let tag_enum_name = format!("{}Tag", struct_name);
1246
1247 writeln!(output, "typedef enum {} {{", tag_enum_name)?;
1249 for (i, variant) in variants.iter().enumerate() {
1250 let variant_name = to_pascal_case(&variant.name);
1251 let comma = if i < variants.len() - 1 { "," } else { "" };
1252 writeln!(output, " {}_{}{}", tag_enum_name, variant_name, comma)?;
1253 }
1254 writeln!(output, "}} {};", tag_enum_name)?;
1255 writeln!(output)?;
1256
1257 writeln!(output, "struct {} {{", struct_name)?;
1259 writeln!(output, " {} tag;", tag_enum_name)?;
1260 writeln!(output, " union {{")?;
1261
1262 for variant in variants {
1263 if matches!(variant.ty, Type::Null) {
1265 continue;
1266 }
1267
1268 let variant_name = to_snake_case(&variant.name);
1269 if matches!(peel_type(&variant.ty), Type::Sequence(_) | Type::Set(_)) {
1272 writeln!(
1273 output,
1274 " void* {}; /* inline SEQUENCE/SET — define separately and cast */",
1275 variant_name
1276 )?;
1277 } else {
1278 let variant_type = get_c_type_for_field(&variant.ty);
1279 writeln!(output, " {} {};", variant_type, variant_name)?;
1280 }
1281 }
1282
1283 writeln!(output, " }} value;")?;
1284 writeln!(output, "}};")?;
1285 Ok(())
1286}
1287
1288fn generate_enum_for_integer(
1290 output: &mut String,
1291 name: &str,
1292 named_numbers: &[NamedNumber],
1293) -> Result<(), Box<dyn std::error::Error>> {
1294 let enum_name = to_pascal_case(name);
1295 writeln!(output, "enum {} {{", enum_name)?;
1296
1297 for (i, nn) in named_numbers.iter().enumerate() {
1298 let variant_name = to_screaming_snake_case(&nn.name);
1299 let comma = if i < named_numbers.len() - 1 { "," } else { "" };
1300 writeln!(
1301 output,
1302 " {}_{} = {}{}",
1303 enum_name, variant_name, nn.value, comma
1304 )?;
1305 }
1306
1307 writeln!(output, "}};")?;
1308 Ok(())
1309}
1310
1311fn generate_defines_for_integer(
1340 output: &mut String,
1341 name: &str,
1342 named_numbers: &[NamedNumber],
1343) -> Result<(), Box<dyn std::error::Error>> {
1344 let type_name = to_pascal_case(name);
1345 let prefix = to_screaming_snake_case(name);
1346
1347 writeln!(output, "/* Named integer values for {} */", type_name)?;
1348
1349 let macro_names: Vec<String> = named_numbers
1351 .iter()
1352 .map(|nn| format!("{}_{}", prefix, to_snake_case(&nn.name).to_uppercase()))
1353 .collect();
1354 let max_len = macro_names.iter().map(|s| s.len()).max().unwrap_or(0);
1355
1356 for (nn, macro_name) in named_numbers.iter().zip(¯o_names) {
1357 writeln!(
1358 output,
1359 "#define {:width$} ((int64_t){})",
1360 macro_name,
1361 nn.value,
1362 width = max_len
1363 )?;
1364 }
1365
1366 Ok(())
1367}
1368
1369fn generate_encoder_decoder_prototypes(
1371 output: &mut String,
1372 def: &Definition,
1373 arena_mode: bool,
1374) -> Result<(), Box<dyn std::error::Error>> {
1375 let c_name = to_pascal_case(&def.name);
1376 let fn_prefix = to_snake_case(&def.name);
1377
1378 writeln!(
1380 output,
1381 "SyntaErrorCode {}_decode(SyntaDecoder* decoder, {}* out);",
1382 fn_prefix, c_name
1383 )?;
1384
1385 if arena_mode {
1387 writeln!(
1388 output,
1389 "SyntaErrorCode {}_decode_arena(SyntaDecoder* decoder, SyntaArena* arena, {}* out);",
1390 fn_prefix, c_name
1391 )?;
1392 }
1393
1394 writeln!(
1396 output,
1397 "SyntaErrorCode {}_encode(SyntaEncoder* encoder, const {}* value);",
1398 fn_prefix, c_name
1399 )?;
1400
1401 match &def.ty {
1403 Type::Sequence(_)
1404 | Type::Set(_)
1405 | Type::Choice(_)
1406 | Type::SequenceOf(_, _)
1407 | Type::SetOf(_, _) => {
1408 writeln!(output, "void {}_free({}* value);", fn_prefix, c_name)?;
1409 }
1410 _ => {}
1411 }
1412
1413 if let Type::Sequence(fields) | Type::Set(fields) = peel_type(&def.ty) {
1415 if fields.iter().all(|f| f.optional || f.default.is_some()) {
1416 writeln!(output, "{} {}_default(void);", c_name, fn_prefix)?;
1417 }
1418 }
1419
1420 Ok(())
1421}
1422
1423fn generate_helper_functions(
1425 output: &mut String,
1426 def: &Definition,
1427) -> Result<(), Box<dyn std::error::Error>> {
1428 let c_name = to_pascal_case(&def.name);
1429 let fn_prefix = to_snake_case(&def.name);
1430
1431 match &def.ty {
1432 Type::Sequence(fields) | Type::Set(fields) => {
1433 generate_init_helper(output, &c_name, &fn_prefix)?;
1434 writeln!(output)?;
1435 generate_validate_sequence(output, &c_name, &fn_prefix, fields)?;
1436 writeln!(output)?;
1437 generate_print_sequence(output, &c_name, &fn_prefix, fields)?;
1438 }
1439 Type::Choice(variants) => {
1440 generate_validate_choice(output, &c_name, &fn_prefix, variants)?;
1441 writeln!(output)?;
1442 generate_print_choice(output, &c_name, &fn_prefix, variants)?;
1443 }
1444 Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
1445 generate_init_helper(output, &c_name, &fn_prefix)?;
1446 writeln!(output)?;
1447 generate_validate_array(output, &c_name, &fn_prefix)?;
1448 writeln!(output)?;
1449 generate_print_array(output, &c_name, &fn_prefix, inner)?;
1450 }
1451 _ => {}
1452 }
1453
1454 Ok(())
1455}
1456
1457fn generate_init_helper(
1462 output: &mut String,
1463 c_name: &str,
1464 fn_prefix: &str,
1465) -> Result<(), Box<dyn std::error::Error>> {
1466 writeln!(
1467 output,
1468 "static inline void {}_init({}* value) {{",
1469 fn_prefix, c_name
1470 )?;
1471 writeln!(output, " if (value != NULL) {{")?;
1472 writeln!(output, " memset(value, 0, sizeof({}));", c_name)?;
1473 writeln!(output, " }}")?;
1474 writeln!(output, "}}")?;
1475 Ok(())
1476}
1477
1478fn generate_validate_sequence(
1486 output: &mut String,
1487 c_name: &str,
1488 fn_prefix: &str,
1489 fields: &[SequenceField],
1490) -> Result<(), Box<dyn std::error::Error>> {
1491 writeln!(
1492 output,
1493 "static inline SyntaErrorCode {}_validate(const {}* value) {{",
1494 fn_prefix, c_name
1495 )?;
1496 writeln!(
1497 output,
1498 " if (value == NULL) return SyntaErrorCode_NullPointer;"
1499 )?;
1500 for field in fields {
1501 if matches!(field.ty, Type::Null) {
1502 continue;
1503 }
1504 let field_name = to_snake_case(&field.name);
1505 if is_pointer_c_type(&field.ty) {
1506 if field.optional {
1507 writeln!(
1508 output,
1509 " if (value->has_{name} && value->{name} == NULL) return SyntaErrorCode_InvalidArgument;",
1510 name = field_name
1511 )?;
1512 } else {
1513 writeln!(
1514 output,
1515 " if (value->{} == NULL) return SyntaErrorCode_InvalidArgument;",
1516 field_name
1517 )?;
1518 }
1519 }
1520 }
1521 writeln!(output, " return SyntaErrorCode_Success;")?;
1522 writeln!(output, "}}")?;
1523 Ok(())
1524}
1525
1526fn generate_print_sequence(
1533 output: &mut String,
1534 c_name: &str,
1535 fn_prefix: &str,
1536 fields: &[SequenceField],
1537) -> Result<(), Box<dyn std::error::Error>> {
1538 writeln!(
1539 output,
1540 "static inline void {}_print(const {}* value, FILE* stream) {{",
1541 fn_prefix, c_name
1542 )?;
1543 writeln!(
1544 output,
1545 " if (value == NULL) {{ fprintf(stream, \"{}(null)\\n\"); return; }}",
1546 c_name
1547 )?;
1548 writeln!(output, " fprintf(stream, \"{}{{\\n\");", c_name)?;
1549 for field in fields {
1550 if matches!(field.ty, Type::Null) {
1551 continue;
1552 }
1553 let field_name = to_snake_case(&field.name);
1554 write_sequence_field_print(output, &field_name, &field.ty, field.optional)?;
1555 }
1556 writeln!(output, " fprintf(stream, \"}}\\n\");")?;
1557 writeln!(output, "}}")?;
1558 Ok(())
1559}
1560
1561fn write_sequence_field_print(
1563 output: &mut String,
1564 field_name: &str,
1565 ty: &Type,
1566 optional: bool,
1567) -> Result<(), Box<dyn std::error::Error>> {
1568 let base = peel_type(ty);
1569
1570 if matches!(base, Type::SequenceOf(_, _) | Type::SetOf(_, _)) {
1572 write_field_value_print(
1573 output,
1574 field_name,
1575 base,
1576 &format!("value->{}", field_name),
1577 " ",
1578 )?;
1579 return Ok(());
1580 }
1581
1582 if optional {
1583 writeln!(output, " if (value->has_{}) {{", field_name)?;
1584 write_field_value_print(
1585 output,
1586 field_name,
1587 base,
1588 &format!("value->{}", field_name),
1589 " ",
1590 )?;
1591 writeln!(output, " }} else {{")?;
1592 writeln!(
1593 output,
1594 " fprintf(stream, \" {}: (absent)\\n\");",
1595 field_name
1596 )?;
1597 writeln!(output, " }}")?;
1598 } else {
1599 write_field_value_print(
1600 output,
1601 field_name,
1602 base,
1603 &format!("value->{}", field_name),
1604 " ",
1605 )?;
1606 }
1607 Ok(())
1608}
1609
1610fn write_field_value_print(
1612 output: &mut String,
1613 field_name: &str,
1614 ty: &Type,
1615 field_expr: &str,
1616 indent: &str,
1617) -> Result<(), Box<dyn std::error::Error>> {
1618 match ty {
1619 Type::Boolean => {
1620 writeln!(
1621 output,
1622 "{}fprintf(stream, \" {}: %s\\n\", {} ? \"true\" : \"false\");",
1623 indent, field_name, field_expr
1624 )?;
1625 }
1626 Type::Real => {
1627 writeln!(
1628 output,
1629 "{}fprintf(stream, \" {}: %g\\n\", {});",
1630 indent, field_name, field_expr
1631 )?;
1632 }
1633 Type::OctetString(_)
1634 | Type::Utf8String(_)
1635 | Type::PrintableString(_)
1636 | Type::IA5String(_)
1637 | Type::UtcTime
1638 | Type::GeneralizedTime
1639 | Type::Any
1640 | Type::AnyDefinedBy(_) => {
1641 writeln!(output, "{}if ({} != NULL)", indent, field_expr)?;
1642 writeln!(
1643 output,
1644 "{} fprintf(stream, \" {}: <string(%zu bytes)>\\n\", synta_octet_string_len({}));",
1645 indent, field_name, field_expr
1646 )?;
1647 writeln!(output, "{}else", indent)?;
1648 writeln!(
1649 output,
1650 "{} fprintf(stream, \" {}: NULL\\n\");",
1651 indent, field_name
1652 )?;
1653 }
1654 Type::Integer(_, _) | Type::Enumerated(_) => {
1655 writeln!(output, "{}if ({} != NULL)", indent, field_expr)?;
1656 writeln!(
1657 output,
1658 "{} fprintf(stream, \" {}: <integer>\\n\");",
1659 indent, field_name
1660 )?;
1661 writeln!(output, "{}else", indent)?;
1662 writeln!(
1663 output,
1664 "{} fprintf(stream, \" {}: NULL\\n\");",
1665 indent, field_name
1666 )?;
1667 }
1668 Type::ObjectIdentifier => {
1669 writeln!(output, "{}if ({} != NULL)", indent, field_expr)?;
1670 writeln!(
1671 output,
1672 "{} fprintf(stream, \" {}: <oid>\\n\");",
1673 indent, field_name
1674 )?;
1675 writeln!(output, "{}else", indent)?;
1676 writeln!(
1677 output,
1678 "{} fprintf(stream, \" {}: NULL\\n\");",
1679 indent, field_name
1680 )?;
1681 }
1682 Type::BitString(_) => {
1683 writeln!(
1684 output,
1685 "{}fprintf(stream, \" {}: <bit-string(%u bytes)>\\n\", (unsigned int){}.data.len);",
1686 indent, field_name, field_expr
1687 )?;
1688 }
1689 Type::TypeRef(name) => {
1690 let type_name = to_pascal_case(name);
1691 writeln!(
1692 output,
1693 "{}fprintf(stream, \" {}: <{}>\\n\");",
1694 indent, field_name, type_name
1695 )?;
1696 }
1697 Type::Sequence(_) | Type::Set(_) => {
1698 writeln!(
1699 output,
1700 "{}fprintf(stream, \" {}: <struct>\\n\");",
1701 indent, field_name
1702 )?;
1703 }
1704 Type::SequenceOf(_, _) | Type::SetOf(_, _) => {
1705 writeln!(
1707 output,
1708 "{}fprintf(stream, \" {}: [%zu elements]\\n\", {}_count);",
1709 indent, field_name, field_expr
1710 )?;
1711 }
1712 Type::Choice(_) => {
1713 writeln!(
1714 output,
1715 "{}fprintf(stream, \" {}: <choice>\\n\");",
1716 indent, field_name
1717 )?;
1718 }
1719 Type::Null => {}
1720 _ => {
1722 writeln!(
1723 output,
1724 "{}fprintf(stream, \" {}: <value>\\n\");",
1725 indent, field_name
1726 )?;
1727 }
1728 }
1729 Ok(())
1730}
1731
1732fn generate_validate_choice(
1739 output: &mut String,
1740 c_name: &str,
1741 fn_prefix: &str,
1742 variants: &[ChoiceVariant],
1743) -> Result<(), Box<dyn std::error::Error>> {
1744 let tag_enum = format!("{}Tag", c_name);
1745 writeln!(
1746 output,
1747 "static inline SyntaErrorCode {}_validate(const {}* value) {{",
1748 fn_prefix, c_name
1749 )?;
1750 writeln!(
1751 output,
1752 " if (value == NULL) return SyntaErrorCode_NullPointer;"
1753 )?;
1754 writeln!(output, " switch (value->tag) {{")?;
1755 for variant in variants {
1756 let variant_name = to_pascal_case(&variant.name);
1757 writeln!(output, " case {}_{}: break;", tag_enum, variant_name)?;
1758 }
1759 writeln!(
1760 output,
1761 " default: return SyntaErrorCode_InvalidEncoding;"
1762 )?;
1763 writeln!(output, " }}")?;
1764 writeln!(output, " return SyntaErrorCode_Success;")?;
1765 writeln!(output, "}}")?;
1766 Ok(())
1767}
1768
1769fn generate_print_choice(
1775 output: &mut String,
1776 c_name: &str,
1777 fn_prefix: &str,
1778 variants: &[ChoiceVariant],
1779) -> Result<(), Box<dyn std::error::Error>> {
1780 let tag_enum = format!("{}Tag", c_name);
1781 writeln!(
1782 output,
1783 "static inline void {}_print(const {}* value, FILE* stream) {{",
1784 fn_prefix, c_name
1785 )?;
1786 writeln!(
1787 output,
1788 " if (value == NULL) {{ fprintf(stream, \"{}(null)\\n\"); return; }}",
1789 c_name
1790 )?;
1791 writeln!(output, " switch (value->tag) {{")?;
1792 for variant in variants {
1793 let variant_name = to_pascal_case(&variant.name);
1794 let variant_field = to_snake_case(&variant.name);
1795 writeln!(output, " case {}_{}:", tag_enum, variant_name)?;
1796 writeln!(
1797 output,
1798 " fprintf(stream, \"{}{{ {} }}\\n\");",
1799 c_name, variant_field
1800 )?;
1801 writeln!(output, " break;")?;
1802 }
1803 writeln!(output, " default:")?;
1804 writeln!(
1805 output,
1806 " fprintf(stream, \"{}{{ <unknown tag> }}\\n\");",
1807 c_name
1808 )?;
1809 writeln!(output, " break;")?;
1810 writeln!(output, " }}")?;
1811 writeln!(output, "}}")?;
1812 Ok(())
1813}
1814
1815fn generate_validate_array(
1822 output: &mut String,
1823 c_name: &str,
1824 fn_prefix: &str,
1825) -> Result<(), Box<dyn std::error::Error>> {
1826 writeln!(
1827 output,
1828 "static inline SyntaErrorCode {}_validate(const {}* value) {{",
1829 fn_prefix, c_name
1830 )?;
1831 writeln!(
1832 output,
1833 " if (value == NULL) return SyntaErrorCode_NullPointer;"
1834 )?;
1835 writeln!(
1836 output,
1837 " if (value->count > 0 && value->items == NULL) return SyntaErrorCode_InvalidArgument;"
1838 )?;
1839 writeln!(output, " return SyntaErrorCode_Success;")?;
1840 writeln!(output, "}}")?;
1841 Ok(())
1842}
1843
1844fn generate_print_array(
1852 output: &mut String,
1853 c_name: &str,
1854 fn_prefix: &str,
1855 _inner: &Type,
1856) -> Result<(), Box<dyn std::error::Error>> {
1857 writeln!(
1858 output,
1859 "static inline void {}_print(const {}* value, FILE* stream) {{",
1860 fn_prefix, c_name
1861 )?;
1862 writeln!(
1863 output,
1864 " if (value == NULL) {{ fprintf(stream, \"{}(null)\\n\"); return; }}",
1865 c_name
1866 )?;
1867 writeln!(
1868 output,
1869 " fprintf(stream, \"{}[%zu]\\n\", value->count);",
1870 c_name
1871 )?;
1872 writeln!(output, "}}")?;
1873 Ok(())
1874}
1875
1876fn build_c_oid_registry(values: &[ValueAssignment]) -> std::collections::HashMap<String, Vec<u32>> {
1883 use std::collections::HashMap;
1884 let mut registry: HashMap<String, Vec<u32>> = HashMap::new();
1885
1886 let mut changed = true;
1887 while changed {
1888 changed = false;
1889 for va in values {
1890 if registry.contains_key(&va.name) {
1891 continue;
1892 }
1893 if let Value::ObjectIdentifier(components) = &va.value {
1894 let mut resolved = Vec::new();
1895 let mut can_resolve = true;
1896 for component in components {
1897 match component {
1898 OidComponent::Number(n) => resolved.push(*n),
1899 OidComponent::NamedRef(name) => {
1900 if let Some(base) = registry.get(name) {
1901 resolved.extend_from_slice(base);
1902 } else {
1903 can_resolve = false;
1904 break;
1905 }
1906 }
1907 }
1908 }
1909 if can_resolve {
1910 registry.insert(va.name.clone(), resolved);
1911 changed = true;
1912 }
1913 }
1914 }
1915 }
1916 registry
1917}
1918
1919fn escape_c_string(s: &str) -> String {
1924 let mut out = String::with_capacity(s.len());
1925 for ch in s.chars() {
1926 match ch {
1927 '\\' => out.push_str("\\\\"),
1928 '"' => out.push_str("\\\""),
1929 '\n' => out.push_str("\\n"),
1930 '\r' => out.push_str("\\r"),
1931 '\t' => out.push_str("\\t"),
1932 c if c.is_ascii() && (c as u8) >= 0x20 && (c as u8) < 0x7f => out.push(c),
1933 c => {
1934 for byte in c.to_string().as_bytes() {
1935 out.push_str(&format!("\\x{:02x}", byte));
1936 }
1937 }
1938 }
1939 }
1940 out
1941}
1942
1943fn generate_value_constants(
1957 output: &mut String,
1958 module: &Module,
1959) -> Result<(), Box<dyn std::error::Error>> {
1960 if module.values.is_empty() {
1961 return Ok(());
1962 }
1963
1964 writeln!(output, "/* Value constants */")?;
1965 writeln!(output)?;
1966
1967 let oid_registry = build_c_oid_registry(&module.values);
1968
1969 for va in &module.values {
1970 let c_name = to_screaming_snake_case(&va.name);
1971 match &va.value {
1972 Value::ObjectIdentifier(_) => {
1973 if let Some(arcs) = oid_registry.get(&va.name) {
1974 write!(output, "static const uint32_t {}[] = {{", c_name)?;
1975 for (i, n) in arcs.iter().enumerate() {
1976 if i > 0 {
1977 write!(output, ", ")?;
1978 }
1979 write!(output, "{}", n)?;
1980 }
1981 writeln!(output, "}};")?;
1982 writeln!(output, "#define {}_LEN {}", c_name, arcs.len())?;
1983 writeln!(output)?;
1984 } else {
1985 writeln!(
1986 output,
1987 "/* OID {} could not be fully resolved (unresolved named reference) */",
1988 va.name
1989 )?;
1990 writeln!(output)?;
1991 }
1992 }
1993 Value::Integer(n) => {
1994 writeln!(output, "#define {} ((int64_t){})", c_name, n)?;
1995 writeln!(output)?;
1996 }
1997 Value::Boolean(b) => {
1998 writeln!(
1999 output,
2000 "#define {} ({})",
2001 c_name,
2002 if *b { "true" } else { "false" }
2003 )?;
2004 writeln!(output)?;
2005 }
2006 Value::String(s) => {
2007 writeln!(output, "#define {} \"{}\"", c_name, escape_c_string(s))?;
2008 writeln!(output)?;
2009 }
2010 }
2011 }
2012
2013 Ok(())
2014}
2015
2016fn peel_type(ty: &Type) -> &Type {
2018 match ty {
2019 Type::Tagged { inner, .. }
2020 | Type::Constrained {
2021 base_type: inner, ..
2022 } => peel_type(inner),
2023 _ => ty,
2024 }
2025}
2026
2027fn is_pointer_c_type(ty: &Type) -> bool {
2029 matches!(
2030 peel_type(ty),
2031 Type::Integer(_, _)
2032 | Type::Enumerated(_)
2033 | Type::OctetString(_)
2034 | Type::ObjectIdentifier
2035 | Type::Utf8String(_)
2036 | Type::PrintableString(_)
2037 | Type::IA5String(_)
2038 | Type::UtcTime
2039 | Type::GeneralizedTime
2040 | Type::Any
2041 | Type::AnyDefinedBy(_)
2042 )
2043}
2044
2045pub(crate) fn get_c_type(ty: &Type) -> String {
2047 match ty {
2048 Type::Integer(_, _) => "SyntaInteger*".to_string(),
2049 Type::Enumerated(_) => "SyntaInteger*".to_string(),
2050 Type::Real => "double".to_string(),
2051 Type::Boolean => "bool".to_string(),
2052 Type::OctetString(_) => "SyntaOctetString*".to_string(),
2053 Type::BitString(_) => "SyntaBitString".to_string(),
2054 Type::ObjectIdentifier => "SyntaObjectIdentifier*".to_string(),
2055 Type::Null => "void".to_string(),
2056 Type::Utf8String(_)
2057 | Type::PrintableString(_)
2058 | Type::IA5String(_)
2059 | Type::TeletexString(_)
2060 | Type::UniversalString(_)
2061 | Type::BmpString(_)
2062 | Type::GeneralString(_)
2063 | Type::NumericString(_)
2064 | Type::VisibleString(_) => {
2065 "SyntaOctetString*".to_string() }
2067 Type::UtcTime | Type::GeneralizedTime => "SyntaOctetString*".to_string(), Type::Sequence(_) | Type::Set(_) => "struct /* complex type */".to_string(),
2069 Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
2070 format!("{}*", get_c_type(inner))
2071 }
2072 Type::Choice(_) => "union /* choice */".to_string(),
2073 Type::TypeRef(name) => to_pascal_case(name),
2074 Type::Tagged { inner, .. } => get_c_type(inner),
2075 Type::Constrained { base_type, .. } => get_c_type(base_type),
2076 Type::Any => "SyntaOctetString*".to_string(), Type::AnyDefinedBy(_) => "SyntaOctetString*".to_string(), Type::Class(_) => "void /* class */".to_string(), }
2080}
2081
2082#[cfg(test)]
2083mod tests {
2084 use super::*;
2085
2086 #[test]
2087 fn test_generate_simple_sequence() {
2088 let module = Module {
2089 name: "TestModule".to_string(),
2090 oid: None,
2091 values: vec![],
2092 tagging_mode: None,
2093 imports: vec![],
2094 exports: vec![],
2095 definitions: vec![Definition {
2096 name: "SimpleSeq".to_string(),
2097 ty: Type::Sequence(vec![
2098 SequenceField {
2099 name: "version".to_string(),
2100 ty: Type::Integer(None, vec![]),
2101 optional: false,
2102 default: None,
2103 },
2104 SequenceField {
2105 name: "serialNumber".to_string(),
2106 ty: Type::Integer(None, vec![]),
2107 optional: false,
2108 default: None,
2109 },
2110 ]),
2111 }],
2112 };
2113
2114 let result = generate_c(&module).unwrap();
2115 assert!(result.contains("typedef struct SimpleSeq"));
2116 assert!(result.contains("SyntaInteger* version;"));
2117 assert!(result.contains("SyntaInteger* serial_number;"));
2118 assert!(result.contains("simple_seq_decode"));
2119 assert!(result.contains("simple_seq_encode"));
2120 }
2121
2122 #[test]
2123 fn test_generate_choice() {
2124 let module = Module {
2125 name: "TestModule".to_string(),
2126 oid: None,
2127 values: vec![],
2128 tagging_mode: None,
2129 imports: vec![],
2130 exports: vec![],
2131 definitions: vec![Definition {
2132 name: "MyChoice".to_string(),
2133 ty: Type::Choice(vec![
2134 ChoiceVariant {
2135 name: "intVal".to_string(),
2136 ty: Type::Integer(None, vec![]),
2137 },
2138 ChoiceVariant {
2139 name: "boolVal".to_string(),
2140 ty: Type::Boolean,
2141 },
2142 ]),
2143 }],
2144 };
2145
2146 let result = generate_c(&module).unwrap();
2147 assert!(result.contains("typedef enum MyChoiceTag"));
2148 assert!(result.contains("typedef struct MyChoice"));
2149 assert!(result.contains("MyChoiceTag tag;"));
2150 assert!(result.contains("union {"));
2151 }
2152
2153 #[test]
2154 fn test_generate_helpers() {
2155 let module = Module {
2156 name: "TestModule".to_string(),
2157 oid: None,
2158 values: vec![],
2159 tagging_mode: None,
2160 imports: vec![],
2161 exports: vec![],
2162 definitions: vec![
2163 Definition {
2164 name: "SimpleSeq".to_string(),
2165 ty: Type::Sequence(vec![
2166 SequenceField {
2167 name: "version".to_string(),
2168 ty: Type::Integer(None, vec![]),
2169 optional: false,
2170 default: None,
2171 },
2172 SequenceField {
2173 name: "flag".to_string(),
2174 ty: Type::Boolean,
2175 optional: true,
2176 default: None,
2177 },
2178 ]),
2179 },
2180 Definition {
2181 name: "MyChoice".to_string(),
2182 ty: Type::Choice(vec![
2183 ChoiceVariant {
2184 name: "intVal".to_string(),
2185 ty: Type::Integer(None, vec![]),
2186 },
2187 ChoiceVariant {
2188 name: "boolVal".to_string(),
2189 ty: Type::Boolean,
2190 },
2191 ]),
2192 },
2193 ],
2194 };
2195
2196 let config = CCodeGenConfig {
2197 generate_helpers: true,
2198 ..Default::default()
2199 };
2200 let result = generate_c_with_config(&module, config).unwrap();
2201
2202 assert!(result.contains("#include <stdio.h>"));
2204
2205 assert!(result.contains("simple_seq_init"));
2207 assert!(result.contains("simple_seq_validate"));
2209 assert!(result.contains("SyntaErrorCode_NullPointer"));
2210 assert!(result.contains("value->version == NULL"));
2212 assert!(!result.contains("value->flag == NULL"));
2214 assert!(result.contains("simple_seq_print"));
2216 assert!(result.contains("FILE* stream"));
2217
2218 assert!(result.contains("my_choice_validate"));
2220 assert!(result.contains("MyChoiceTag_IntVal"));
2221 assert!(result.contains("SyntaErrorCode_InvalidEncoding"));
2222 assert!(result.contains("my_choice_print"));
2224 }
2225
2226 #[test]
2227 fn test_generate_named_integer() {
2228 let module = Module {
2229 name: "TestModule".to_string(),
2230 oid: None,
2231 values: vec![],
2232 tagging_mode: None,
2233 imports: vec![],
2234 exports: vec![],
2235 definitions: vec![Definition {
2236 name: "Protocol".to_string(),
2237 ty: Type::Integer(
2238 None,
2239 vec![
2240 NamedNumber {
2241 name: "tcp".to_string(),
2242 value: 6,
2243 },
2244 NamedNumber {
2245 name: "udp".to_string(),
2246 value: 17,
2247 },
2248 ],
2249 ),
2250 }],
2251 };
2252
2253 let result = generate_c(&module).unwrap();
2254 assert!(result.contains("typedef int64_t Protocol;"));
2256 assert!(result.contains("#define PROTOCOL_TCP"));
2258 assert!(result.contains("((int64_t)6)"));
2259 assert!(result.contains("#define PROTOCOL_UDP"));
2260 assert!(result.contains("((int64_t)17)"));
2261 assert!(!result.contains("enum Protocol {"));
2263 }
2264
2265 fn make_constrained_integer_module(
2270 type_name: &str,
2271 constraint: SubtypeConstraint,
2272 named_numbers: Vec<NamedNumber>,
2273 ) -> Module {
2274 Module {
2275 name: "TestModule".to_string(),
2276 oid: None,
2277 values: vec![],
2278 tagging_mode: None,
2279 imports: vec![],
2280 exports: vec![],
2281 definitions: vec![Definition {
2282 name: type_name.to_string(),
2283 ty: Type::Constrained {
2284 base_type: Box::new(Type::Integer(None, named_numbers)),
2285 constraint: Constraint {
2286 spec: ConstraintSpec::Subtype(constraint),
2287 exception: None,
2288 },
2289 },
2290 }],
2291 }
2292 }
2293
2294 #[test]
2295 fn test_constrained_integer_value_range() {
2296 let module = make_constrained_integer_module(
2298 "Int32",
2299 SubtypeConstraint::ValueRange {
2300 min: ConstraintValue::Integer(-2147483648),
2301 max: ConstraintValue::Integer(2147483647),
2302 },
2303 vec![],
2304 );
2305 let result = generate_c(&module).unwrap();
2306
2307 assert!(
2309 result.contains("typedef struct { int32_t value; } Int32;"),
2310 "missing struct typedef:\n{}",
2311 result
2312 );
2313 assert!(result.contains("INTEGER (-2147483648..2147483647)"));
2315 assert!(result.contains("int32_new(int32_t v, Int32* out)"));
2317 assert!(result.contains("v >= -2147483648LL") && result.contains("v <= 2147483647LL"));
2318 assert!(result.contains("int32_new_unchecked(int32_t v)"));
2320 assert!(result.contains("int32_get(const Int32* self)"));
2322 assert!(result.contains("int32_validate(const Int32* self)"));
2324 assert!(result.contains("int32_decode(SyntaDecoder*"));
2326 assert!(result.contains("int32_encode(SyntaEncoder*"));
2327 assert!(!result.contains("int32_free"));
2329 }
2330
2331 #[test]
2332 fn test_constrained_integer_single_value() {
2333 let module = make_constrained_integer_module(
2335 "PvNo",
2336 SubtypeConstraint::SingleValue(ConstraintValue::Integer(5)),
2337 vec![],
2338 );
2339 let result = generate_c(&module).unwrap();
2340
2341 assert!(result.contains("typedef struct { uint8_t value; } PvNo;"));
2343 assert!(result.contains("INTEGER (5)"));
2344 assert!(result.contains("v == 5LL"));
2345 assert!(result.contains("pv_no_new(uint8_t v, PvNo* out)"));
2346 assert!(result.contains("pv_no_validate(const PvNo* self)"));
2347 }
2348
2349 #[test]
2350 fn test_constrained_integer_min_max_unconstrained() {
2351 let module = make_constrained_integer_module(
2353 "UncheckedInt",
2354 SubtypeConstraint::ValueRange {
2355 min: ConstraintValue::Min,
2356 max: ConstraintValue::Max,
2357 },
2358 vec![],
2359 );
2360 let result = generate_c(&module).unwrap();
2361
2362 assert!(result.contains("typedef struct { int64_t value; } UncheckedInt;"));
2363 assert!(result.contains("return 1;"));
2365 assert!(result.contains("if (!(1)) return false;"));
2368 }
2369
2370 #[test]
2371 fn test_constrained_integer_half_open_range() {
2372 let module = make_constrained_integer_module(
2374 "NonNegInt",
2375 SubtypeConstraint::ValueRange {
2376 min: ConstraintValue::Integer(0),
2377 max: ConstraintValue::Max,
2378 },
2379 vec![],
2380 );
2381 let result = generate_c(&module).unwrap();
2382
2383 assert!(result.contains("v >= 0LL"));
2385 assert!(!result.contains("v <= INT64_MAX"));
2386 }
2387
2388 #[test]
2389 fn test_constrained_integer_union() {
2390 let module = make_constrained_integer_module(
2392 "SmallOrLarge",
2393 SubtypeConstraint::Union(vec![
2394 SubtypeConstraint::ValueRange {
2395 min: ConstraintValue::Integer(0),
2396 max: ConstraintValue::Integer(10),
2397 },
2398 SubtypeConstraint::ValueRange {
2399 min: ConstraintValue::Integer(100),
2400 max: ConstraintValue::Integer(200),
2401 },
2402 ]),
2403 vec![],
2404 );
2405 let result = generate_c(&module).unwrap();
2406
2407 assert!(result.contains("typedef struct { int64_t value; } SmallOrLarge;"));
2408 assert!(result.contains("||"));
2410 assert!(result.contains("v >= 0LL") && result.contains("v <= 10LL"));
2411 assert!(result.contains("v >= 100LL") && result.contains("v <= 200LL"));
2412 }
2413
2414 #[test]
2415 fn test_constrained_integer_complement() {
2416 let module = make_constrained_integer_module(
2418 "NotZero",
2419 SubtypeConstraint::Complement(Box::new(SubtypeConstraint::SingleValue(
2420 ConstraintValue::Integer(0),
2421 ))),
2422 vec![],
2423 );
2424 let result = generate_c(&module).unwrap();
2425
2426 assert!(result.contains("typedef struct { int64_t value; } NotZero;"));
2427 assert!(result.contains("!(v == 0LL)"));
2428 }
2429
2430 #[test]
2431 fn test_constrained_integer_with_named_numbers() {
2432 let module = make_constrained_integer_module(
2434 "MsgType",
2435 SubtypeConstraint::ValueRange {
2436 min: ConstraintValue::Integer(0),
2437 max: ConstraintValue::Integer(30),
2438 },
2439 vec![
2440 NamedNumber {
2441 name: "asReq".to_string(),
2442 value: 10,
2443 },
2444 NamedNumber {
2445 name: "asRep".to_string(),
2446 value: 11,
2447 },
2448 ],
2449 );
2450 let result = generate_c(&module).unwrap();
2451
2452 assert!(result.contains("typedef struct { uint8_t value; } MsgType;"));
2454 assert!(result.contains("#define MsgType_AS_REQ ((uint8_t)10)"));
2456 assert!(result.contains("#define MsgType_AS_REP ((uint8_t)11)"));
2457 assert!(result.contains("msg_type_new(uint8_t v, MsgType* out)"));
2459 assert!(result.contains("msg_type_validate(const MsgType* self)"));
2460 }
2461
2462 #[test]
2463 fn test_format_c_constraint_display() {
2464 assert_eq!(
2465 format_c_constraint_display(&SubtypeConstraint::ValueRange {
2466 min: ConstraintValue::Integer(-128),
2467 max: ConstraintValue::Integer(127),
2468 }),
2469 "-128..127"
2470 );
2471 assert_eq!(
2472 format_c_constraint_display(&SubtypeConstraint::SingleValue(ConstraintValue::Integer(
2473 42
2474 ))),
2475 "42"
2476 );
2477 assert_eq!(
2478 format_c_constraint_display(&SubtypeConstraint::ValueRange {
2479 min: ConstraintValue::Min,
2480 max: ConstraintValue::Max,
2481 }),
2482 "MIN..MAX"
2483 );
2484 }
2485
2486 #[test]
2487 fn test_generate_c_constraint_check() {
2488 assert_eq!(
2490 generate_c_constraint_check(
2491 "val",
2492 &SubtypeConstraint::ValueRange {
2493 min: ConstraintValue::Integer(0),
2494 max: ConstraintValue::Integer(100),
2495 },
2496 "int64_t",
2497 ),
2498 "(val >= 0LL && val <= 100LL)"
2499 );
2500 assert_eq!(
2502 generate_c_constraint_check(
2503 "val",
2504 &SubtypeConstraint::ValueRange {
2505 min: ConstraintValue::Integer(0),
2506 max: ConstraintValue::Integer(100),
2507 },
2508 "uint8_t",
2509 ),
2510 "(val <= 100LL)"
2511 );
2512 assert_eq!(
2514 generate_c_constraint_check(
2515 "val",
2516 &SubtypeConstraint::SingleValue(ConstraintValue::Integer(5)),
2517 "int64_t",
2518 ),
2519 "val == 5LL"
2520 );
2521 assert_eq!(
2523 generate_c_constraint_check(
2524 "val",
2525 &SubtypeConstraint::ValueRange {
2526 min: ConstraintValue::Min,
2527 max: ConstraintValue::Max,
2528 },
2529 "int64_t",
2530 ),
2531 "1"
2532 );
2533 assert_eq!(
2535 generate_c_constraint_check(
2536 "val",
2537 &SubtypeConstraint::ValueRange {
2538 min: ConstraintValue::Integer(0),
2539 max: ConstraintValue::Max,
2540 },
2541 "int64_t",
2542 ),
2543 "(val >= 0LL)"
2544 );
2545 assert_eq!(
2547 generate_c_constraint_check(
2548 "val",
2549 &SubtypeConstraint::Complement(Box::new(SubtypeConstraint::SingleValue(
2550 ConstraintValue::Integer(0)
2551 ))),
2552 "int64_t",
2553 ),
2554 "!(val == 0LL)"
2555 );
2556 }
2557
2558 fn make_constrained_string_module(
2563 type_name: &str,
2564 base_ty: Type,
2565 constraint: SubtypeConstraint,
2566 ) -> Module {
2567 Module {
2568 name: "TestModule".to_string(),
2569 oid: None,
2570 values: vec![],
2571 tagging_mode: None,
2572 imports: vec![],
2573 exports: vec![],
2574 definitions: vec![Definition {
2575 name: type_name.to_string(),
2576 ty: Type::Constrained {
2577 base_type: Box::new(base_ty),
2578 constraint: Constraint {
2579 spec: ConstraintSpec::Subtype(constraint),
2580 exception: None,
2581 },
2582 },
2583 }],
2584 }
2585 }
2586
2587 #[test]
2588 fn test_constrained_string_size_only() {
2589 let module = make_constrained_string_module(
2591 "Realm",
2592 Type::IA5String(None),
2593 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2594 min: ConstraintValue::Integer(1),
2595 max: ConstraintValue::Max,
2596 })),
2597 );
2598 let result = generate_c(&module).unwrap();
2599
2600 assert!(
2602 result.contains("typedef struct { SyntaByteArray value; } Realm;"),
2603 "missing struct typedef:\n{}",
2604 result
2605 );
2606 assert!(result.contains("/* IA5String (SIZE (1..MAX)) */"));
2608 assert!(result.contains("realm_new(SyntaByteArray value, Realm* out)"));
2610 assert!(result.contains("uint32_t _len = value.len;"));
2611 assert!(result.contains("_len >= 1U"));
2612 assert!(result.contains("realm_new_unchecked(SyntaByteArray value)"));
2614 assert!(result.contains("realm_get(const Realm* self)"));
2616 assert!(result.contains("r.owned = 0"));
2617 assert!(result.contains("realm_validate(const Realm* self)"));
2619 assert!(result.contains("uint32_t _len = self->value.len;"));
2620 assert!(result.contains("realm_free(Realm* self)"));
2622 assert!(result.contains("synta_byte_array_free"));
2623 assert!(result.contains("realm_decode(SyntaDecoder*"));
2625 assert!(result.contains("realm_encode(SyntaEncoder*"));
2626 }
2627
2628 #[test]
2629 fn test_constrained_string_size_exact() {
2630 let module = make_constrained_string_module(
2632 "FixedTag",
2633 Type::OctetString(None),
2634 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::SingleValue(
2635 ConstraintValue::Integer(4),
2636 ))),
2637 );
2638 let result = generate_c(&module).unwrap();
2639
2640 assert!(result.contains("/* OCTET STRING (SIZE (4)) */"));
2641 assert!(result.contains("typedef struct { SyntaByteArray value; } FixedTag;"));
2642 assert!(result.contains("_len == 4U"));
2643 assert!(result.contains("fixed_tag_new(SyntaByteArray value, FixedTag* out)"));
2644 assert!(result.contains("fixed_tag_validate(const FixedTag* self)"));
2645 assert!(result.contains("fixed_tag_free(FixedTag* self)"));
2646 }
2647
2648 #[test]
2649 fn test_constrained_string_size_zero_min() {
2650 let module = make_constrained_string_module(
2652 "OptStr",
2653 Type::IA5String(None),
2654 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2655 min: ConstraintValue::Integer(0),
2656 max: ConstraintValue::Integer(255),
2657 })),
2658 );
2659 let result = generate_c(&module).unwrap();
2660
2661 assert!(result.contains("_len <= 255U"));
2663 assert!(!result.contains("_len >= 0U"));
2665 }
2666
2667 #[test]
2668 fn test_constrained_string_min_max_size() {
2669 let module = make_constrained_string_module(
2671 "AnyStr",
2672 Type::IA5String(None),
2673 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2674 min: ConstraintValue::Min,
2675 max: ConstraintValue::Max,
2676 })),
2677 );
2678 let result = generate_c(&module).unwrap();
2679
2680 assert!(result.contains("if (!(1)) return false;"));
2682 }
2683
2684 #[test]
2685 fn test_constrained_string_alphabet_only() {
2686 let module = make_constrained_string_module(
2688 "DigitStr",
2689 Type::IA5String(None),
2690 SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: '0', max: '9' }]),
2691 );
2692 let result = generate_c(&module).unwrap();
2693
2694 assert!(result.contains("/* IA5String (FROM (\"0\"..\"9\")) */"));
2695 assert!(result.contains("const unsigned char *_ap ="));
2697 assert!(result.contains("unsigned char _c = _ap[_i]"));
2698 assert!(result.contains("_ok = (_c >= '0' && _c <= '9')"));
2699 assert!(result.contains("if (!_ok) return false;"));
2700 }
2701
2702 #[test]
2703 fn test_constrained_string_alphabet_single_char() {
2704 let module = make_constrained_string_module(
2706 "SingleChar",
2707 Type::IA5String(None),
2708 SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: 'x', max: 'x' }]),
2709 );
2710 let result = generate_c(&module).unwrap();
2711 assert!(result.contains("_ok = _c == 'x'"));
2713 }
2714
2715 #[test]
2716 fn test_constrained_string_size_and_alphabet() {
2717 let module = make_constrained_string_module(
2719 "VisStr",
2720 Type::PrintableString(None),
2721 SubtypeConstraint::Intersection(vec![
2722 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2723 min: ConstraintValue::Integer(1),
2724 max: ConstraintValue::Integer(64),
2725 })),
2726 SubtypeConstraint::PermittedAlphabet(vec![
2727 CharRange { min: 'A', max: 'Z' },
2728 CharRange { min: 'a', max: 'z' },
2729 ]),
2730 ]),
2731 );
2732 let result = generate_c(&module).unwrap();
2733
2734 assert!(result.contains("/* PrintableString"));
2735 assert!(result.contains("_len >= 1U") && result.contains("_len <= 64U"));
2737 assert!(result.contains("(_c >= 'A' && _c <= 'Z') || (_c >= 'a' && _c <= 'z')"));
2739 assert!(result.contains("uint32_t _len = self->value.len;"));
2741 }
2742
2743 #[test]
2744 fn test_constrained_string_pattern_placeholder() {
2745 let module = make_constrained_string_module(
2747 "PatStr",
2748 Type::IA5String(None),
2749 SubtypeConstraint::Pattern("[0-9]+".to_string()),
2750 );
2751 let result = generate_c(&module).unwrap();
2752
2753 assert!(result.contains("PATTERN constraint \"[0-9]+\" not enforced at runtime"));
2754 assert!(result.contains("typedef struct { SyntaByteArray value; } PatStr;"));
2756 assert!(result.contains("pat_str_new(SyntaByteArray value, PatStr* out)"));
2757 }
2758
2759 #[test]
2760 fn test_constrained_utf8string() {
2761 let module = make_constrained_string_module(
2763 "Label",
2764 Type::Utf8String(None),
2765 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2766 min: ConstraintValue::Integer(1),
2767 max: ConstraintValue::Integer(255),
2768 })),
2769 );
2770 let result = generate_c(&module).unwrap();
2771
2772 assert!(result.contains("/* UTF8String (SIZE (1..255)) */"));
2773 assert!(result.contains("typedef struct { SyntaByteArray value; } Label;"));
2774 assert!(result.contains("label_new(SyntaByteArray value, Label* out)"));
2775 assert!(result.contains("label_free(Label* self)"));
2776 }
2777
2778 #[test]
2779 fn test_generate_c_length_check() {
2780 assert_eq!(
2782 generate_c_length_check(
2783 "_len",
2784 &SubtypeConstraint::SingleValue(ConstraintValue::Integer(4))
2785 ),
2786 "_len == 4U"
2787 );
2788 assert_eq!(
2790 generate_c_length_check(
2791 "_len",
2792 &SubtypeConstraint::ValueRange {
2793 min: ConstraintValue::Integer(1),
2794 max: ConstraintValue::Max,
2795 }
2796 ),
2797 "(_len >= 1U)"
2798 );
2799 assert_eq!(
2801 generate_c_length_check(
2802 "_len",
2803 &SubtypeConstraint::ValueRange {
2804 min: ConstraintValue::Integer(0),
2805 max: ConstraintValue::Integer(256),
2806 }
2807 ),
2808 "(_len <= 256U)"
2809 );
2810 assert_eq!(
2812 generate_c_length_check(
2813 "_len",
2814 &SubtypeConstraint::ValueRange {
2815 min: ConstraintValue::Min,
2816 max: ConstraintValue::Max,
2817 }
2818 ),
2819 "1"
2820 );
2821 }
2822
2823 #[test]
2824 fn test_format_c_char_literal() {
2825 assert_eq!(format_c_char_literal('A'), "'A'");
2826 assert_eq!(format_c_char_literal('0'), "'0'");
2827 assert_eq!(format_c_char_literal('\''), "'\\''");
2828 assert_eq!(format_c_char_literal('\\'), "'\\\\'");
2829 assert_eq!(format_c_char_literal('\x01'), "'\\x01'");
2830 }
2831
2832 fn make_named_bit_module(name: &str, bits: Vec<NamedNumber>) -> Module {
2837 Module {
2838 name: "TestModule".to_string(),
2839 oid: None,
2840 values: vec![],
2841 tagging_mode: None,
2842 imports: vec![],
2843 exports: vec![],
2844 definitions: vec![Definition {
2845 name: name.to_string(),
2846 ty: Type::Constrained {
2847 base_type: Box::new(Type::BitString(None)),
2848 constraint: Constraint {
2849 spec: ConstraintSpec::Subtype(SubtypeConstraint::NamedBitList(bits)),
2850 exception: None,
2851 },
2852 },
2853 }],
2854 }
2855 }
2856
2857 #[test]
2858 fn test_named_bit_string_typedef_and_defines() {
2859 let module = make_named_bit_module(
2861 "TicketFlags",
2862 vec![
2863 NamedNumber {
2864 name: "reserved".to_string(),
2865 value: 0,
2866 },
2867 NamedNumber {
2868 name: "forwardable".to_string(),
2869 value: 1,
2870 },
2871 NamedNumber {
2872 name: "proxiable".to_string(),
2873 value: 3,
2874 },
2875 ],
2876 );
2877 let result = generate_c(&module).unwrap();
2878
2879 assert!(
2881 result.contains("typedef SyntaBitString TicketFlags;"),
2882 "typedef present"
2883 );
2884 assert!(
2886 result.contains("TICKET_FLAGS_RESERVED_BIT"),
2887 "reserved bit define"
2888 );
2889 assert!(
2890 result.contains("TICKET_FLAGS_FORWARDABLE_BIT"),
2891 "forwardable bit define"
2892 );
2893 assert!(
2894 result.contains("TICKET_FLAGS_PROXIABLE_BIT"),
2895 "proxiable bit define"
2896 );
2897 assert!(
2899 result.contains("TICKET_FLAGS_RESERVED_BIT") && result.contains(" 0"),
2900 "value 0 present"
2901 );
2902 assert!(result.contains(" 1"), "value 1 present");
2903 assert!(result.contains(" 3"), "value 3 present");
2904 assert!(!result.contains("IS_SET"), "no IS_SET without helpers");
2906 }
2907
2908 #[test]
2909 fn test_named_bit_string_hyphenated_name() {
2910 let module = make_named_bit_module(
2912 "kdc-options",
2913 vec![
2914 NamedNumber {
2915 name: "reserved".to_string(),
2916 value: 0,
2917 },
2918 NamedNumber {
2919 name: "forwardable".to_string(),
2920 value: 1,
2921 },
2922 ],
2923 );
2924 let result = generate_c(&module).unwrap();
2925 assert!(
2927 result.contains("typedef SyntaBitString KdcOptions;"),
2928 "typedef with PascalCase"
2929 );
2930 assert!(
2931 result.contains("KDC_OPTIONS_RESERVED_BIT"),
2932 "hyphenated prefix uses underscore"
2933 );
2934 assert!(
2935 result.contains("KDC_OPTIONS_FORWARDABLE_BIT"),
2936 "forwardable bit define"
2937 );
2938 }
2939
2940 #[test]
2941 fn test_named_bit_string_camel_case_bit_name() {
2942 let module = make_named_bit_module(
2944 "KeyUsage",
2945 vec![
2946 NamedNumber {
2947 name: "digitalSignature".to_string(),
2948 value: 0,
2949 },
2950 NamedNumber {
2951 name: "nonRepudiation".to_string(),
2952 value: 1,
2953 },
2954 ],
2955 );
2956 let result = generate_c(&module).unwrap();
2957 assert!(
2959 result.contains("KEY_USAGE_DIGITAL_SIGNATURE_BIT"),
2960 "camelCase bit → SCREAMING_SNAKE"
2961 );
2962 assert!(
2963 result.contains("KEY_USAGE_NON_REPUDIATION_BIT"),
2964 "nonRepudiation bit"
2965 );
2966 }
2967
2968 #[test]
2969 fn test_named_bit_string_with_helpers() {
2970 let module = make_named_bit_module(
2971 "TicketFlags",
2972 vec![NamedNumber {
2973 name: "forwardable".to_string(),
2974 value: 1,
2975 }],
2976 );
2977 let config = CCodeGenConfig {
2978 generate_helpers: true,
2979 ..Default::default()
2980 };
2981 let result = generate_c_with_config(&module, config).unwrap();
2982 assert!(
2984 result.contains("TICKET_FLAGS_IS_SET(bs, bit)"),
2985 "IS_SET helper"
2986 );
2987 assert!(result.contains("TICKET_FLAGS_SET(bs, bit)"), "SET helper");
2988 assert!(
2989 result.contains("TICKET_FLAGS_CLEAR(bs, bit)"),
2990 "CLEAR helper"
2991 );
2992 assert!(result.contains("synta_bitstring_is_set"), "is_set API call");
2994 assert!(result.contains("synta_bitstring_set"), "set API call");
2995 assert!(result.contains("synta_bitstring_clear"), "clear API call");
2996 }
2997
2998 #[test]
2999 fn test_named_bit_string_empty_list() {
3000 let module = make_named_bit_module("EmptyFlags", vec![]);
3002 let result = generate_c(&module).unwrap();
3003 assert!(
3004 result.contains("typedef SyntaBitString EmptyFlags;"),
3005 "typedef present"
3006 );
3007 assert!(
3008 !result.contains("EMPTY_FLAGS_"),
3009 "no defines for empty list"
3010 );
3011 }
3012
3013 fn make_named_bit_with_size_module(name: &str, bits: Vec<NamedNumber>) -> Module {
3023 Module {
3025 name: "TestModule".to_string(),
3026 oid: None,
3027 values: vec![],
3028 tagging_mode: None,
3029 imports: vec![],
3030 exports: vec![],
3031 definitions: vec![Definition {
3032 name: name.to_string(),
3033 ty: Type::Constrained {
3034 base_type: Box::new(Type::BitString(None)),
3035 constraint: Constraint {
3036 spec: ConstraintSpec::Subtype(SubtypeConstraint::Intersection(vec![
3037 SubtypeConstraint::NamedBitList(bits),
3038 SubtypeConstraint::SizeConstraint(Box::new(
3039 SubtypeConstraint::ValueRange {
3040 min: ConstraintValue::Integer(32),
3041 max: ConstraintValue::Max,
3042 },
3043 )),
3044 ])),
3045 exception: None,
3046 },
3047 },
3048 }],
3049 }
3050 }
3051
3052 #[test]
3053 fn test_named_bit_string_with_size_emits_typedef() {
3054 let module = make_named_bit_with_size_module(
3056 "TicketFlags",
3057 vec![
3058 NamedNumber {
3059 name: "forwardable".to_string(),
3060 value: 1,
3061 },
3062 NamedNumber {
3063 name: "proxiable".to_string(),
3064 value: 3,
3065 },
3066 ],
3067 );
3068 let result = generate_c(&module).unwrap();
3069 assert!(
3070 result.contains("typedef SyntaBitString TicketFlags;"),
3071 "combined form must still typedef SyntaBitString; got:\n{}",
3072 result
3073 );
3074 assert!(
3075 result.contains("#define TICKET_FLAGS_FORWARDABLE_BIT"),
3076 "FORWARDABLE_BIT define must appear; got:\n{}",
3077 result
3078 );
3079 assert!(
3080 result.contains("#define TICKET_FLAGS_PROXIABLE_BIT"),
3081 "PROXIABLE_BIT define must appear; got:\n{}",
3082 result
3083 );
3084 assert!(
3086 !result.contains("typedef struct { SyntaByteArray value; } TicketFlags;"),
3087 "combined form must not fall through to constrained-string struct; got:\n{}",
3088 result
3089 );
3090 }
3091
3092 #[test]
3093 fn test_named_bit_string_with_size_helpers() {
3094 let module = make_named_bit_with_size_module(
3096 "KdcOptions",
3097 vec![NamedNumber {
3098 name: "forwardable".to_string(),
3099 value: 1,
3100 }],
3101 );
3102 let config = CCodeGenConfig {
3103 generate_helpers: true,
3104 ..Default::default()
3105 };
3106 let result = generate_c_with_config(&module, config).unwrap();
3107 assert!(
3108 result.contains("KDC_OPTIONS_IS_SET(bs, bit)"),
3109 "IS_SET helper must appear; got:\n{}",
3110 result
3111 );
3112 }
3113
3114 fn make_default_module(fields: Vec<SequenceField>) -> Module {
3119 Module {
3120 name: "TestModule".to_string(),
3121 oid: None,
3122 values: vec![],
3123 tagging_mode: None,
3124 imports: vec![],
3125 exports: vec![],
3126 definitions: vec![Definition {
3127 name: "Config".to_string(),
3128 ty: Type::Sequence(fields),
3129 }],
3130 }
3131 }
3132
3133 #[test]
3134 fn test_sequence_default_comment_in_struct() {
3135 let module = make_default_module(vec![
3137 SequenceField {
3138 name: "port".to_string(),
3139 ty: Type::Integer(None, vec![]),
3140 optional: false,
3141 default: Some("8080".to_string()),
3142 },
3143 SequenceField {
3144 name: "enabled".to_string(),
3145 ty: Type::Boolean,
3146 optional: false,
3147 default: Some("TRUE".to_string()),
3148 },
3149 ]);
3150 let result = generate_c(&module).unwrap();
3151 assert!(
3152 result.contains("SyntaInteger* port; /* DEFAULT 8080 */"),
3153 "integer default comment"
3154 );
3155 assert!(
3156 result.contains("bool enabled; /* DEFAULT TRUE */"),
3157 "boolean default comment"
3158 );
3159 }
3160
3161 #[test]
3162 fn test_sequence_default_prototype_generated() {
3163 let module = make_default_module(vec![SequenceField {
3165 name: "port".to_string(),
3166 ty: Type::Integer(None, vec![]),
3167 optional: false,
3168 default: Some("8080".to_string()),
3169 }]);
3170 let result = generate_c(&module).unwrap();
3171 assert!(
3172 result.contains("Config config_default(void);"),
3173 "default prototype generated"
3174 );
3175 }
3176
3177 #[test]
3178 fn test_sequence_no_default_prototype_for_required_field() {
3179 let module = make_default_module(vec![SequenceField {
3181 name: "name".to_string(),
3182 ty: Type::Integer(None, vec![]),
3183 optional: false,
3184 default: None,
3185 }]);
3186 let result = generate_c(&module).unwrap();
3187 assert!(
3188 !result.contains("config_default(void)"),
3189 "no prototype for required-only sequence"
3190 );
3191 }
3192
3193 fn make_tagged_seq_module(
3198 field_name: &str,
3199 class: TagClass,
3200 number: u32,
3201 tagging: Tagging,
3202 inner: Type,
3203 ) -> Module {
3204 Module {
3205 name: "TestModule".to_string(),
3206 oid: None,
3207 values: vec![],
3208 tagging_mode: None,
3209 imports: vec![],
3210 exports: vec![],
3211 definitions: vec![Definition {
3212 name: "Msg".to_string(),
3213 ty: Type::Sequence(vec![SequenceField {
3214 name: field_name.to_string(),
3215 ty: Type::Tagged {
3216 tag: TagInfo {
3217 class,
3218 number,
3219 tagging,
3220 },
3221 inner: Box::new(inner),
3222 },
3223 optional: false,
3224 default: None,
3225 }]),
3226 }],
3227 }
3228 }
3229
3230 #[test]
3231 fn test_explicit_tag_annotation_in_struct() {
3232 let module = make_tagged_seq_module(
3234 "id",
3235 TagClass::ContextSpecific,
3236 0,
3237 Tagging::Explicit,
3238 Type::Integer(None, vec![]),
3239 );
3240 let result = generate_c(&module).unwrap();
3241 assert!(
3242 result.contains("SyntaInteger* id; /* [0] EXPLICIT */"),
3243 "explicit tag comment missing; got:\n{}",
3244 result
3245 );
3246 }
3247
3248 #[test]
3249 fn test_implicit_tag_annotation_in_struct() {
3250 let module = make_tagged_seq_module(
3252 "data",
3253 TagClass::ContextSpecific,
3254 1,
3255 Tagging::Implicit,
3256 Type::OctetString(None),
3257 );
3258 let result = generate_c(&module).unwrap();
3259 assert!(
3260 result.contains("SyntaOctetString* data; /* [1] IMPLICIT */"),
3261 "implicit tag comment missing; got:\n{}",
3262 result
3263 );
3264 }
3265
3266 #[test]
3267 fn test_application_tag_annotation_in_struct() {
3268 let module = make_tagged_seq_module(
3270 "val",
3271 TagClass::Application,
3272 2,
3273 Tagging::Implicit,
3274 Type::Integer(None, vec![]),
3275 );
3276 let result = generate_c(&module).unwrap();
3277 assert!(
3278 result.contains("SyntaInteger* val; /* [APPLICATION 2] IMPLICIT */"),
3279 "APPLICATION tag comment missing; got:\n{}",
3280 result
3281 );
3282 }
3283
3284 fn make_values_module(values: Vec<crate::ast::ValueAssignment>) -> Module {
3289 Module {
3290 name: "TestModule".to_string(),
3291 oid: None,
3292 values,
3293 tagging_mode: None,
3294 imports: vec![],
3295 exports: vec![],
3296 definitions: vec![],
3297 }
3298 }
3299
3300 #[test]
3301 fn test_oid_value_constant_emitted() {
3302 let module = make_values_module(vec![crate::ast::ValueAssignment {
3304 name: "id-ori".to_string(),
3305 ty: Type::ObjectIdentifier,
3306 value: Value::ObjectIdentifier(vec![
3307 OidComponent::Number(1),
3308 OidComponent::Number(2),
3309 OidComponent::Number(840),
3310 OidComponent::Number(113549),
3311 OidComponent::Number(1),
3312 OidComponent::Number(9),
3313 OidComponent::Number(16),
3314 OidComponent::Number(13),
3315 ]),
3316 }]);
3317 let result = generate_c(&module).unwrap();
3318 assert!(
3319 result.contains("static const uint32_t ID_ORI[] = {1, 2, 840, 113549, 1, 9, 16, 13};"),
3320 "OID array missing:\n{}",
3321 result
3322 );
3323 assert!(
3324 result.contains("#define ID_ORI_LEN 8"),
3325 "_LEN define missing:\n{}",
3326 result
3327 );
3328 }
3329
3330 #[test]
3331 fn test_oid_named_reference_resolved() {
3332 let module = make_values_module(vec![
3335 crate::ast::ValueAssignment {
3336 name: "id-ori".to_string(),
3337 ty: Type::ObjectIdentifier,
3338 value: Value::ObjectIdentifier(vec![
3339 OidComponent::Number(1),
3340 OidComponent::Number(2),
3341 OidComponent::Number(840),
3342 OidComponent::Number(113549),
3343 OidComponent::Number(1),
3344 OidComponent::Number(9),
3345 OidComponent::Number(16),
3346 OidComponent::Number(13),
3347 ]),
3348 },
3349 crate::ast::ValueAssignment {
3350 name: "id-ori-kem".to_string(),
3351 ty: Type::ObjectIdentifier,
3352 value: Value::ObjectIdentifier(vec![
3353 OidComponent::NamedRef("id-ori".to_string()),
3354 OidComponent::Number(3),
3355 ]),
3356 },
3357 ]);
3358 let result = generate_c(&module).unwrap();
3359 assert!(
3360 result.contains(
3361 "static const uint32_t ID_ORI_KEM[] = {1, 2, 840, 113549, 1, 9, 16, 13, 3};"
3362 ),
3363 "resolved child OID missing:\n{}",
3364 result
3365 );
3366 assert!(
3367 result.contains("#define ID_ORI_KEM_LEN 9"),
3368 "_LEN for child OID missing:\n{}",
3369 result
3370 );
3371 }
3372
3373 #[test]
3374 fn test_oid_unresolvable_named_ref_emits_comment() {
3375 let module = make_values_module(vec![crate::ast::ValueAssignment {
3377 name: "my-oid".to_string(),
3378 ty: Type::ObjectIdentifier,
3379 value: Value::ObjectIdentifier(vec![
3380 OidComponent::NamedRef("undefined-base".to_string()),
3381 OidComponent::Number(1),
3382 ]),
3383 }]);
3384 let result = generate_c(&module).unwrap();
3385 assert!(
3386 result.contains("could not be fully resolved"),
3387 "unresolvable OID should produce a comment:\n{}",
3388 result
3389 );
3390 assert!(
3392 !result.contains("static const uint32_t MY_OID[] ="),
3393 "broken array must not be emitted:\n{}",
3394 result
3395 );
3396 }
3397
3398 #[test]
3399 fn test_integer_value_constant() {
3400 let module = make_values_module(vec![crate::ast::ValueAssignment {
3401 name: "max-count".to_string(),
3402 ty: Type::Integer(None, vec![]),
3403 value: Value::Integer(256),
3404 }]);
3405 let result = generate_c(&module).unwrap();
3406 assert!(
3407 result.contains("#define MAX_COUNT ((int64_t)256)"),
3408 "integer constant missing:\n{}",
3409 result
3410 );
3411 }
3412
3413 #[test]
3414 fn test_boolean_value_constant() {
3415 let module = make_values_module(vec![
3416 crate::ast::ValueAssignment {
3417 name: "flag-true".to_string(),
3418 ty: Type::Boolean,
3419 value: Value::Boolean(true),
3420 },
3421 crate::ast::ValueAssignment {
3422 name: "flag-false".to_string(),
3423 ty: Type::Boolean,
3424 value: Value::Boolean(false),
3425 },
3426 ]);
3427 let result = generate_c(&module).unwrap();
3428 assert!(
3429 result.contains("#define FLAG_TRUE (true)"),
3430 "true constant missing:\n{}",
3431 result
3432 );
3433 assert!(
3434 result.contains("#define FLAG_FALSE (false)"),
3435 "false constant missing:\n{}",
3436 result
3437 );
3438 }
3439
3440 #[test]
3441 fn test_string_value_constant() {
3442 let module = make_values_module(vec![crate::ast::ValueAssignment {
3443 name: "default-realm".to_string(),
3444 ty: Type::Utf8String(None),
3445 value: Value::String("EXAMPLE.COM".to_string()),
3446 }]);
3447 let result = generate_c(&module).unwrap();
3448 assert!(
3449 result.contains("#define DEFAULT_REALM \"EXAMPLE.COM\""),
3450 "string constant missing:\n{}",
3451 result
3452 );
3453 }
3454
3455 #[test]
3456 fn test_string_escape_in_constant() {
3457 let module = make_values_module(vec![crate::ast::ValueAssignment {
3458 name: "path".to_string(),
3459 ty: Type::Utf8String(None),
3460 value: Value::String("C:\\foo\\bar".to_string()),
3461 }]);
3462 let result = generate_c(&module).unwrap();
3463 assert!(
3464 result.contains("#define PATH \"C:\\\\foo\\\\bar\""),
3465 "backslash not escaped:\n{}",
3466 result
3467 );
3468 }
3469
3470 #[test]
3471 fn test_value_constants_section_header() {
3472 let module = make_values_module(vec![crate::ast::ValueAssignment {
3474 name: "x".to_string(),
3475 ty: Type::Integer(None, vec![]),
3476 value: Value::Integer(1),
3477 }]);
3478 let result = generate_c(&module).unwrap();
3479 assert!(
3480 result.contains("/* Value constants */"),
3481 "section comment missing"
3482 );
3483 }
3484
3485 #[test]
3486 fn test_no_value_constants_section_when_empty() {
3487 let module = make_values_module(vec![]);
3489 let result = generate_c(&module).unwrap();
3490 assert!(
3491 !result.contains("/* Value constants */"),
3492 "spurious section comment"
3493 );
3494 }
3495
3496 fn make_import_module(imports: Vec<Import>) -> Module {
3501 Module {
3502 name: "TestModule".to_string(),
3503 oid: None,
3504 values: vec![],
3505 tagging_mode: None,
3506 imports,
3507 exports: vec![],
3508 definitions: vec![],
3509 }
3510 }
3511
3512 #[test]
3513 fn test_import_generates_include() {
3514 let module = make_import_module(vec![Import {
3516 symbols: vec!["AlgorithmIdentifier".to_string()],
3517 module_name: "AlgorithmInformation-2009".to_string(),
3518 }]);
3519 let result = generate_c(&module).unwrap();
3520 assert!(
3521 result.contains("#include \"algorithm_information_2009.h\""),
3522 "import include missing:\n{}",
3523 result
3524 );
3525 assert!(
3526 result.contains("/* Imported module headers */"),
3527 "import section comment missing:\n{}",
3528 result
3529 );
3530 }
3531
3532 #[test]
3533 fn test_multiple_imports_generate_includes() {
3534 let module = make_import_module(vec![
3535 Import {
3536 symbols: vec!["Name".to_string()],
3537 module_name: "PKIX1Explicit88".to_string(),
3538 },
3539 Import {
3540 symbols: vec!["AlgorithmIdentifier".to_string()],
3541 module_name: "AlgorithmInformation-2009".to_string(),
3542 },
3543 ]);
3544 let result = generate_c(&module).unwrap();
3545 assert!(
3546 result.contains("#include \"pkix1_explicit88.h\""),
3547 "first import missing:\n{}",
3548 result
3549 );
3550 assert!(
3551 result.contains("#include \"algorithm_information_2009.h\""),
3552 "second import missing:\n{}",
3553 result
3554 );
3555 }
3556
3557 #[test]
3558 fn test_no_imports_no_import_section() {
3559 let module = make_import_module(vec![]);
3560 let result = generate_c(&module).unwrap();
3561 assert!(
3562 !result.contains("/* Imported module headers */"),
3563 "spurious import section:\n{}",
3564 result
3565 );
3566 }
3567
3568 #[test]
3573 fn test_choice_inline_sequence_emits_void_pointer() {
3574 let module = Module {
3577 name: "TestModule".to_string(),
3578 oid: None,
3579 values: vec![],
3580 tagging_mode: None,
3581 imports: vec![],
3582 exports: vec![],
3583 definitions: vec![Definition {
3584 name: "MyChoice".to_string(),
3585 ty: Type::Choice(vec![
3586 ChoiceVariant {
3587 name: "seqVal".to_string(),
3588 ty: Type::Sequence(vec![SequenceField {
3589 name: "x".to_string(),
3590 ty: Type::Integer(None, vec![]),
3591 optional: false,
3592 default: None,
3593 }]),
3594 },
3595 ChoiceVariant {
3596 name: "intVal".to_string(),
3597 ty: Type::Integer(None, vec![]),
3598 },
3599 ]),
3600 }],
3601 };
3602 let result = generate_c(&module).unwrap();
3603 assert!(
3605 result.contains("void* seq_val;"),
3606 "expected void* for inline SEQUENCE variant:\n{}",
3607 result
3608 );
3609 assert!(
3611 result.contains("SyntaInteger* int_val;"),
3612 "regular integer variant missing:\n{}",
3613 result
3614 );
3615 assert!(
3617 !result.contains("struct /* complex type */ seq_val"),
3618 "invalid embedded struct must not appear:\n{}",
3619 result
3620 );
3621 }
3622
3623 #[test]
3624 fn test_sequence_all_optional_gets_default_prototype() {
3625 let module = make_default_module(vec![
3628 SequenceField {
3629 name: "host".to_string(),
3630 ty: Type::OctetString(None),
3631 optional: true,
3632 default: None,
3633 },
3634 SequenceField {
3635 name: "port".to_string(),
3636 ty: Type::Integer(None, vec![]),
3637 optional: true,
3638 default: None,
3639 },
3640 ]);
3641 let result = generate_c(&module).unwrap();
3642 assert!(
3643 result.contains("Config config_default(void);"),
3644 "prototype for all-optional sequence"
3645 );
3646 }
3647}