1use std::sync::Arc;
9
10use cljrs_types::span::Span;
11use cljrs_value::{Keyword, MapValue, Value};
12
13use cljrs_ir::*;
14
15#[derive(Debug)]
18pub enum ConvertError {
19 MissingField(String),
21 TypeError(String),
23 UnknownVariant(String),
25}
26
27impl std::fmt::Display for ConvertError {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 match self {
30 ConvertError::MissingField(s) => write!(f, "missing field: {s}"),
31 ConvertError::TypeError(s) => write!(f, "type error: {s}"),
32 ConvertError::UnknownVariant(s) => write!(f, "unknown variant: {s}"),
33 }
34 }
35}
36
37impl std::error::Error for ConvertError {}
38
39type ConvertResult<T> = Result<T, ConvertError>;
40
41fn get_field(map: &MapValue, key: &str) -> ConvertResult<Value> {
44 let kw = Value::keyword(Keyword::simple(key));
45 map.get(&kw)
46 .filter(|v| !matches!(v, Value::Nil))
47 .ok_or_else(|| ConvertError::MissingField(key.to_string()))
48}
49
50fn get_field_opt(map: &MapValue, key: &str) -> Option<Value> {
51 let kw = Value::keyword(Keyword::simple(key));
52 map.get(&kw).filter(|v| !matches!(v, Value::Nil))
53}
54
55fn as_map(val: &Value) -> ConvertResult<&MapValue> {
56 match val {
57 Value::Map(m) => Ok(m),
58 other => Err(ConvertError::TypeError(format!(
59 "expected map, got {}",
60 other.type_name()
61 ))),
62 }
63}
64
65fn as_long(val: &Value) -> ConvertResult<i64> {
66 match val {
67 Value::Long(n) => Ok(*n),
68 other => Err(ConvertError::TypeError(format!(
69 "expected long, got {}",
70 other.type_name()
71 ))),
72 }
73}
74
75fn as_u32(val: &Value) -> ConvertResult<u32> {
76 as_long(val).map(|n| n as u32)
77}
78
79fn as_str(val: &Value) -> ConvertResult<Arc<str>> {
80 match val {
81 Value::Str(s) => Ok(Arc::from(s.get().as_str())),
82 other => Err(ConvertError::TypeError(format!(
83 "expected string, got {}",
84 other.type_name()
85 ))),
86 }
87}
88
89fn as_keyword_name(val: &Value) -> ConvertResult<Arc<str>> {
90 match val {
91 Value::Keyword(kw) => Ok(Arc::clone(&kw.get().name)),
92 other => Err(ConvertError::TypeError(format!(
93 "expected keyword, got {}",
94 other.type_name()
95 ))),
96 }
97}
98
99fn as_vec(val: &Value) -> ConvertResult<Vec<Value>> {
100 match val {
101 Value::Vector(v) => {
102 let pv = v.get();
103 let mut result = Vec::with_capacity(pv.count());
104 for i in 0..pv.count() {
105 if let Some(item) = pv.nth(i) {
106 result.push(item.clone());
107 }
108 }
109 Ok(result)
110 }
111 Value::List(l) => {
112 let mut result = Vec::new();
113 let mut cur = l.get().clone();
114 while let Some(v) = cur.first() {
115 result.push(v.clone());
116 let rest = cur.rest();
117 cur = (*rest).clone();
118 }
119 Ok(result)
120 }
121 Value::Nil => Ok(vec![]),
122 other => Err(ConvertError::TypeError(format!(
123 "expected vector/list, got {}",
124 other.type_name()
125 ))),
126 }
127}
128
129fn as_var_id(val: &Value) -> ConvertResult<VarId> {
130 as_u32(val).map(VarId)
131}
132
133fn as_block_id(val: &Value) -> ConvertResult<BlockId> {
134 as_u32(val).map(BlockId)
135}
136
137pub fn value_to_ir_function(val: &Value) -> ConvertResult<IrFunction> {
141 let map = as_map(val)?;
142
143 let name = get_field_opt(map, "name").map(|v| as_str(&v)).transpose()?;
144 let next_var = as_u32(&get_field(map, "next-var")?)?;
145 let next_block = as_u32(&get_field(map, "next-block")?)?;
146
147 let params_val = get_field(map, "params")?;
149 let params_vec = as_vec(¶ms_val)?;
150 let mut params = Vec::with_capacity(params_vec.len());
151 for p in ¶ms_vec {
152 let pair = as_vec(p)?;
153 if pair.len() != 2 {
154 return Err(ConvertError::TypeError(
155 "param must be [name var-id]".into(),
156 ));
157 }
158 let name = as_str(&pair[0])?;
159 let var_id = as_var_id(&pair[1])?;
160 params.push((name, var_id));
161 }
162
163 let blocks_val = get_field(map, "blocks")?;
165 let blocks_vec = as_vec(&blocks_val)?;
166 let mut blocks = Vec::with_capacity(blocks_vec.len());
167 for b in &blocks_vec {
168 blocks.push(value_to_block(b)?);
169 }
170
171 let subfunctions = if let Some(subs_val) = get_field_opt(map, "subfunctions") {
173 let subs_vec = as_vec(&subs_val)?;
174 let mut subs = Vec::with_capacity(subs_vec.len());
175 for s in &subs_vec {
176 subs.push(value_to_ir_function(s)?);
177 }
178 subs
179 } else {
180 vec![]
181 };
182
183 Ok(IrFunction {
184 name,
185 params,
186 blocks,
187 next_var,
188 next_block,
189 span: None,
190 subfunctions,
191 })
192}
193
194fn value_to_block(val: &Value) -> ConvertResult<Block> {
196 let map = as_map(val)?;
197
198 let id = as_block_id(&get_field(map, "id")?)?;
199
200 let phis_val = get_field(map, "phis")?;
201 let phis_vec = as_vec(&phis_val)?;
202 let mut phis = Vec::with_capacity(phis_vec.len());
203 for p in &phis_vec {
204 phis.push(value_to_inst(p)?);
205 }
206
207 let insts_val = get_field(map, "insts")?;
208 let insts_vec = as_vec(&insts_val)?;
209 let mut insts = Vec::with_capacity(insts_vec.len());
210 for i in &insts_vec {
211 insts.push(value_to_inst(i)?);
212 }
213
214 let term_val = get_field(map, "terminator")?;
215 let terminator = value_to_terminator(&term_val)?;
216
217 Ok(Block {
218 id,
219 phis,
220 insts,
221 terminator,
222 })
223}
224
225fn value_to_inst(val: &Value) -> ConvertResult<Inst> {
227 let map = as_map(val)?;
228 let op = as_keyword_name(&get_field(map, "op")?)?;
229
230 match op.as_ref() {
231 "const" => {
232 let dst = as_var_id(&get_field(map, "dst")?)?;
233 let const_val = get_field(map, "value")?;
234 let c = value_to_const(&const_val)?;
235 Ok(Inst::Const(dst, c))
236 }
237 "load-local" => {
238 let dst = as_var_id(&get_field(map, "dst")?)?;
239 let name = as_str(&get_field(map, "name")?)?;
240 Ok(Inst::LoadLocal(dst, name))
241 }
242 "load-global" => {
243 let dst = as_var_id(&get_field(map, "dst")?)?;
244 let ns = as_str(&get_field(map, "ns")?)?;
245 let name = as_str(&get_field(map, "name")?)?;
246 Ok(Inst::LoadGlobal(dst, ns, name))
247 }
248 "load-var" => {
249 let dst = as_var_id(&get_field(map, "dst")?)?;
250 let ns = as_str(&get_field(map, "ns")?)?;
251 let name = as_str(&get_field(map, "name")?)?;
252 Ok(Inst::LoadVar(dst, ns, name))
253 }
254 "alloc-vector" => {
255 let dst = as_var_id(&get_field(map, "dst")?)?;
256 let elems = as_var_id_vec(&get_field(map, "elems")?)?;
257 Ok(Inst::AllocVector(dst, elems))
258 }
259 "alloc-map" => {
260 let dst = as_var_id(&get_field(map, "dst")?)?;
261 let pairs_val = get_field(map, "pairs")?;
262 let pairs_vec = as_vec(&pairs_val)?;
263 let mut pairs = Vec::with_capacity(pairs_vec.len());
264 for p in &pairs_vec {
265 let pair = as_vec(p)?;
266 if pair.len() != 2 {
267 return Err(ConvertError::TypeError("map pair must be [k v]".into()));
268 }
269 pairs.push((as_var_id(&pair[0])?, as_var_id(&pair[1])?));
270 }
271 Ok(Inst::AllocMap(dst, pairs))
272 }
273 "alloc-set" => {
274 let dst = as_var_id(&get_field(map, "dst")?)?;
275 let elems = as_var_id_vec(&get_field(map, "elems")?)?;
276 Ok(Inst::AllocSet(dst, elems))
277 }
278 "alloc-list" => {
279 let dst = as_var_id(&get_field(map, "dst")?)?;
280 let elems = as_var_id_vec(&get_field(map, "elems")?)?;
281 Ok(Inst::AllocList(dst, elems))
282 }
283 "alloc-cons" => {
284 let dst = as_var_id(&get_field(map, "dst")?)?;
285 let head = as_var_id(&get_field(map, "head")?)?;
286 let tail = as_var_id(&get_field(map, "tail")?)?;
287 Ok(Inst::AllocCons(dst, head, tail))
288 }
289 "alloc-closure" => {
290 let dst = as_var_id(&get_field(map, "dst")?)?;
291 let captures = as_var_id_vec(&get_field(map, "captures")?)?;
292 let name = get_field_opt(map, "closure-name")
293 .map(|v| as_str(&v))
294 .transpose()?;
295 let arity_fn_names = if let Some(v) = get_field_opt(map, "arity-fn-names") {
297 as_vec(&v)?
298 .iter()
299 .map(as_str)
300 .collect::<ConvertResult<Vec<_>>>()?
301 } else {
302 vec![]
303 };
304 let param_counts = if let Some(v) = get_field_opt(map, "param-counts") {
306 as_vec(&v)?
307 .iter()
308 .map(|n| as_long(n).map(|i| i as usize))
309 .collect::<ConvertResult<Vec<_>>>()?
310 } else {
311 vec![]
312 };
313 let capture_names = if let Some(v) = get_field_opt(map, "capture-names") {
315 as_vec(&v)?
316 .iter()
317 .map(as_str)
318 .collect::<ConvertResult<Vec<_>>>()?
319 } else {
320 vec![]
321 };
322 let is_variadic = if let Some(v) = get_field_opt(map, "is-variadic") {
324 as_vec(&v)?
325 .iter()
326 .map(|b| match b {
327 Value::Bool(v) => Ok(*v),
328 _ => Err(ConvertError::TypeError(
329 "expected bool for is-variadic".into(),
330 )),
331 })
332 .collect::<ConvertResult<Vec<_>>>()?
333 } else {
334 vec![false; param_counts.len()]
335 };
336 let tmpl = ClosureTemplate {
337 name,
338 arity_fn_names,
339 param_counts,
340 is_variadic,
341 capture_names,
342 };
343 Ok(Inst::AllocClosure(dst, tmpl, captures))
344 }
345 "call-known" => {
346 let dst = as_var_id(&get_field(map, "dst")?)?;
347 let func_kw = as_keyword_name(&get_field(map, "func")?)?;
348 let known = keyword_to_known_fn(&func_kw).ok_or_else(|| {
349 ConvertError::UnknownVariant(format!("unknown KnownFn: {func_kw}"))
350 })?;
351 let args = as_var_id_vec(&get_field(map, "args")?)?;
352 Ok(Inst::CallKnown(dst, known, args))
353 }
354 "call" => {
355 let dst = as_var_id(&get_field(map, "dst")?)?;
356 let callee = as_var_id(&get_field(map, "callee")?)?;
357 let args = as_var_id_vec(&get_field(map, "args")?)?;
358 Ok(Inst::Call(dst, callee, args))
359 }
360 "deref" => {
361 let dst = as_var_id(&get_field(map, "dst")?)?;
362 let src = as_var_id(&get_field(map, "src")?)?;
363 Ok(Inst::Deref(dst, src))
364 }
365 "def-var" => {
366 let dst = as_var_id(&get_field(map, "dst")?)?;
367 let ns = as_str(&get_field(map, "ns")?)?;
368 let name = as_str(&get_field(map, "name")?)?;
369 let value = as_var_id(&get_field(map, "value")?)?;
370 Ok(Inst::DefVar(dst, ns, name, value))
371 }
372 "set!" => {
373 let var = as_var_id(&get_field(map, "var")?)?;
374 let value = as_var_id(&get_field(map, "value")?)?;
375 Ok(Inst::SetBang(var, value))
376 }
377 "throw" => {
378 let val = as_var_id(&get_field(map, "value")?)?;
379 Ok(Inst::Throw(val))
380 }
381 "phi" => {
382 let dst = as_var_id(&get_field(map, "dst")?)?;
383 let entries_val = get_field(map, "entries")?;
384 let entries_vec = as_vec(&entries_val)?;
385 let mut entries = Vec::with_capacity(entries_vec.len());
386 for e in &entries_vec {
387 let pair = as_vec(e)?;
388 if pair.len() != 2 {
389 return Err(ConvertError::TypeError(
390 "phi entry must be [block-id var-id]".into(),
391 ));
392 }
393 entries.push((as_block_id(&pair[0])?, as_var_id(&pair[1])?));
394 }
395 Ok(Inst::Phi(dst, entries))
396 }
397 "recur" => {
398 let args = as_var_id_vec(&get_field(map, "args")?)?;
399 Ok(Inst::Recur(args))
400 }
401 "source-loc" => {
402 let file = as_str(&get_field(map, "file")?)?;
403 let line = as_u32(&get_field(map, "line")?)?;
404 let col = as_u32(&get_field(map, "col")?)?;
405 Ok(Inst::SourceLoc(Span::new(
406 Arc::new(file.to_string()),
407 0,
408 0,
409 line,
410 col,
411 )))
412 }
413 "region-start" => {
414 let dst = as_var_id(&get_field(map, "dst")?)?;
415 Ok(Inst::RegionStart(dst))
416 }
417 "region-alloc" => {
418 let dst = as_var_id(&get_field(map, "dst")?)?;
419 let region = as_var_id(&get_field(map, "region")?)?;
420 let kind_kw = as_keyword_name(&get_field(map, "kind")?)?;
421 let kind = keyword_to_region_alloc_kind(&kind_kw).ok_or_else(|| {
422 ConvertError::UnknownVariant(format!("unknown RegionAllocKind: {kind_kw}"))
423 })?;
424 let operands = as_var_id_vec(&get_field(map, "operands")?)?;
425 Ok(Inst::RegionAlloc(dst, region, kind, operands))
426 }
427 "region-end" => {
428 let region = as_var_id(&get_field(map, "region")?)?;
429 Ok(Inst::RegionEnd(region))
430 }
431 other => Err(ConvertError::UnknownVariant(format!(
432 "unknown inst op: {other}"
433 ))),
434 }
435}
436
437fn value_to_terminator(val: &Value) -> ConvertResult<Terminator> {
439 let map = as_map(val)?;
440 let op = as_keyword_name(&get_field(map, "op")?)?;
441
442 match op.as_ref() {
443 "jump" => {
444 let target = as_block_id(&get_field(map, "target")?)?;
445 Ok(Terminator::Jump(target))
446 }
447 "branch" => {
448 let cond = as_var_id(&get_field(map, "cond")?)?;
449 let then_block = as_block_id(&get_field(map, "then-block")?)?;
450 let else_block = as_block_id(&get_field(map, "else-block")?)?;
451 Ok(Terminator::Branch {
452 cond,
453 then_block,
454 else_block,
455 })
456 }
457 "return" => {
458 let var = as_var_id(&get_field(map, "var")?)?;
459 Ok(Terminator::Return(var))
460 }
461 "recur-jump" => {
462 let target = as_block_id(&get_field(map, "target")?)?;
463 let args = as_var_id_vec(&get_field(map, "args")?)?;
464 Ok(Terminator::RecurJump { target, args })
465 }
466 "unreachable" => Ok(Terminator::Unreachable),
467 other => Err(ConvertError::UnknownVariant(format!(
468 "unknown terminator op: {other}"
469 ))),
470 }
471}
472
473fn value_to_const(val: &Value) -> ConvertResult<Const> {
475 let map = as_map(val)?;
476 let ty = as_keyword_name(&get_field(map, "type")?)?;
477
478 match ty.as_ref() {
479 "nil" => Ok(Const::Nil),
480 "bool" => {
481 let v = get_field(map, "val")?;
482 match v {
483 Value::Bool(b) => Ok(Const::Bool(b)),
484 _ => Err(ConvertError::TypeError(
485 "expected bool for :bool const".into(),
486 )),
487 }
488 }
489 "long" => {
490 let v = as_long(&get_field(map, "val")?)?;
491 Ok(Const::Long(v))
492 }
493 "double" => {
494 let v = get_field(map, "val")?;
495 match v {
496 Value::Double(d) => Ok(Const::Double(d)),
497 Value::Long(n) => Ok(Const::Double(n as f64)),
498 _ => Err(ConvertError::TypeError(
499 "expected double for :double const".into(),
500 )),
501 }
502 }
503 "string" => {
504 let v = as_str(&get_field(map, "val")?)?;
505 Ok(Const::Str(v))
506 }
507 "keyword" => {
508 let v = as_str(&get_field(map, "val")?)?;
509 Ok(Const::Keyword(v))
510 }
511 "symbol" => {
512 let v = as_str(&get_field(map, "val")?)?;
513 Ok(Const::Symbol(v))
514 }
515 "char" => {
516 let v = get_field(map, "val")?;
517 match v {
518 Value::Char(c) => Ok(Const::Char(c)),
519 _ => Err(ConvertError::TypeError(
520 "expected char for :char const".into(),
521 )),
522 }
523 }
524 other => Err(ConvertError::UnknownVariant(format!(
525 "unknown const type: {other}"
526 ))),
527 }
528}
529
530pub fn keyword_to_known_fn(kw: &str) -> Option<KnownFn> {
534 match kw {
535 "vector" => Some(KnownFn::Vector),
536 "hash-map" => Some(KnownFn::HashMap),
537 "hash-set" => Some(KnownFn::HashSet),
538 "list" => Some(KnownFn::List),
539 "assoc" => Some(KnownFn::Assoc),
540 "dissoc" => Some(KnownFn::Dissoc),
541 "conj" => Some(KnownFn::Conj),
542 "disj" => Some(KnownFn::Disj),
543 "get" => Some(KnownFn::Get),
544 "nth" => Some(KnownFn::Nth),
545 "count" => Some(KnownFn::Count),
546 "contains" => Some(KnownFn::Contains),
547 "transient" => Some(KnownFn::Transient),
548 "assoc!" => Some(KnownFn::AssocBang),
549 "conj!" => Some(KnownFn::ConjBang),
550 "persistent!" => Some(KnownFn::PersistentBang),
551 "first" => Some(KnownFn::First),
552 "rest" => Some(KnownFn::Rest),
553 "next" => Some(KnownFn::Next),
554 "cons" => Some(KnownFn::Cons),
555 "seq" => Some(KnownFn::Seq),
556 "lazy-seq" => Some(KnownFn::LazySeq),
557 "+" => Some(KnownFn::Add),
558 "-" => Some(KnownFn::Sub),
559 "*" => Some(KnownFn::Mul),
560 "/" => Some(KnownFn::Div),
561 "rem" => Some(KnownFn::Rem),
562 "=" => Some(KnownFn::Eq),
563 "<" => Some(KnownFn::Lt),
564 ">" => Some(KnownFn::Gt),
565 "<=" => Some(KnownFn::Lte),
566 ">=" => Some(KnownFn::Gte),
567 "nil?" => Some(KnownFn::IsNil),
568 "seq?" => Some(KnownFn::IsSeq),
569 "vector?" => Some(KnownFn::IsVector),
570 "map?" => Some(KnownFn::IsMap),
571 "identical?" => Some(KnownFn::Identical),
572 "str" => Some(KnownFn::Str),
573 "deref" => Some(KnownFn::Deref),
574 "println" => Some(KnownFn::Println),
575 "pr" => Some(KnownFn::Pr),
576 "atom-deref" => Some(KnownFn::AtomDeref),
577 "atom-reset" => Some(KnownFn::AtomReset),
578 "atom-swap" => Some(KnownFn::AtomSwap),
579 "apply" => Some(KnownFn::Apply),
580 "try-catch-finally" => Some(KnownFn::TryCatchFinally),
581 "set!-var" => Some(KnownFn::SetBangVar),
582 "with-bindings" => Some(KnownFn::WithBindings),
583 "with-out-str" => Some(KnownFn::WithOutStr),
584 "reduce2" => Some(KnownFn::Reduce2),
585 "reduce3" => Some(KnownFn::Reduce3),
586 "map" => Some(KnownFn::Map),
587 "filter" => Some(KnownFn::Filter),
588 "mapv" => Some(KnownFn::Mapv),
589 "filterv" => Some(KnownFn::Filterv),
590 "some" => Some(KnownFn::Some),
591 "every?" => Some(KnownFn::Every),
592 "into" => Some(KnownFn::Into),
593 "into3" => Some(KnownFn::Into3),
594 "group-by" => Some(KnownFn::GroupBy),
595 "partition2" => Some(KnownFn::Partition2),
596 "partition3" => Some(KnownFn::Partition3),
597 "partition4" => Some(KnownFn::Partition4),
598 "frequencies" => Some(KnownFn::Frequencies),
599 "keep" => Some(KnownFn::Keep),
600 "remove" => Some(KnownFn::Remove),
601 "map-indexed" => Some(KnownFn::MapIndexed),
602 "zipmap" => Some(KnownFn::Zipmap),
603 "juxt" => Some(KnownFn::Juxt),
604 "comp" => Some(KnownFn::Comp),
605 "partial" => Some(KnownFn::Partial),
606 "complement" => Some(KnownFn::Complement),
607 "concat" => Some(KnownFn::Concat),
608 "range1" => Some(KnownFn::Range1),
609 "range2" => Some(KnownFn::Range2),
610 "range3" => Some(KnownFn::Range3),
611 "take" => Some(KnownFn::Take),
612 "drop" => Some(KnownFn::Drop),
613 "reverse" => Some(KnownFn::Reverse),
614 "sort" => Some(KnownFn::Sort),
615 "sort-by" => Some(KnownFn::SortBy),
616 "keys" => Some(KnownFn::Keys),
617 "vals" => Some(KnownFn::Vals),
618 "merge" => Some(KnownFn::Merge),
619 "update" => Some(KnownFn::Update),
620 "get-in" => Some(KnownFn::GetIn),
621 "assoc-in" => Some(KnownFn::AssocIn),
622 "number?" => Some(KnownFn::IsNumber),
623 "string?" => Some(KnownFn::IsString),
624 "keyword?" => Some(KnownFn::IsKeyword),
625 "symbol?" => Some(KnownFn::IsSymbol),
626 "boolean?" => Some(KnownFn::IsBool),
627 "int?" => Some(KnownFn::IsInt),
628 "prn" => Some(KnownFn::Prn),
629 "print" => Some(KnownFn::Print),
630 "atom" => Some(KnownFn::Atom),
631 _ => None,
632 }
633}
634
635fn keyword_to_region_alloc_kind(kw: &str) -> Option<RegionAllocKind> {
637 match kw {
638 "vector" => Some(RegionAllocKind::Vector),
639 "map" => Some(RegionAllocKind::Map),
640 "set" => Some(RegionAllocKind::Set),
641 "list" => Some(RegionAllocKind::List),
642 "cons" => Some(RegionAllocKind::Cons),
643 _ => None,
644 }
645}
646
647fn as_var_id_vec(val: &Value) -> ConvertResult<Vec<VarId>> {
650 let vec = as_vec(val)?;
651 vec.iter().map(as_var_id).collect()
652}
653
654#[cfg(test)]
655mod tests {
656 use super::*;
657 use cljrs_value::Keyword;
658
659 fn kw(s: &str) -> Value {
660 Value::keyword(Keyword::simple(s))
661 }
662
663 fn make_map(pairs: Vec<(Value, Value)>) -> Value {
664 let mut m = MapValue::empty();
665 for (k, v) in pairs {
666 m = m.assoc(k, v);
667 }
668 Value::Map(m)
669 }
670
671 fn make_vec(items: Vec<Value>) -> Value {
672 use cljrs_gc::GcPtr;
673 use cljrs_value::collections::vector::PersistentVector;
674 Value::Vector(GcPtr::new(PersistentVector::from_iter(items)))
675 }
676
677 #[test]
678 fn test_const_nil() {
679 let val = make_map(vec![(kw("type"), kw("nil"))]);
680 let c = value_to_const(&val).unwrap();
681 assert!(matches!(c, Const::Nil));
682 }
683
684 #[test]
685 fn test_const_long() {
686 let val = make_map(vec![(kw("type"), kw("long")), (kw("val"), Value::Long(42))]);
687 let c = value_to_const(&val).unwrap();
688 assert!(matches!(c, Const::Long(42)));
689 }
690
691 #[test]
692 fn test_terminator_return() {
693 let val = make_map(vec![(kw("op"), kw("return")), (kw("var"), Value::Long(0))]);
694 let t = value_to_terminator(&val).unwrap();
695 assert!(matches!(t, Terminator::Return(VarId(0))));
696 }
697
698 #[test]
699 fn test_inst_const() {
700 let const_map = make_map(vec![(kw("type"), kw("long")), (kw("val"), Value::Long(42))]);
701 let val = make_map(vec![
702 (kw("op"), kw("const")),
703 (kw("dst"), Value::Long(0)),
704 (kw("value"), const_map),
705 ]);
706 let inst = value_to_inst(&val).unwrap();
707 assert!(matches!(inst, Inst::Const(VarId(0), Const::Long(42))));
708 }
709
710 #[test]
711 fn test_simple_ir_function() {
712 let const_val = make_map(vec![(kw("type"), kw("long")), (kw("val"), Value::Long(42))]);
714 let inst = make_map(vec![
715 (kw("op"), kw("const")),
716 (kw("dst"), Value::Long(1)),
717 (kw("value"), const_val),
718 ]);
719 let terminator = make_map(vec![(kw("op"), kw("return")), (kw("var"), Value::Long(1))]);
720 let block = make_map(vec![
721 (kw("id"), Value::Long(0)),
722 (kw("phis"), make_vec(vec![])),
723 (kw("insts"), make_vec(vec![inst])),
724 (kw("terminator"), terminator),
725 ]);
726 let param = make_vec(vec![Value::string("x".to_string()), Value::Long(0)]);
727 let ir_val = make_map(vec![
728 (kw("name"), Value::string("test".to_string())),
729 (kw("params"), make_vec(vec![param])),
730 (kw("blocks"), make_vec(vec![block])),
731 (kw("next-var"), Value::Long(2)),
732 (kw("next-block"), Value::Long(1)),
733 ]);
734
735 let ir = value_to_ir_function(&ir_val).unwrap();
736 assert_eq!(ir.name.as_deref(), Some("test"));
737 assert_eq!(ir.params.len(), 1);
738 assert_eq!(ir.params[0].0.as_ref(), "x");
739 assert_eq!(ir.blocks.len(), 1);
740 assert_eq!(ir.next_var, 2);
741 assert_eq!(ir.next_block, 1);
742 }
743}