1use vexil_lang::ast::{PrimitiveType, SemanticType};
2use vexil_lang::ir::{
3 ConfigDef, Encoding, FieldEncoding, MessageDef, ResolvedType, TypeDef, TypeRegistry,
4};
5
6use crate::emit::CodeWriter;
7use crate::types::ts_type;
8
9pub fn is_byte_aligned(ty: &ResolvedType, registry: &TypeRegistry) -> bool {
15 match ty {
16 ResolvedType::Primitive(PrimitiveType::Bool) => false,
17 ResolvedType::SubByte(_) => false,
18 ResolvedType::Named(id) => {
19 if let Some(TypeDef::Enum(e)) = registry.get(*id) {
20 e.wire_bits >= 8
21 } else {
22 true
23 }
24 }
25 ResolvedType::Optional(inner) => is_byte_aligned(inner, registry),
26 _ => true,
27 }
28}
29
30fn primitive_bits(p: &PrimitiveType) -> u8 {
35 match p {
36 PrimitiveType::I8 | PrimitiveType::U8 => 8,
37 PrimitiveType::I16 | PrimitiveType::U16 => 16,
38 PrimitiveType::I32 | PrimitiveType::U32 | PrimitiveType::F32 => 32,
39 PrimitiveType::I64 | PrimitiveType::U64 | PrimitiveType::F64 => 64,
40 _ => 0,
41 }
42}
43
44pub fn emit_write(
53 w: &mut CodeWriter,
54 access: &str,
55 ty: &ResolvedType,
56 enc: &FieldEncoding,
57 registry: &TypeRegistry,
58 writer: &str,
59) {
60 match &enc.encoding {
62 Encoding::Varint => {
63 let is_64 = matches!(
64 ty,
65 ResolvedType::Primitive(PrimitiveType::U64 | PrimitiveType::I64)
66 );
67 if is_64 {
68 w.line(&format!("{writer}.writeLeb12864({access});"));
69 } else {
70 w.line(&format!("{writer}.writeLeb128({access});"));
71 }
72 return;
73 }
74 Encoding::ZigZag => {
75 let type_bits = match ty {
76 ResolvedType::Primitive(p) => primitive_bits(p),
77 _ => 64,
78 };
79 if type_bits == 64 {
80 w.line(&format!("{writer}.writeZigZag64({access});"));
81 } else {
82 w.line(&format!("{writer}.writeZigZag({access}, {type_bits});",));
83 }
84 return;
85 }
86 Encoding::Delta(inner) => {
87 let base_enc = FieldEncoding {
88 encoding: *inner.clone(),
89 limit: enc.limit,
90 };
91 emit_write(w, access, ty, &base_enc, registry, writer);
92 return;
93 }
94 Encoding::Default => {} _ => {} }
97
98 emit_write_type(w, access, ty, registry, writer);
99}
100
101fn emit_write_type(
102 w: &mut CodeWriter,
103 access: &str,
104 ty: &ResolvedType,
105 registry: &TypeRegistry,
106 writer: &str,
107) {
108 match ty {
109 ResolvedType::Primitive(p) => match p {
110 PrimitiveType::Bool => w.line(&format!("{writer}.writeBool({access});")),
111 PrimitiveType::U8 => w.line(&format!("{writer}.writeU8({access});")),
112 PrimitiveType::U16 => w.line(&format!("{writer}.writeU16({access});")),
113 PrimitiveType::U32 => w.line(&format!("{writer}.writeU32({access});")),
114 PrimitiveType::U64 => w.line(&format!("{writer}.writeU64({access});")),
115 PrimitiveType::I8 => w.line(&format!("{writer}.writeI8({access});")),
116 PrimitiveType::I16 => w.line(&format!("{writer}.writeI16({access});")),
117 PrimitiveType::I32 => w.line(&format!("{writer}.writeI32({access});")),
118 PrimitiveType::I64 => w.line(&format!("{writer}.writeI64({access});")),
119 PrimitiveType::F32 => w.line(&format!("{writer}.writeF32({access});")),
120 PrimitiveType::F64 => w.line(&format!("{writer}.writeF64({access});")),
121 PrimitiveType::Void => {} },
123 ResolvedType::SubByte(s) => {
124 let bits = s.bits;
125 w.line(&format!("{writer}.writeBits({access}, {bits});"));
126 }
127 ResolvedType::Semantic(s) => match s {
128 SemanticType::String => w.line(&format!("{writer}.writeString({access});")),
129 SemanticType::Bytes => w.line(&format!("{writer}.writeBytes({access});")),
130 SemanticType::Rgb => {
131 w.line(&format!("{writer}.writeU8({access}[0]);"));
132 w.line(&format!("{writer}.writeU8({access}[1]);"));
133 w.line(&format!("{writer}.writeU8({access}[2]);"));
134 }
135 SemanticType::Uuid => w.line(&format!("{writer}.writeRawBytes({access});")),
136 SemanticType::Timestamp => w.line(&format!("{writer}.writeI64({access});")),
137 SemanticType::Hash => w.line(&format!("{writer}.writeRawBytes({access});")),
138 },
139 ResolvedType::Named(id) => {
140 let type_name = match registry.get(*id) {
141 Some(def) => match def {
142 TypeDef::Message(m) => m.name.to_string(),
143 TypeDef::Enum(e) => e.name.to_string(),
144 TypeDef::Flags(f) => f.name.to_string(),
145 TypeDef::Union(u) => u.name.to_string(),
146 TypeDef::Newtype(n) => n.name.to_string(),
147 _ => "Unknown".to_string(),
148 },
149 None => "Unknown".to_string(),
150 };
151 w.line(&format!("{writer}.enterNested();"));
152 w.line(&format!("encode{type_name}({access}, {writer});"));
153 w.line(&format!("{writer}.leaveNested();"));
154 }
155 ResolvedType::Optional(inner) => {
156 w.line(&format!("{writer}.writeBool({access} !== null);"));
158 if is_byte_aligned(inner, registry) {
159 w.line(&format!("{writer}.flushToByteBoundary();"));
160 }
161 w.open_block(&format!("if ({access} !== null)"));
162 emit_write_type(w, access, inner, registry, writer);
163 w.close_block();
164 }
165 ResolvedType::Array(inner) => {
166 w.line(&format!("{writer}.writeLeb128({access}.length);"));
167 w.open_block(&format!("for (const item of {access})"));
168 emit_write_type(w, "item", inner, registry, writer);
169 w.close_block();
170 }
171 ResolvedType::Map(k, v) => {
172 w.line(&format!("{writer}.writeLeb128({access}.size);"));
173 w.open_block(&format!("for (const [mapK, mapV] of {access})"));
174 emit_write_type(w, "mapK", k, registry, writer);
175 emit_write_type(w, "mapV", v, registry, writer);
176 w.close_block();
177 }
178 ResolvedType::Result(ok, err) => {
179 w.open_block(&format!("if ('ok' in {access})"));
180 w.line(&format!("{writer}.writeBool(true);"));
181 emit_write_type(w, &format!("{access}.ok"), ok, registry, writer);
182 w.dedent();
183 w.line("} else {");
184 w.indent();
185 w.line(&format!("{writer}.writeBool(false);"));
186 emit_write_type(w, &format!("{access}.err"), err, registry, writer);
187 w.close_block();
188 }
189 _ => {} }
191}
192
193pub fn emit_read(
201 w: &mut CodeWriter,
202 var_name: &str,
203 ty: &ResolvedType,
204 enc: &FieldEncoding,
205 registry: &TypeRegistry,
206 reader: &str,
207) {
208 match &enc.encoding {
209 Encoding::Varint => {
210 let is_64 = matches!(
211 ty,
212 ResolvedType::Primitive(PrimitiveType::U64 | PrimitiveType::I64)
213 );
214 if is_64 {
215 w.line(&format!("const {var_name} = {reader}.readLeb12864();"));
216 } else {
217 w.line(&format!("const {var_name} = {reader}.readLeb128();"));
218 }
219 return;
220 }
221 Encoding::ZigZag => {
222 let type_bits = match ty {
223 ResolvedType::Primitive(p) => primitive_bits(p),
224 _ => 64,
225 };
226 if type_bits == 64 {
227 w.line(&format!("const {var_name} = {reader}.readZigZag64();",));
228 } else {
229 w.line(&format!(
230 "const {var_name} = {reader}.readZigZag({type_bits});",
231 ));
232 }
233 return;
234 }
235 Encoding::Delta(inner) => {
236 let base_enc = FieldEncoding {
237 encoding: *inner.clone(),
238 limit: enc.limit,
239 };
240 emit_read(w, var_name, ty, &base_enc, registry, reader);
241 return;
242 }
243 Encoding::Default => {}
244 _ => {} }
246
247 emit_read_type(w, var_name, ty, registry, reader);
248}
249
250fn emit_read_type(
251 w: &mut CodeWriter,
252 var_name: &str,
253 ty: &ResolvedType,
254 registry: &TypeRegistry,
255 reader: &str,
256) {
257 match ty {
258 ResolvedType::Primitive(p) => match p {
259 PrimitiveType::Bool => {
260 w.line(&format!("const {var_name} = {reader}.readBool();"));
261 }
262 PrimitiveType::U8 => {
263 w.line(&format!("const {var_name} = {reader}.readU8();"));
264 }
265 PrimitiveType::U16 => {
266 w.line(&format!("const {var_name} = {reader}.readU16();"));
267 }
268 PrimitiveType::U32 => {
269 w.line(&format!("const {var_name} = {reader}.readU32();"));
270 }
271 PrimitiveType::U64 => {
272 w.line(&format!("const {var_name} = {reader}.readU64();"));
273 }
274 PrimitiveType::I8 => {
275 w.line(&format!("const {var_name} = {reader}.readI8();"));
276 }
277 PrimitiveType::I16 => {
278 w.line(&format!("const {var_name} = {reader}.readI16();"));
279 }
280 PrimitiveType::I32 => {
281 w.line(&format!("const {var_name} = {reader}.readI32();"));
282 }
283 PrimitiveType::I64 => {
284 w.line(&format!("const {var_name} = {reader}.readI64();"));
285 }
286 PrimitiveType::F32 => {
287 w.line(&format!("const {var_name} = {reader}.readF32();"));
288 }
289 PrimitiveType::F64 => {
290 w.line(&format!("const {var_name} = {reader}.readF64();"));
291 }
292 PrimitiveType::Void => {
293 w.line(&format!("const {var_name} = undefined;"));
294 }
295 },
296 ResolvedType::SubByte(s) => {
297 let bits = s.bits;
298 if s.signed {
299 let shift = 8 - bits;
301 w.line(&format!(
302 "const {var_name} = ({reader}.readBits({bits}) << {shift}) >> {shift};",
303 ));
304 } else {
305 w.line(&format!("const {var_name} = {reader}.readBits({bits});",));
306 }
307 }
308 ResolvedType::Semantic(s) => match s {
309 SemanticType::String => {
310 w.line(&format!("const {var_name} = {reader}.readString();"));
311 }
312 SemanticType::Bytes => {
313 w.line(&format!("const {var_name} = {reader}.readBytes();"));
314 }
315 SemanticType::Rgb => {
316 w.line(&format!("const {var_name}_0 = {reader}.readU8();"));
317 w.line(&format!("const {var_name}_1 = {reader}.readU8();"));
318 w.line(&format!("const {var_name}_2 = {reader}.readU8();"));
319 w.line(&format!(
320 "const {var_name}: [number, number, number] = [{var_name}_0, {var_name}_1, {var_name}_2];"
321 ));
322 }
323 SemanticType::Uuid => {
324 w.line(&format!("const {var_name} = {reader}.readRawBytes(16);"));
325 }
326 SemanticType::Timestamp => {
327 w.line(&format!("const {var_name} = {reader}.readI64();"));
328 }
329 SemanticType::Hash => {
330 w.line(&format!("const {var_name} = {reader}.readRawBytes(32);"));
331 }
332 },
333 ResolvedType::Named(id) => {
334 let type_name = match registry.get(*id) {
335 Some(def) => match def {
336 TypeDef::Message(m) => m.name.to_string(),
337 TypeDef::Enum(e) => e.name.to_string(),
338 TypeDef::Flags(f) => f.name.to_string(),
339 TypeDef::Union(u) => u.name.to_string(),
340 TypeDef::Newtype(n) => n.name.to_string(),
341 _ => "Unknown".to_string(),
342 },
343 None => "Unknown".to_string(),
344 };
345 w.line(&format!("{reader}.enterNested();"));
346 w.line(&format!("const {var_name} = decode{type_name}({reader});"));
347 w.line(&format!("{reader}.leaveNested();"));
348 }
349 ResolvedType::Optional(inner) => {
350 w.line(&format!("const {var_name}_present = {reader}.readBool();"));
351 if is_byte_aligned(inner, registry) {
352 w.line(&format!("{reader}.flushToByteBoundary();"));
353 }
354 let inner_ts = ts_type(inner, registry);
355 w.line(&format!("let {var_name}: {inner_ts} | null;",));
356 w.open_block(&format!("if ({var_name}_present)"));
357 emit_read_type(w, &format!("{var_name}_inner"), inner, registry, reader);
358 w.line(&format!("{var_name} = {var_name}_inner;"));
359 w.dedent();
360 w.line("} else {");
361 w.indent();
362 w.line(&format!("{var_name} = null;"));
363 w.close_block();
364 }
365 ResolvedType::Array(inner) => {
366 w.line(&format!("const {var_name}_len = {reader}.readLeb128();"));
367 let inner_ts = ts_type(inner, registry);
368 w.line(&format!("const {var_name}: {inner_ts}[] = [];"));
369 w.open_block(&format!("for (let i = 0; i < {var_name}_len; i++)"));
370 emit_read_type(w, &format!("{var_name}_item"), inner, registry, reader);
371 w.line(&format!("{var_name}.push({var_name}_item);"));
372 w.close_block();
373 }
374 ResolvedType::Map(k, v) => {
375 w.line(&format!("const {var_name}_len = {reader}.readLeb128();"));
376 let k_ts = ts_type(k, registry);
377 let v_ts = ts_type(v, registry);
378 w.line(&format!("const {var_name} = new Map<{k_ts}, {v_ts}>();"));
379 w.open_block(&format!("for (let i = 0; i < {var_name}_len; i++)"));
380 emit_read_type(w, &format!("{var_name}_k"), k, registry, reader);
381 emit_read_type(w, &format!("{var_name}_v"), v, registry, reader);
382 w.line(&format!("{var_name}.set({var_name}_k, {var_name}_v);"));
383 w.close_block();
384 }
385 ResolvedType::Result(ok, err) => {
386 let ok_ts = ts_type(ok, registry);
387 let err_ts = ts_type(err, registry);
388 w.line(&format!("const {var_name}_isOk = {reader}.readBool();"));
389 w.line(&format!(
390 "let {var_name}: {{ ok: {ok_ts} }} | {{ err: {err_ts} }};"
391 ));
392 w.open_block(&format!("if ({var_name}_isOk)"));
393 emit_read_type(w, &format!("{var_name}_ok"), ok, registry, reader);
394 w.line(&format!("{var_name} = {{ ok: {var_name}_ok }};"));
395 w.dedent();
396 w.line("} else {");
397 w.indent();
398 emit_read_type(w, &format!("{var_name}_err"), err, registry, reader);
399 w.line(&format!("{var_name} = {{ err: {var_name}_err }};"));
400 w.close_block();
401 }
402 _ => {} }
404}
405
406pub fn emit_message(w: &mut CodeWriter, msg: &MessageDef, registry: &TypeRegistry) {
412 let name = msg.name.as_str();
413
414 w.open_block(&format!("export interface {name}"));
416 for field in &msg.fields {
417 let field_ts = ts_type(&field.resolved_type, registry);
418 w.line(&format!("{}: {};", field.name, field_ts));
419 }
420 w.close_block();
421 w.blank();
422
423 w.open_block(&format!(
425 "export function encode{name}(v: {name}, w: BitWriter): void"
426 ));
427 for field in &msg.fields {
428 let access = format!("v.{}", field.name);
429 emit_write(
430 w,
431 &access,
432 &field.resolved_type,
433 &field.encoding,
434 registry,
435 "w",
436 );
437 }
438 w.line("w.flushToByteBoundary();");
439 w.close_block();
440 w.blank();
441
442 w.open_block(&format!(
444 "export function decode{name}(r: BitReader): {name}"
445 ));
446 for field in &msg.fields {
447 emit_read(
448 w,
449 field.name.as_str(),
450 &field.resolved_type,
451 &field.encoding,
452 registry,
453 "r",
454 );
455 }
456 w.line("r.flushToByteBoundary();");
457 let field_names: Vec<&str> = msg.fields.iter().map(|f| f.name.as_str()).collect();
458 w.line(&format!("return {{ {} }};", field_names.join(", ")));
459 w.close_block();
460 w.blank();
461}
462
463pub fn emit_config(w: &mut CodeWriter, cfg: &ConfigDef, registry: &TypeRegistry) {
469 let name = cfg.name.as_str();
470
471 w.open_block(&format!("export interface {name}"));
472 for field in &cfg.fields {
473 let field_ts = ts_type(&field.resolved_type, registry);
474 w.line(&format!("{}: {};", field.name, field_ts));
475 }
476 w.close_block();
477 w.blank();
478}