odin-jsonschema

Implementation of JSON schema for Odin
Log | Files | Refs | LICENSE

emit.odin (18293B)


      1 package jschema
      2 
      3 import "core:strings"
      4 
      5 @(private)
      6 Kind :: enum u8 {
      7 	Any,     // no usable constraints -> json.Value
      8 	Boolean,
      9 	Integer,
     10 	Number,
     11 	Str,
     12 	Struct,
     13 	Enum,
     14 	Union,   // oneOf / anyOf
     15 	Array,
     16 	Map,     // object keyed by arbitrary strings
     17 	Multi,   // several simple types -> inline union
     18 }
     19 
     20 @(private)
     21 Visit_State :: enum u8 {
     22 	Unvisited,
     23 	On_Stack,
     24 	Done,
     25 }
     26 
     27 @(private)
     28 Member :: struct {
     29 	prop: u32,    // global index into Pool.props
     30 	name: string, // original JSON name
     31 	node: Node_Index,
     32 }
     33 
     34 @(private)
     35 Emitter :: struct {
     36 	pool:      ^Pool,
     37 	opts:      Options,
     38 	openapi:   bool,                 // OpenAPI-idiom lowering enabled
     39 	def_names: map[Node_Index]string, // raw def key per node
     40 	names:     []string,              // assigned declaration name per node
     41 	visit:     []Visit_State,
     42 	queued:    []bool,
     43 	queue:     [dynamic]Node_Index,
     44 	broken:    map[u32]bool,          // props that would form a by-value cycle
     45 	taken:     map[string]bool,
     46 	body:             strings.Builder,
     47 	uses_json:        bool,
     48 	uses_or_reference: bool,
     49 }
     50 
     51 // Follows chains of nodes that are nothing but a $ref.
     52 @(private)
     53 effective :: proc(e: ^Emitter, idx: Node_Index) -> Node_Index {
     54 	idx := idx
     55 	for _ in 0 ..< len(e.pool.nodes) {
     56 		node := &e.pool.nodes[idx]
     57 		if node.ref == NIL_NODE {
     58 			break
     59 		}
     60 		pure := node.props.count == 0 &&
     61 			node.all_of.count == 0 &&
     62 			node.any_of.count == 0 &&
     63 			node.one_of.count == 0 &&
     64 			node.enum_kind == .None &&
     65 			node.types == {} &&
     66 			node.items == NIL_NODE &&
     67 			node.additional == NIL_NODE
     68 		if !pure {
     69 			break
     70 		}
     71 		idx = node.ref
     72 	}
     73 	return idx
     74 }
     75 
     76 @(private)
     77 is_null_only :: proc(e: ^Emitter, idx: Node_Index) -> bool {
     78 	node := &e.pool.nodes[idx]
     79 	return node.types == {.Null} &&
     80 		node.props.count == 0 &&
     81 		node.all_of.count == 0 &&
     82 		node.any_of.count == 0 &&
     83 		node.one_of.count == 0 &&
     84 		node.enum_kind == .None
     85 }
     86 
     87 @(private)
     88 classify :: proc(e: ^Emitter, idx: Node_Index) -> Kind {
     89 	node := &e.pool.nodes[idx]
     90 	if node.props.count > 0 || node.all_of.count > 0 {
     91 		return .Struct
     92 	}
     93 	if node.enum_kind == .Strings {
     94 		all_idents := node.enum_values.count > 0
     95 		for value in enum_values(e, idx) {
     96 			if !is_valid_ident(value) {
     97 				all_idents = false
     98 				break
     99 			}
    100 		}
    101 		if all_idents {
    102 			return .Enum
    103 		}
    104 		return .Str
    105 	}
    106 	if node.one_of.count > 0 || node.any_of.count > 0 {
    107 		return .Union
    108 	}
    109 
    110 	types := node.types - {.Null}
    111 	switch card(types) {
    112 	case 0:
    113 		if node.additional != NIL_NODE || node.additional_bool != .Unset {
    114 			return .Map
    115 		}
    116 		if node.items != NIL_NODE {
    117 			return .Array
    118 		}
    119 		return .Any
    120 	case 1:
    121 		switch {
    122 		case .String in types:
    123 			return .Str
    124 		case .Integer in types:
    125 			return .Integer
    126 		case .Number in types:
    127 			return .Number
    128 		case .Boolean in types:
    129 			return .Boolean
    130 		case .Array in types:
    131 			return .Array
    132 		}
    133 		return .Map // .Object
    134 	}
    135 	return .Multi
    136 }
    137 
    138 @(private)
    139 enum_values :: proc(e: ^Emitter, idx: Node_Index) -> []string {
    140 	r := e.pool.nodes[idx].enum_values
    141 	return e.pool.strings[r.first:][:r.count]
    142 }
    143 
    144 @(private)
    145 is_reference_def :: proc(e: ^Emitter, idx: Node_Index) -> bool {
    146 	name, ok := e.def_names[idx]
    147 	return ok && name == "reference"
    148 }
    149 
    150 // A 2-variant union shaped like OpenAPI's "X-or-reference" idiom: one
    151 // branch resolves to the "reference" $def, the other to a concrete type.
    152 // Lowered to the generic Or_Reference($T) instead of a dedicated decl.
    153 // Only applies when the non-reference branch is itself a named decl
    154 // (struct/enum/union); inline-only kinds (map, array, etc.) cannot be a
    155 // polymorphic type argument and fall back to a dedicated union decl.
    156 @(private)
    157 is_or_reference :: proc(e: ^Emitter, idx: Node_Index) -> (ref: Node_Index, other: Node_Index, ok: bool) {
    158 	if !e.openapi {
    159 		return NIL_NODE, NIL_NODE, false
    160 	}
    161 	variants := union_variants(e, idx)
    162 	if len(variants) != 2 {
    163 		return NIL_NODE, NIL_NODE, false
    164 	}
    165 	a := effective(e, variants[0])
    166 	b := effective(e, variants[1])
    167 	a_ref := is_reference_def(e, a)
    168 	b_ref := is_reference_def(e, b)
    169 	if a_ref && !b_ref {
    170 		#partial switch classify(e, b) {
    171 		case .Struct, .Enum, .Union:
    172 			return a, b, true
    173 		}
    174 	}
    175 	if b_ref && !a_ref {
    176 		#partial switch classify(e, a) {
    177 		case .Struct, .Enum, .Union:
    178 			return b, a, true
    179 		}
    180 	}
    181 	return NIL_NODE, NIL_NODE, false
    182 }
    183 
    184 @(private)
    185 union_variants :: proc(e: ^Emitter, idx: Node_Index) -> []Node_Index {
    186 	node := &e.pool.nodes[idx]
    187 	r := node.one_of if node.one_of.count > 0 else node.any_of
    188 	return e.pool.children[r.first:][:r.count]
    189 }
    190 
    191 // Collects the struct members of a node, merging $ref targets and allOf
    192 // children depth-first. The first occurrence of a property name wins.
    193 @(private)
    194 collect_members :: proc(
    195 	e: ^Emitter,
    196 	idx: Node_Index,
    197 	members: ^[dynamic]Member,
    198 	required: ^map[string]bool,
    199 ) {
    200 	seen := make(map[Node_Index]bool, context.temp_allocator)
    201 	have := make(map[string]bool, context.temp_allocator)
    202 
    203 	gather :: proc(
    204 		e: ^Emitter,
    205 		idx: Node_Index,
    206 		members: ^[dynamic]Member,
    207 		required: ^map[string]bool,
    208 		seen: ^map[Node_Index]bool,
    209 		have: ^map[string]bool,
    210 	) {
    211 		if seen[idx] {
    212 			return
    213 		}
    214 		seen[idx] = true
    215 		node := e.pool.nodes[idx]
    216 		if node.ref != NIL_NODE {
    217 			gather(e, node.ref, members, required, seen, have)
    218 		}
    219 		for child in e.pool.children[node.all_of.first:][:node.all_of.count] {
    220 			gather(e, child, members, required, seen, have)
    221 		}
    222 		for name in e.pool.names[node.required.first:][:node.required.count] {
    223 			required[name] = true
    224 		}
    225 		for i in 0 ..< node.props.count {
    226 			prop_index := node.props.first + i
    227 			prop := e.pool.props[prop_index]
    228 			if have[prop.name] {
    229 				continue
    230 			}
    231 			have[prop.name] = true
    232 			append(members, Member{prop = prop_index, name = prop.name, node = prop.node})
    233 		}
    234 	}
    235 	gather(e, idx, members, required, &seen, &have)
    236 }
    237 
    238 // --- by-value cycle detection ------------------------------------------------
    239 //
    240 // Odin structs cannot contain themselves by value (including through Maybe or
    241 // a union), so any property whose type chain reaches a struct that is already
    242 // being laid out is emitted as json.Value instead.
    243 
    244 @(private)
    245 find_cycles :: proc(e: ^Emitter, idx: Node_Index) {
    246 	visit_indirect(e, idx)
    247 }
    248 
    249 // Follows a property's type by value; reports whether it hit a struct that is
    250 // currently on the layout stack.
    251 @(private)
    252 visit_value :: proc(e: ^Emitter, idx: Node_Index) -> (cycles: bool) {
    253 	target := effective(e, idx)
    254 	switch classify(e, target) {
    255 	case .Struct:
    256 		switch e.visit[target] {
    257 		case .On_Stack:
    258 			return true
    259 		case .Done:
    260 			return false
    261 		case .Unvisited:
    262 			visit_struct(e, target)
    263 		}
    264 	case .Union:
    265 		for variant in union_variants(e, target) {
    266 			if is_null_only(e, effective(e, variant)) {
    267 				continue
    268 			}
    269 			if visit_value(e, variant) {
    270 				return true
    271 			}
    272 		}
    273 	case .Multi, .Array:
    274 		items := e.pool.nodes[target].items
    275 		if items != NIL_NODE {
    276 			visit_indirect(e, items)
    277 		}
    278 		if classify(e, target) == .Multi && .Object in e.pool.nodes[target].types {
    279 			additional := e.pool.nodes[target].additional
    280 			if additional != NIL_NODE {
    281 				visit_indirect(e, additional)
    282 			}
    283 		}
    284 	case .Map:
    285 		additional := e.pool.nodes[target].additional
    286 		if additional != NIL_NODE {
    287 			visit_indirect(e, additional)
    288 		}
    289 	case .Any, .Boolean, .Integer, .Number, .Str, .Enum:
    290 	}
    291 	return false
    292 }
    293 
    294 // Crosses an indirection (slice or map), which breaks value containment.
    295 @(private)
    296 visit_indirect :: proc(e: ^Emitter, idx: Node_Index) {
    297 	target := effective(e, idx)
    298 	switch classify(e, target) {
    299 	case .Struct:
    300 		if e.visit[target] == .Unvisited {
    301 			visit_struct(e, target)
    302 		}
    303 	case .Union:
    304 		for variant in union_variants(e, target) {
    305 			visit_indirect(e, variant)
    306 		}
    307 	case .Multi, .Array:
    308 		items := e.pool.nodes[target].items
    309 		if items != NIL_NODE {
    310 			visit_indirect(e, items)
    311 		}
    312 		additional := e.pool.nodes[target].additional
    313 		if additional != NIL_NODE {
    314 			visit_indirect(e, additional)
    315 		}
    316 	case .Map:
    317 		additional := e.pool.nodes[target].additional
    318 		if additional != NIL_NODE {
    319 			visit_indirect(e, additional)
    320 		}
    321 	case .Any, .Boolean, .Integer, .Number, .Str, .Enum:
    322 	}
    323 }
    324 
    325 @(private)
    326 visit_struct :: proc(e: ^Emitter, idx: Node_Index) {
    327 	e.visit[idx] = .On_Stack
    328 	members := make([dynamic]Member, context.temp_allocator)
    329 	required := make(map[string]bool, context.temp_allocator)
    330 	collect_members(e, idx, &members, &required)
    331 	for member in members {
    332 		if visit_value(e, member.node) {
    333 			e.broken[member.prop] = true
    334 		}
    335 	}
    336 	e.visit[idx] = .Done
    337 }
    338 
    339 // --- naming ------------------------------------------------------------------
    340 
    341 @(private)
    342 unique_name :: proc(e: ^Emitter, base: string) -> string {
    343 	if !e.taken[base] {
    344 		e.taken[base] = true
    345 		return base
    346 	}
    347 	for n := 2; ; n += 1 {
    348 		candidate := strings.concatenate({base, "_", itoa(n)})
    349 		if !e.taken[candidate] {
    350 			e.taken[candidate] = true
    351 			return candidate
    352 		}
    353 	}
    354 }
    355 
    356 // Assigns (once) a declaration name to a node and queues it for emission.
    357 @(private)
    358 ensure_decl :: proc(e: ^Emitter, idx: Node_Index, hint: string) -> string {
    359 	if e.names[idx] != "" {
    360 		return e.names[idx]
    361 	}
    362 	base := hint
    363 	if def, is_def := e.def_names[idx]; is_def {
    364 		base = type_name(def)
    365 	}
    366 	name := unique_name(e, base)
    367 	e.names[idx] = name
    368 	if !e.queued[idx] {
    369 		e.queued[idx] = true
    370 		append(&e.queue, idx)
    371 	}
    372 	return name
    373 }
    374 
    375 // --- type expressions ----------------------------------------------------------
    376 
    377 // Produces the Odin type expression for a property or element schema.
    378 // union_like types (unions and json.Value) already encode absence as nil,
    379 // so they are never wrapped in Maybe.
    380 @(private)
    381 type_expr :: proc(e: ^Emitter, idx: Node_Index, hint: string) -> (expr: string, union_like: bool, nullable: bool) {
    382 	target := effective(e, idx)
    383 	node := &e.pool.nodes[target]
    384 	nullable = .Null in e.pool.nodes[idx].types || .Null in node.types
    385 
    386 	switch classify(e, target) {
    387 	case .Any:
    388 		e.uses_json = true
    389 		return "json.Value", true, nullable
    390 	case .Str:
    391 		return "string", false, nullable
    392 	case .Integer:
    393 		return "i64", false, nullable
    394 	case .Number:
    395 		return "f64", false, nullable
    396 	case .Boolean:
    397 		return "bool", false, nullable
    398 	case .Struct, .Enum:
    399 		return ensure_decl(e, target, hint), false, nullable
    400 	case .Union:
    401 		if ref, other, ok := is_or_reference(e, target); ok {
    402 			other_name := ensure_decl(e, other, hint)
    403 			ensure_decl(e, ref, "Reference")
    404 			e.uses_or_reference = true
    405 			return strings.concatenate({"Or_Reference(", other_name, ")"}), true, nullable
    406 		}
    407 		if _, is_def := e.def_names[target]; is_def || e.names[target] != "" {
    408 			return ensure_decl(e, target, hint), true, nullable
    409 		}
    410 		inline, inline_union_like, nullable_variant := union_expr(e, target, hint)
    411 		return inline, inline_union_like, nullable || nullable_variant
    412 	case .Multi:
    413 		return multi_expr(e, target, hint), true, nullable
    414 	case .Array:
    415 		item := "json.Value"
    416 		if node.items != NIL_NODE {
    417 			item, _, _ = type_expr(e, node.items, strings.concatenate({hint, "_Item"}))
    418 		} else {
    419 			e.uses_json = true
    420 		}
    421 		return strings.concatenate({"[]", item}), false, nullable
    422 	case .Map:
    423 		value := "json.Value"
    424 		if node.additional != NIL_NODE {
    425 			value, _, _ = type_expr(e, node.additional, strings.concatenate({hint, "_Value"}))
    426 		} else {
    427 			e.uses_json = true
    428 		}
    429 		return strings.concatenate({"map[string]", value}), false, nullable
    430 	}
    431 	e.uses_json = true
    432 	return "json.Value", true, nullable
    433 }
    434 
    435 @(private)
    436 union_expr :: proc(e: ^Emitter, idx: Node_Index, hint: string) -> (expr: string, union_like: bool, nullable: bool) {
    437 	parts := make([dynamic]string, context.temp_allocator)
    438 	sole_union_like := false
    439 	for variant, i in union_variants(e, idx) {
    440 		if is_null_only(e, effective(e, variant)) {
    441 			nullable = true
    442 			continue
    443 		}
    444 		variant_hint := strings.concatenate({hint, "_Variant_", itoa(i + 1)})
    445 		part, part_union_like, variant_nullable := type_expr(e, variant, variant_hint)
    446 		nullable |= variant_nullable
    447 		sole_union_like = part_union_like
    448 		append(&parts, part)
    449 	}
    450 	switch len(parts) {
    451 	case 0:
    452 		e.uses_json = true
    453 		return "json.Value", true, nullable
    454 	case 1:
    455 		return parts[0], sole_union_like, nullable
    456 	}
    457 	b: strings.Builder
    458 	strings.builder_init(&b)
    459 	strings.write_string(&b, "union {")
    460 	for part, i in parts {
    461 		if i > 0 {
    462 			strings.write_string(&b, ", ")
    463 		}
    464 		strings.write_string(&b, part)
    465 	}
    466 	strings.write_string(&b, "}")
    467 	return strings.to_string(b), true, nullable
    468 }
    469 
    470 // Emission order for multi-type schemas, so output is deterministic.
    471 @(private)
    472 MULTI_ORDER :: [?]Simple_Type{.String, .Integer, .Number, .Boolean, .Array, .Object}
    473 
    474 @(private)
    475 multi_expr :: proc(e: ^Emitter, idx: Node_Index, hint: string) -> string {
    476 	node := &e.pool.nodes[idx]
    477 	b: strings.Builder
    478 	strings.builder_init(&b)
    479 	strings.write_string(&b, "union {")
    480 	written := 0
    481 	for t in MULTI_ORDER {
    482 		if t not_in node.types {
    483 			continue
    484 		}
    485 		if written > 0 {
    486 			strings.write_string(&b, ", ")
    487 		}
    488 		switch t {
    489 		case .String:
    490 			strings.write_string(&b, "string")
    491 		case .Integer:
    492 			strings.write_string(&b, "i64")
    493 		case .Number:
    494 			strings.write_string(&b, "f64")
    495 		case .Boolean:
    496 			strings.write_string(&b, "bool")
    497 		case .Array:
    498 			item := "json.Value"
    499 			if node.items != NIL_NODE {
    500 				item, _, _ = type_expr(e, node.items, strings.concatenate({hint, "_Item"}))
    501 			} else {
    502 				e.uses_json = true
    503 			}
    504 			strings.write_string(&b, "[]")
    505 			strings.write_string(&b, item)
    506 		case .Object:
    507 			value := "json.Value"
    508 			if node.additional != NIL_NODE {
    509 				value, _, _ = type_expr(e, node.additional, strings.concatenate({hint, "_Value"}))
    510 			} else {
    511 				e.uses_json = true
    512 			}
    513 			strings.write_string(&b, "map[string]")
    514 			strings.write_string(&b, value)
    515 		case .Null:
    516 		}
    517 		written += 1
    518 	}
    519 	strings.write_string(&b, "}")
    520 	return strings.to_string(b)
    521 }
    522 
    523 // --- declarations ----------------------------------------------------------------
    524 
    525 @(private)
    526 emit_struct :: proc(e: ^Emitter, idx: Node_Index) {
    527 	name := e.names[idx]
    528 	members := make([dynamic]Member, context.temp_allocator)
    529 	required := make(map[string]bool, context.temp_allocator)
    530 	collect_members(e, idx, &members, &required)
    531 
    532 	b := &e.body
    533 	if len(members) == 0 {
    534 		strings.write_string(b, name)
    535 		strings.write_string(b, " :: struct {}")
    536 		return
    537 	}
    538 
    539 	strings.write_string(b, name)
    540 	strings.write_string(b, " :: struct {\n")
    541 	field_names := make(map[string]bool, context.temp_allocator)
    542 	for member in members {
    543 		fname := field_name(member.name)
    544 		if field_names[fname] {
    545 			fname = unique_field(fname, &field_names)
    546 		}
    547 		field_names[fname] = true
    548 
    549 		expr: string
    550 		union_like: bool
    551 		nullable: bool
    552 		if e.broken[member.prop] {
    553 			e.uses_json = true
    554 			expr, union_like = "json.Value", true
    555 		} else {
    556 			hint := strings.concatenate({name, "_", type_name(member.name)})
    557 			expr, union_like, nullable = type_expr(e, member.node, hint)
    558 		}
    559 
    560 		optional := !required[member.name]
    561 		strings.write_string(b, "\t")
    562 		strings.write_string(b, fname)
    563 		strings.write_string(b, ": ")
    564 		if !union_like && (optional || nullable) {
    565 			strings.write_string(b, "Maybe(")
    566 			strings.write_string(b, expr)
    567 			strings.write_string(b, ")")
    568 		} else {
    569 			strings.write_string(b, expr)
    570 		}
    571 		strings.write_string(b, " `json:\"")
    572 		strings.write_string(b, member.name)
    573 		strings.write_string(b, "\"`,\n")
    574 	}
    575 	strings.write_string(b, "}")
    576 }
    577 
    578 @(private)
    579 unique_field :: proc(base: string, taken: ^map[string]bool) -> string {
    580 	for n := 2; ; n += 1 {
    581 		candidate := strings.concatenate({base, "_", itoa(n)})
    582 		if !taken[candidate] {
    583 			return candidate
    584 		}
    585 	}
    586 }
    587 
    588 @(private)
    589 emit_enum :: proc(e: ^Emitter, idx: Node_Index) {
    590 	b := &e.body
    591 	strings.write_string(b, e.names[idx])
    592 	strings.write_string(b, " :: enum {\n")
    593 	for value in enum_values(e, idx) {
    594 		strings.write_string(b, "\t")
    595 		strings.write_string(b, value)
    596 		strings.write_string(b, ",\n")
    597 	}
    598 	strings.write_string(b, "}")
    599 }
    600 
    601 @(private)
    602 emit_union :: proc(e: ^Emitter, idx: Node_Index) {
    603 	name := e.names[idx]
    604 	b := &e.body
    605 	if ref, other, ok := is_or_reference(e, idx); ok {
    606 		other_name := ensure_decl(e, other, name)
    607 		ensure_decl(e, ref, "Reference")
    608 		e.uses_or_reference = true
    609 		strings.write_string(b, name)
    610 		strings.write_string(b, " :: Or_Reference(")
    611 		strings.write_string(b, other_name)
    612 		strings.write_string(b, ")")
    613 		return
    614 	}
    615 	expr, _, _ := union_expr(e, idx, name)
    616 	strings.write_string(b, name)
    617 	strings.write_string(b, " :: ")
    618 	strings.write_string(b, expr)
    619 }
    620 
    621 // Emits the full Odin source for a resolved pool.
    622 @(private)
    623 emit :: proc(pool: ^Pool, opts: Options, openapi: bool) -> string {
    624 	e := Emitter {
    625 		pool    = pool,
    626 		opts    = opts,
    627 		openapi = openapi,
    628 		names   = make([]string, len(pool.nodes)),
    629 		visit   = make([]Visit_State, len(pool.nodes)),
    630 		queued  = make([]bool, len(pool.nodes)),
    631 	}
    632 	strings.builder_init(&e.body)
    633 
    634 	for def in pool.defs {
    635 		if _, exists := e.def_names[def.node]; !exists {
    636 			e.def_names[def.node] = def.name
    637 		}
    638 	}
    639 
    640 	root := effective(&e, pool.root)
    641 	find_cycles(&e, root)
    642 
    643 	root_name := opts.root_name if opts.root_name != "" else "Root"
    644 	switch classify(&e, root) {
    645 	case .Struct, .Enum, .Union:
    646 		ensure_decl(&e, root, root_name)
    647 	case .Any, .Boolean, .Integer, .Number, .Str, .Array, .Map, .Multi:
    648 		// Root is an alias-style declaration.
    649 		expr, _, _ := type_expr(&e, root, root_name)
    650 		name := unique_name(&e, root_name)
    651 		strings.write_string(&e.body, name)
    652 		strings.write_string(&e.body, " :: ")
    653 		strings.write_string(&e.body, expr)
    654 	}
    655 
    656 	for i := 0; i < len(e.queue); i += 1 {
    657 		if strings.builder_len(e.body) > 0 {
    658 			strings.write_string(&e.body, "\n\n")
    659 		}
    660 		idx := e.queue[i]
    661 		switch classify(&e, idx) {
    662 		case .Struct:
    663 			emit_struct(&e, idx)
    664 		case .Enum:
    665 			emit_enum(&e, idx)
    666 		case .Union:
    667 			emit_union(&e, idx)
    668 		case .Any, .Boolean, .Integer, .Number, .Str, .Array, .Map, .Multi:
    669 		}
    670 	}
    671 
    672 	package_name := opts.package_name if opts.package_name != "" else "schema"
    673 	out: strings.Builder
    674 	strings.builder_init(&out)
    675 	strings.write_string(&out, "// Code generated by jschema. DO NOT EDIT.\npackage ")
    676 	strings.write_string(&out, package_name)
    677 	strings.write_string(&out, "\n\n")
    678 	if e.uses_json {
    679 		strings.write_string(&out, "import \"core:encoding/json\"\n\n")
    680 	}
    681 	if e.uses_or_reference {
    682 		strings.write_string(&out, "Or_Reference :: union($T: typeid) {Reference, T}\n\n")
    683 	}
    684 	strings.write_string(&out, strings.to_string(e.body))
    685 	strings.write_string(&out, "\n")
    686 	return strings.to_string(out)
    687 }