1use std::collections::BTreeMap;
9use std::io::Write;
10use std::rc::Rc;
11
12use yulang_runtime as runtime;
13use yulang_runtime::runtime::list_tree::ListView;
14use yulang_typed_ir as typed_ir;
15
16pub const NATIVE_PRIMITIVE_BOOL_NOT: i64 = 1;
17pub const NATIVE_PRIMITIVE_INT_TO_STRING: i64 = 2;
18pub const NATIVE_PRIMITIVE_INT_TO_HEX: i64 = 3;
19pub const NATIVE_PRIMITIVE_INT_TO_UPPER_HEX: i64 = 4;
20pub const NATIVE_PRIMITIVE_FLOAT_TO_STRING: i64 = 5;
21pub const NATIVE_PRIMITIVE_BOOL_TO_STRING: i64 = 6;
22pub const NATIVE_PRIMITIVE_STRING_LEN: i64 = 7;
23
24pub const NATIVE_PRIMITIVE_BOOL_EQ: i64 = 101;
25pub const NATIVE_PRIMITIVE_INT_ADD: i64 = 102;
26pub const NATIVE_PRIMITIVE_INT_SUB: i64 = 103;
27pub const NATIVE_PRIMITIVE_INT_MUL: i64 = 104;
28pub const NATIVE_PRIMITIVE_INT_DIV: i64 = 105;
29pub const NATIVE_PRIMITIVE_INT_EQ: i64 = 106;
30pub const NATIVE_PRIMITIVE_INT_LT: i64 = 107;
31pub const NATIVE_PRIMITIVE_INT_LE: i64 = 108;
32pub const NATIVE_PRIMITIVE_INT_GT: i64 = 109;
33pub const NATIVE_PRIMITIVE_INT_GE: i64 = 110;
34pub const NATIVE_PRIMITIVE_FLOAT_ADD: i64 = 111;
35pub const NATIVE_PRIMITIVE_FLOAT_SUB: i64 = 112;
36pub const NATIVE_PRIMITIVE_FLOAT_MUL: i64 = 113;
37pub const NATIVE_PRIMITIVE_FLOAT_DIV: i64 = 114;
38pub const NATIVE_PRIMITIVE_FLOAT_EQ: i64 = 115;
39pub const NATIVE_PRIMITIVE_FLOAT_LT: i64 = 116;
40pub const NATIVE_PRIMITIVE_FLOAT_LE: i64 = 117;
41pub const NATIVE_PRIMITIVE_FLOAT_GT: i64 = 118;
42pub const NATIVE_PRIMITIVE_FLOAT_GE: i64 = 119;
43pub const NATIVE_PRIMITIVE_STRING_INDEX: i64 = 120;
44pub const NATIVE_PRIMITIVE_STRING_EQ: i64 = 121;
45
46#[derive(Default)]
47pub struct NativeRuntimeContext {
48 values: Vec<Box<runtime::VmValue>>,
49 closures: Vec<Box<NativeRuntimeClosure>>,
50}
51
52pub struct NativeRuntimeClosure {
53 target: i64,
54 environment: Vec<*mut runtime::VmValue>,
55}
56
57impl NativeRuntimeContext {
58 pub fn new() -> Self {
59 Self::default()
60 }
61
62 pub fn alloc(&mut self, value: runtime::VmValue) -> *mut runtime::VmValue {
63 self.values.push(Box::new(value));
64 self.values
65 .last_mut()
66 .map(|value| value.as_mut() as *mut runtime::VmValue)
67 .unwrap_or(std::ptr::null_mut())
68 }
69
70 pub fn alloc_closure(&mut self, target: i64) -> *mut NativeRuntimeClosure {
71 self.closures.push(Box::new(NativeRuntimeClosure {
72 target,
73 environment: Vec::new(),
74 }));
75 self.closures
76 .last_mut()
77 .map(|closure| closure.as_mut() as *mut NativeRuntimeClosure)
78 .unwrap_or(std::ptr::null_mut())
79 }
80
81 pub fn is_closure_handle(&self, value: *mut runtime::VmValue) -> bool {
82 let ptr = value.cast::<NativeRuntimeClosure>() as *const NativeRuntimeClosure;
83 self.closures
84 .iter()
85 .any(|closure| std::ptr::eq(closure.as_ref(), ptr))
86 }
87
88 pub fn clone_value(&self, value: *mut runtime::VmValue) -> Option<runtime::VmValue> {
89 if value.is_null() {
90 return None;
91 }
92 Some(unsafe { (*value).clone() })
93 }
94}
95
96pub fn make_int(context: &mut NativeRuntimeContext, bytes: &[u8]) -> Option<*mut runtime::VmValue> {
97 let text = std::str::from_utf8(bytes).ok()?;
98 Some(context.alloc(runtime::VmValue::Int(text.to_string())))
99}
100
101pub fn make_string(
102 context: &mut NativeRuntimeContext,
103 bytes: &[u8],
104) -> Option<*mut runtime::VmValue> {
105 let text = std::str::from_utf8(bytes).ok()?;
106 Some(context.alloc(runtime::VmValue::String(
107 runtime::runtime::string_tree::StringTree::from_str(text),
108 )))
109}
110
111pub fn make_float(
112 context: &mut NativeRuntimeContext,
113 bytes: &[u8],
114) -> Option<*mut runtime::VmValue> {
115 let text = std::str::from_utf8(bytes).ok()?;
116 Some(context.alloc(runtime::VmValue::Float(text.to_string())))
117}
118
119pub fn make_bool(context: &mut NativeRuntimeContext, value: bool) -> *mut runtime::VmValue {
120 context.alloc(runtime::VmValue::Bool(value))
121}
122
123pub fn bool_is_true(value: *mut runtime::VmValue) -> Option<i64> {
124 let value = unsafe { value.as_ref()? };
125 let runtime::VmValue::Bool(value) = value else {
126 return None;
127 };
128 Some(i64::from(*value))
129}
130
131pub fn value_eq(
132 context: &mut NativeRuntimeContext,
133 left: *mut runtime::VmValue,
134 right: *mut runtime::VmValue,
135) -> Option<*mut runtime::VmValue> {
136 let left = unsafe { left.as_ref()? };
137 let right = unsafe { right.as_ref()? };
138 Some(context.alloc(runtime::VmValue::Bool(vm_value_eq(left, right))))
139}
140
141pub fn bool_and(
142 context: &mut NativeRuntimeContext,
143 left: *mut runtime::VmValue,
144 right: *mut runtime::VmValue,
145) -> Option<*mut runtime::VmValue> {
146 let left = unsafe { left.as_ref()? };
147 let right = unsafe { right.as_ref()? };
148 Some(context.alloc(runtime::VmValue::Bool(
149 matches!(left, runtime::VmValue::Bool(true))
150 && matches!(right, runtime::VmValue::Bool(true)),
151 )))
152}
153
154pub fn make_unit(context: &mut NativeRuntimeContext) -> *mut runtime::VmValue {
155 context.alloc(runtime::VmValue::Unit)
156}
157
158pub fn closure_new(context: &mut NativeRuntimeContext, target: i64) -> *mut NativeRuntimeClosure {
159 context.alloc_closure(target)
160}
161
162pub fn closure_push_env(
163 closure: *mut NativeRuntimeClosure,
164 value: *mut runtime::VmValue,
165) -> Option<*mut NativeRuntimeClosure> {
166 let closure = unsafe { closure.as_mut()? };
167 if value.is_null() {
168 return None;
169 }
170 closure.environment.push(value);
171 Some(closure as *mut NativeRuntimeClosure)
172}
173
174pub fn closure_target_id(closure: *mut NativeRuntimeClosure) -> Option<i64> {
175 let closure = unsafe { closure.as_ref()? };
176 Some(closure.target)
177}
178
179pub fn closure_env_get(
180 closure: *mut NativeRuntimeClosure,
181 slot: usize,
182) -> Option<*mut runtime::VmValue> {
183 let closure = unsafe { closure.as_ref()? };
184 closure.environment.get(slot).copied()
185}
186
187pub fn concat_string(
188 context: &mut NativeRuntimeContext,
189 left: *mut runtime::VmValue,
190 right: *mut runtime::VmValue,
191) -> Option<*mut runtime::VmValue> {
192 let left = unsafe { left.as_ref()? };
193 let right = unsafe { right.as_ref()? };
194 let (runtime::VmValue::String(left), runtime::VmValue::String(right)) = (left, right) else {
195 return None;
196 };
197 Some(context.alloc(runtime::VmValue::String(
198 runtime::runtime::string_tree::StringTree::concat(left.clone(), right.clone()),
199 )))
200}
201
202pub fn primitive_unary(
203 context: &mut NativeRuntimeContext,
204 op: i64,
205 value: *mut runtime::VmValue,
206) -> Option<*mut runtime::VmValue> {
207 let value = unsafe { value.as_ref()? };
208 let result = match op {
209 NATIVE_PRIMITIVE_BOOL_NOT => runtime::VmValue::Bool(!bool_value(value)?),
210 NATIVE_PRIMITIVE_INT_TO_STRING => {
211 runtime::VmValue::String(string_tree_from(int_value(value)?.to_string()))
212 }
213 NATIVE_PRIMITIVE_INT_TO_HEX => {
214 runtime::VmValue::String(string_tree_from(format!("{:x}", int_value(value)?)))
215 }
216 NATIVE_PRIMITIVE_INT_TO_UPPER_HEX => {
217 runtime::VmValue::String(string_tree_from(format!("{:X}", int_value(value)?)))
218 }
219 NATIVE_PRIMITIVE_FLOAT_TO_STRING => {
220 runtime::VmValue::String(string_tree_from(format_float_value(float_value(value)?)))
221 }
222 NATIVE_PRIMITIVE_BOOL_TO_STRING => {
223 runtime::VmValue::String(string_tree_from(bool_value(value)?.to_string()))
224 }
225 NATIVE_PRIMITIVE_STRING_LEN => {
226 runtime::VmValue::Int(string_value(value)?.len().to_string())
227 }
228 _ => return None,
229 };
230 Some(context.alloc(result))
231}
232
233pub fn primitive_binary(
234 context: &mut NativeRuntimeContext,
235 op: i64,
236 left: *mut runtime::VmValue,
237 right: *mut runtime::VmValue,
238) -> Option<*mut runtime::VmValue> {
239 let left = unsafe { left.as_ref()? };
240 let right = unsafe { right.as_ref()? };
241 let result = match op {
242 NATIVE_PRIMITIVE_BOOL_EQ => runtime::VmValue::Bool(bool_value(left)? == bool_value(right)?),
243 NATIVE_PRIMITIVE_INT_ADD => {
244 runtime::VmValue::Int((int_value(left)? + int_value(right)?).to_string())
245 }
246 NATIVE_PRIMITIVE_INT_SUB => {
247 runtime::VmValue::Int((int_value(left)? - int_value(right)?).to_string())
248 }
249 NATIVE_PRIMITIVE_INT_MUL => {
250 runtime::VmValue::Int((int_value(left)? * int_value(right)?).to_string())
251 }
252 NATIVE_PRIMITIVE_INT_DIV => {
253 runtime::VmValue::Int((int_value(left)? / int_value(right)?).to_string())
254 }
255 NATIVE_PRIMITIVE_INT_EQ => runtime::VmValue::Bool(int_value(left)? == int_value(right)?),
256 NATIVE_PRIMITIVE_INT_LT => runtime::VmValue::Bool(int_value(left)? < int_value(right)?),
257 NATIVE_PRIMITIVE_INT_LE => runtime::VmValue::Bool(int_value(left)? <= int_value(right)?),
258 NATIVE_PRIMITIVE_INT_GT => runtime::VmValue::Bool(int_value(left)? > int_value(right)?),
259 NATIVE_PRIMITIVE_INT_GE => runtime::VmValue::Bool(int_value(left)? >= int_value(right)?),
260 NATIVE_PRIMITIVE_FLOAT_ADD => {
261 runtime::VmValue::Float(format_float_value(float_value(left)? + float_value(right)?))
262 }
263 NATIVE_PRIMITIVE_FLOAT_SUB => {
264 runtime::VmValue::Float(format_float_value(float_value(left)? - float_value(right)?))
265 }
266 NATIVE_PRIMITIVE_FLOAT_MUL => {
267 runtime::VmValue::Float(format_float_value(float_value(left)? * float_value(right)?))
268 }
269 NATIVE_PRIMITIVE_FLOAT_DIV => {
270 runtime::VmValue::Float(format_float_value(float_value(left)? / float_value(right)?))
271 }
272 NATIVE_PRIMITIVE_FLOAT_EQ => {
273 runtime::VmValue::Bool(float_value(left)? == float_value(right)?)
274 }
275 NATIVE_PRIMITIVE_FLOAT_LT => {
276 runtime::VmValue::Bool(float_value(left)? < float_value(right)?)
277 }
278 NATIVE_PRIMITIVE_FLOAT_LE => {
279 runtime::VmValue::Bool(float_value(left)? <= float_value(right)?)
280 }
281 NATIVE_PRIMITIVE_FLOAT_GT => {
282 runtime::VmValue::Bool(float_value(left)? > float_value(right)?)
283 }
284 NATIVE_PRIMITIVE_FLOAT_GE => {
285 runtime::VmValue::Bool(float_value(left)? >= float_value(right)?)
286 }
287 NATIVE_PRIMITIVE_STRING_INDEX => {
288 let index = usize::try_from(int_value(right)?).ok()?;
289 runtime::VmValue::String(string_tree_from(string_value(left)?.index(index)?))
290 }
291 NATIVE_PRIMITIVE_STRING_EQ => runtime::VmValue::Bool(
292 string_value(left)?.to_flat_string() == string_value(right)?.to_flat_string(),
293 ),
294 _ => return None,
295 };
296 Some(context.alloc(result))
297}
298
299pub fn list_empty(context: &mut NativeRuntimeContext) -> *mut runtime::VmValue {
300 context.alloc(runtime::VmValue::List(
301 runtime::runtime::list_tree::ListTree::empty(),
302 ))
303}
304
305pub fn list_singleton(
306 context: &mut NativeRuntimeContext,
307 value: *mut runtime::VmValue,
308) -> Option<*mut runtime::VmValue> {
309 let value = unsafe { value.as_ref()? };
310 Some(context.alloc(runtime::VmValue::List(
311 runtime::runtime::list_tree::ListTree::singleton(Rc::new(value.clone())),
312 )))
313}
314
315pub fn list_merge(
316 context: &mut NativeRuntimeContext,
317 left: *mut runtime::VmValue,
318 right: *mut runtime::VmValue,
319) -> Option<*mut runtime::VmValue> {
320 let left = unsafe { left.as_ref()? };
321 let right = unsafe { right.as_ref()? };
322 let (runtime::VmValue::List(left), runtime::VmValue::List(right)) = (left, right) else {
323 return None;
324 };
325 Some(context.alloc(runtime::VmValue::List(
326 runtime::runtime::list_tree::ListTree::concat(left.clone(), right.clone()),
327 )))
328}
329
330pub fn list_len(
331 context: &mut NativeRuntimeContext,
332 list: *mut runtime::VmValue,
333) -> Option<*mut runtime::VmValue> {
334 let list = unsafe { list.as_ref()? };
335 let runtime::VmValue::List(list) = list else {
336 return None;
337 };
338 Some(context.alloc(runtime::VmValue::Int(list.len().to_string())))
339}
340
341pub fn list_index(
342 context: &mut NativeRuntimeContext,
343 list: *mut runtime::VmValue,
344 index: *mut runtime::VmValue,
345) -> Option<*mut runtime::VmValue> {
346 let list = unsafe { list.as_ref()? };
347 let index = unsafe { index.as_ref()? };
348 let runtime::VmValue::List(list) = list else {
349 return None;
350 };
351 let runtime::VmValue::Int(index) = index else {
352 return None;
353 };
354 let index = index.parse::<usize>().ok()?;
355 let value = list.index(index)?;
356 Some(context.alloc(value.as_ref().clone()))
357}
358
359pub fn list_index_range(
360 context: &mut NativeRuntimeContext,
361 list: *mut runtime::VmValue,
362 range: *mut runtime::VmValue,
363) -> Option<*mut runtime::VmValue> {
364 let list = unsafe { list.as_ref()? };
365 let range = unsafe { range.as_ref()? };
366 let runtime::VmValue::List(list) = list else {
367 return None;
368 };
369 let (start, end) = normalized_int_range_value(range, list.len())?;
370 let value = list.index_range(start, end)?;
371 Some(context.alloc(runtime::VmValue::List(value)))
372}
373
374pub fn list_splice(
375 context: &mut NativeRuntimeContext,
376 list: *mut runtime::VmValue,
377 range: *mut runtime::VmValue,
378 insert: *mut runtime::VmValue,
379) -> Option<*mut runtime::VmValue> {
380 let list = unsafe { list.as_ref()? };
381 let range = unsafe { range.as_ref()? };
382 let insert = unsafe { insert.as_ref()? };
383 let runtime::VmValue::List(list) = list else {
384 return None;
385 };
386 let runtime::VmValue::List(insert) = insert else {
387 return None;
388 };
389 let (start, end) = normalized_int_range_value(range, list.len())?;
390 let value = list.splice(start, end, insert.clone())?;
391 Some(context.alloc(runtime::VmValue::List(value)))
392}
393
394pub fn list_index_range_raw(
395 context: &mut NativeRuntimeContext,
396 list: *mut runtime::VmValue,
397 start: *mut runtime::VmValue,
398 end: *mut runtime::VmValue,
399) -> Option<*mut runtime::VmValue> {
400 let list = unsafe { list.as_ref()? };
401 let start = unsafe { start.as_ref()? };
402 let end = unsafe { end.as_ref()? };
403 let runtime::VmValue::List(list) = list else {
404 return None;
405 };
406 let runtime::VmValue::Int(start) = start else {
407 return None;
408 };
409 let runtime::VmValue::Int(end) = end else {
410 return None;
411 };
412 let start = start.parse::<usize>().ok()?;
413 let end = end.parse::<usize>().ok()?;
414 let value = list.index_range(start, end)?;
415 Some(context.alloc(runtime::VmValue::List(value)))
416}
417
418pub fn list_splice_raw(
419 context: &mut NativeRuntimeContext,
420 list: *mut runtime::VmValue,
421 start: *mut runtime::VmValue,
422 end: *mut runtime::VmValue,
423 insert: *mut runtime::VmValue,
424) -> Option<*mut runtime::VmValue> {
425 let list = unsafe { list.as_ref()? };
426 let start = unsafe { start.as_ref()? };
427 let end = unsafe { end.as_ref()? };
428 let insert = unsafe { insert.as_ref()? };
429 let runtime::VmValue::List(list) = list else {
430 return None;
431 };
432 let runtime::VmValue::Int(start) = start else {
433 return None;
434 };
435 let runtime::VmValue::Int(end) = end else {
436 return None;
437 };
438 let runtime::VmValue::List(insert) = insert else {
439 return None;
440 };
441 let start = start.parse::<usize>().ok()?;
442 let end = end.parse::<usize>().ok()?;
443 let value = list.splice(start, end, insert.clone())?;
444 Some(context.alloc(runtime::VmValue::List(value)))
445}
446
447pub fn list_view_raw(
448 context: &mut NativeRuntimeContext,
449 list: *mut runtime::VmValue,
450) -> Option<*mut runtime::VmValue> {
451 let list = unsafe { list.as_ref()? };
452 let runtime::VmValue::List(list) = list else {
453 return None;
454 };
455 let value = match list.view() {
456 ListView::Empty => runtime::VmValue::Variant {
457 tag: typed_ir_name("empty"),
458 value: None,
459 },
460 ListView::Leaf(single) => runtime::VmValue::Variant {
461 tag: typed_ir_name("leaf"),
462 value: Some(Box::new((*single).clone())),
463 },
464 ListView::Node { left, right, .. } => runtime::VmValue::Variant {
465 tag: typed_ir_name("node"),
466 value: Some(Box::new(runtime::VmValue::Tuple(vec![
467 runtime::VmValue::List(left),
468 runtime::VmValue::List(right),
469 ]))),
470 },
471 };
472 Some(context.alloc(value))
473}
474
475pub fn string_index_range(
476 context: &mut NativeRuntimeContext,
477 text: *mut runtime::VmValue,
478 range: *mut runtime::VmValue,
479) -> Option<*mut runtime::VmValue> {
480 let text = unsafe { text.as_ref()? };
481 let range = unsafe { range.as_ref()? };
482 let text = string_value(text)?;
483 let (start, end) = normalized_int_range_value(range, text.len())?;
484 let value = text.index_range(start, end)?;
485 Some(context.alloc(runtime::VmValue::String(value)))
486}
487
488pub fn string_splice(
489 context: &mut NativeRuntimeContext,
490 text: *mut runtime::VmValue,
491 range: *mut runtime::VmValue,
492 insert: *mut runtime::VmValue,
493) -> Option<*mut runtime::VmValue> {
494 let text = unsafe { text.as_ref()? };
495 let range = unsafe { range.as_ref()? };
496 let insert = unsafe { insert.as_ref()? };
497 let text = string_value(text)?;
498 let insert = string_value(insert)?;
499 let (start, end) = normalized_int_range_value(range, text.len())?;
500 let value = text.splice(start, end, insert.clone())?;
501 Some(context.alloc(runtime::VmValue::String(value)))
502}
503
504pub fn string_index_range_raw(
505 context: &mut NativeRuntimeContext,
506 text: *mut runtime::VmValue,
507 start: *mut runtime::VmValue,
508 end: *mut runtime::VmValue,
509) -> Option<*mut runtime::VmValue> {
510 let text = unsafe { text.as_ref()? };
511 let start = unsafe { start.as_ref()? };
512 let end = unsafe { end.as_ref()? };
513 let text = string_value(text)?;
514 let runtime::VmValue::Int(start) = start else {
515 return None;
516 };
517 let runtime::VmValue::Int(end) = end else {
518 return None;
519 };
520 let start = start.parse::<usize>().ok()?;
521 let end = end.parse::<usize>().ok()?;
522 let value = text.index_range(start, end)?;
523 Some(context.alloc(runtime::VmValue::String(value)))
524}
525
526pub fn string_splice_raw(
527 context: &mut NativeRuntimeContext,
528 text: *mut runtime::VmValue,
529 start: *mut runtime::VmValue,
530 end: *mut runtime::VmValue,
531 insert: *mut runtime::VmValue,
532) -> Option<*mut runtime::VmValue> {
533 let text = unsafe { text.as_ref()? };
534 let start = unsafe { start.as_ref()? };
535 let end = unsafe { end.as_ref()? };
536 let insert = unsafe { insert.as_ref()? };
537 let text = string_value(text)?;
538 let runtime::VmValue::Int(start) = start else {
539 return None;
540 };
541 let runtime::VmValue::Int(end) = end else {
542 return None;
543 };
544 let insert = string_value(insert)?;
545 let start = start.parse::<usize>().ok()?;
546 let end = end.parse::<usize>().ok()?;
547 let value = text.splice(start, end, insert.clone())?;
548 Some(context.alloc(runtime::VmValue::String(value)))
549}
550
551pub fn tuple_empty(context: &mut NativeRuntimeContext) -> *mut runtime::VmValue {
552 context.alloc(runtime::VmValue::Tuple(Vec::new()))
553}
554
555pub fn tuple_push(
556 context: &mut NativeRuntimeContext,
557 tuple: *mut runtime::VmValue,
558 value: *mut runtime::VmValue,
559) -> Option<*mut runtime::VmValue> {
560 let tuple = unsafe { tuple.as_ref()? };
561 let value = unsafe { value.as_ref()? };
562 let runtime::VmValue::Tuple(items) = tuple else {
563 return None;
564 };
565 let mut items = items.clone();
566 items.push(value.clone());
567 Some(context.alloc(runtime::VmValue::Tuple(items)))
568}
569
570pub fn tuple_get(
571 context: &mut NativeRuntimeContext,
572 tuple: *mut runtime::VmValue,
573 index: usize,
574) -> Option<*mut runtime::VmValue> {
575 let tuple = unsafe { tuple.as_ref()? };
576 let runtime::VmValue::Tuple(items) = tuple else {
577 return None;
578 };
579 Some(context.alloc(items.get(index)?.clone()))
580}
581
582pub fn record_empty(context: &mut NativeRuntimeContext) -> *mut runtime::VmValue {
583 context.alloc(runtime::VmValue::Record(BTreeMap::new()))
584}
585
586pub fn record_insert(
587 context: &mut NativeRuntimeContext,
588 record: *mut runtime::VmValue,
589 name: &[u8],
590 value: *mut runtime::VmValue,
591) -> Option<*mut runtime::VmValue> {
592 let record = unsafe { record.as_ref()? };
593 let value = unsafe { value.as_ref()? };
594 let runtime::VmValue::Record(fields) = record else {
595 return None;
596 };
597 let name = std::str::from_utf8(name).ok()?;
598 let mut fields = fields.clone();
599 fields.insert(typed_ir_name(name), value.clone());
600 Some(context.alloc(runtime::VmValue::Record(fields)))
601}
602
603pub fn record_select(
604 context: &mut NativeRuntimeContext,
605 record: *mut runtime::VmValue,
606 name: &[u8],
607) -> Option<*mut runtime::VmValue> {
608 let record = unsafe { record.as_ref()? };
609 let runtime::VmValue::Record(fields) = record else {
610 return None;
611 };
612 let name = std::str::from_utf8(name).ok()?;
613 let value = fields.get(&typed_ir_name(name))?;
614 Some(context.alloc(value.clone()))
615}
616
617pub fn record_without_field(
618 context: &mut NativeRuntimeContext,
619 record: *mut runtime::VmValue,
620 name: &[u8],
621) -> Option<*mut runtime::VmValue> {
622 let record = unsafe { record.as_ref()? };
623 let runtime::VmValue::Record(fields) = record else {
624 return None;
625 };
626 let name = std::str::from_utf8(name).ok()?;
627 let mut fields = fields.clone();
628 fields.remove(&typed_ir_name(name));
629 Some(context.alloc(runtime::VmValue::Record(fields)))
630}
631
632pub fn variant(
633 context: &mut NativeRuntimeContext,
634 tag: &[u8],
635 value: *mut runtime::VmValue,
636) -> Option<*mut runtime::VmValue> {
637 let tag = std::str::from_utf8(tag).ok()?;
638 let value = if value.is_null() {
639 None
640 } else {
641 Some(Box::new(unsafe { value.as_ref()? }.clone()))
642 };
643 Some(context.alloc(runtime::VmValue::Variant {
644 tag: typed_ir_name(tag),
645 value,
646 }))
647}
648
649pub fn variant_tag_eq(
650 context: &mut NativeRuntimeContext,
651 variant: *mut runtime::VmValue,
652 tag: &[u8],
653) -> Option<*mut runtime::VmValue> {
654 let variant = unsafe { variant.as_ref()? };
655 let runtime::VmValue::Variant {
656 tag: actual_tag, ..
657 } = variant
658 else {
659 return None;
660 };
661 let tag = std::str::from_utf8(tag).ok()?;
662 Some(context.alloc(runtime::VmValue::Bool(actual_tag.0 == tag)))
663}
664
665pub fn variant_payload(
666 context: &mut NativeRuntimeContext,
667 variant: *mut runtime::VmValue,
668) -> Option<*mut runtime::VmValue> {
669 let variant = unsafe { variant.as_ref()? };
670 let runtime::VmValue::Variant {
671 value: Some(payload),
672 ..
673 } = variant
674 else {
675 return None;
676 };
677 Some(context.alloc((**payload).clone()))
678}
679
680#[unsafe(no_mangle)]
681pub extern "C" fn yulang_native_context_new() -> *mut NativeRuntimeContext {
682 Box::into_raw(Box::new(NativeRuntimeContext::new()))
683}
684
685#[unsafe(no_mangle)]
686pub extern "C" fn yulang_native_context_free(context: *mut NativeRuntimeContext) {
687 if context.is_null() {
688 return;
689 }
690 unsafe {
691 drop(Box::from_raw(context));
692 }
693}
694
695#[unsafe(no_mangle)]
696pub extern "C" fn yulang_native_print_value(value: *mut runtime::VmValue) {
697 if value.is_null() {
698 print!("<null>");
699 flush_stdout();
700 return;
701 }
702 let value = unsafe { &*value };
703 print_native_value(value);
704 flush_stdout();
705}
706
707#[unsafe(no_mangle)]
708pub extern "C" fn yulang_native_make_int(
709 context: *mut NativeRuntimeContext,
710 ptr: *const u8,
711 len: usize,
712) -> *mut runtime::VmValue {
713 let Some(context) = (unsafe { context.as_mut() }) else {
714 return std::ptr::null_mut();
715 };
716 let Some(bytes) = bytes_from_raw(ptr, len) else {
717 return std::ptr::null_mut();
718 };
719 make_int(context, bytes).unwrap_or(std::ptr::null_mut())
720}
721
722#[unsafe(no_mangle)]
723pub extern "C" fn yulang_native_make_string(
724 context: *mut NativeRuntimeContext,
725 ptr: *const u8,
726 len: usize,
727) -> *mut runtime::VmValue {
728 let Some(context) = (unsafe { context.as_mut() }) else {
729 return std::ptr::null_mut();
730 };
731 let Some(bytes) = bytes_from_raw(ptr, len) else {
732 return std::ptr::null_mut();
733 };
734 make_string(context, bytes).unwrap_or(std::ptr::null_mut())
735}
736
737#[unsafe(no_mangle)]
738pub extern "C" fn yulang_native_make_float(
739 context: *mut NativeRuntimeContext,
740 ptr: *const u8,
741 len: usize,
742) -> *mut runtime::VmValue {
743 let Some(context) = (unsafe { context.as_mut() }) else {
744 return std::ptr::null_mut();
745 };
746 let Some(bytes) = bytes_from_raw(ptr, len) else {
747 return std::ptr::null_mut();
748 };
749 make_float(context, bytes).unwrap_or(std::ptr::null_mut())
750}
751
752#[unsafe(no_mangle)]
753pub extern "C" fn yulang_native_make_bool(
754 context: *mut NativeRuntimeContext,
755 value: i64,
756) -> *mut runtime::VmValue {
757 let Some(context) = (unsafe { context.as_mut() }) else {
758 return std::ptr::null_mut();
759 };
760 make_bool(context, value != 0)
761}
762
763#[unsafe(no_mangle)]
764pub extern "C" fn yulang_native_bool_is_true(value: *mut runtime::VmValue) -> i64 {
765 bool_is_true(value).unwrap_or(0)
766}
767
768#[unsafe(no_mangle)]
769pub extern "C" fn yulang_native_value_eq(
770 context: *mut NativeRuntimeContext,
771 left: *mut runtime::VmValue,
772 right: *mut runtime::VmValue,
773) -> *mut runtime::VmValue {
774 let Some(context) = (unsafe { context.as_mut() }) else {
775 return std::ptr::null_mut();
776 };
777 value_eq(context, left, right).unwrap_or(std::ptr::null_mut())
778}
779
780#[unsafe(no_mangle)]
781pub extern "C" fn yulang_native_bool_and(
782 context: *mut NativeRuntimeContext,
783 left: *mut runtime::VmValue,
784 right: *mut runtime::VmValue,
785) -> *mut runtime::VmValue {
786 let Some(context) = (unsafe { context.as_mut() }) else {
787 return std::ptr::null_mut();
788 };
789 bool_and(context, left, right).unwrap_or(std::ptr::null_mut())
790}
791
792#[unsafe(no_mangle)]
793pub extern "C" fn yulang_native_make_unit(
794 context: *mut NativeRuntimeContext,
795) -> *mut runtime::VmValue {
796 let Some(context) = (unsafe { context.as_mut() }) else {
797 return std::ptr::null_mut();
798 };
799 make_unit(context)
800}
801
802#[unsafe(no_mangle)]
803pub extern "C" fn yulang_native_closure_new(
804 context: *mut NativeRuntimeContext,
805 target: i64,
806) -> *mut NativeRuntimeClosure {
807 let Some(context) = (unsafe { context.as_mut() }) else {
808 return std::ptr::null_mut();
809 };
810 closure_new(context, target)
811}
812
813#[unsafe(no_mangle)]
814pub extern "C" fn yulang_native_closure_push_env(
815 closure: *mut NativeRuntimeClosure,
816 value: *mut runtime::VmValue,
817) -> *mut NativeRuntimeClosure {
818 closure_push_env(closure, value).unwrap_or(std::ptr::null_mut())
819}
820
821#[unsafe(no_mangle)]
822pub extern "C" fn yulang_native_closure_target_id(closure: *mut NativeRuntimeClosure) -> i64 {
823 closure_target_id(closure).unwrap_or(-1)
824}
825
826#[unsafe(no_mangle)]
827pub extern "C" fn yulang_native_closure_env_get(
828 closure: *mut NativeRuntimeClosure,
829 slot: usize,
830) -> *mut runtime::VmValue {
831 closure_env_get(closure, slot).unwrap_or(std::ptr::null_mut())
832}
833
834#[unsafe(no_mangle)]
835pub extern "C" fn yulang_native_concat_string(
836 context: *mut NativeRuntimeContext,
837 left: *mut runtime::VmValue,
838 right: *mut runtime::VmValue,
839) -> *mut runtime::VmValue {
840 let Some(context) = (unsafe { context.as_mut() }) else {
841 return std::ptr::null_mut();
842 };
843 concat_string(context, left, right).unwrap_or(std::ptr::null_mut())
844}
845
846#[unsafe(no_mangle)]
847pub extern "C" fn yulang_native_primitive_unary(
848 context: *mut NativeRuntimeContext,
849 op: i64,
850 value: *mut runtime::VmValue,
851) -> *mut runtime::VmValue {
852 let Some(context) = (unsafe { context.as_mut() }) else {
853 return std::ptr::null_mut();
854 };
855 primitive_unary(context, op, value).unwrap_or(std::ptr::null_mut())
856}
857
858#[unsafe(no_mangle)]
859pub extern "C" fn yulang_native_primitive_binary(
860 context: *mut NativeRuntimeContext,
861 op: i64,
862 left: *mut runtime::VmValue,
863 right: *mut runtime::VmValue,
864) -> *mut runtime::VmValue {
865 let Some(context) = (unsafe { context.as_mut() }) else {
866 return std::ptr::null_mut();
867 };
868 primitive_binary(context, op, left, right).unwrap_or(std::ptr::null_mut())
869}
870
871#[unsafe(no_mangle)]
872pub extern "C" fn yulang_native_list_empty(
873 context: *mut NativeRuntimeContext,
874) -> *mut runtime::VmValue {
875 let Some(context) = (unsafe { context.as_mut() }) else {
876 return std::ptr::null_mut();
877 };
878 list_empty(context)
879}
880
881#[unsafe(no_mangle)]
882pub extern "C" fn yulang_native_list_singleton(
883 context: *mut NativeRuntimeContext,
884 value: *mut runtime::VmValue,
885) -> *mut runtime::VmValue {
886 let Some(context) = (unsafe { context.as_mut() }) else {
887 return std::ptr::null_mut();
888 };
889 list_singleton(context, value).unwrap_or(std::ptr::null_mut())
890}
891
892#[unsafe(no_mangle)]
893pub extern "C" fn yulang_native_list_merge(
894 context: *mut NativeRuntimeContext,
895 left: *mut runtime::VmValue,
896 right: *mut runtime::VmValue,
897) -> *mut runtime::VmValue {
898 let Some(context) = (unsafe { context.as_mut() }) else {
899 return std::ptr::null_mut();
900 };
901 list_merge(context, left, right).unwrap_or(std::ptr::null_mut())
902}
903
904#[unsafe(no_mangle)]
905pub extern "C" fn yulang_native_list_len(
906 context: *mut NativeRuntimeContext,
907 list: *mut runtime::VmValue,
908) -> *mut runtime::VmValue {
909 let Some(context) = (unsafe { context.as_mut() }) else {
910 return std::ptr::null_mut();
911 };
912 list_len(context, list).unwrap_or(std::ptr::null_mut())
913}
914
915#[unsafe(no_mangle)]
916pub extern "C" fn yulang_native_list_index(
917 context: *mut NativeRuntimeContext,
918 list: *mut runtime::VmValue,
919 index: *mut runtime::VmValue,
920) -> *mut runtime::VmValue {
921 let Some(context) = (unsafe { context.as_mut() }) else {
922 return std::ptr::null_mut();
923 };
924 list_index(context, list, index).unwrap_or(std::ptr::null_mut())
925}
926
927#[unsafe(no_mangle)]
928pub extern "C" fn yulang_native_list_index_range(
929 context: *mut NativeRuntimeContext,
930 list: *mut runtime::VmValue,
931 range: *mut runtime::VmValue,
932) -> *mut runtime::VmValue {
933 let Some(context) = (unsafe { context.as_mut() }) else {
934 return std::ptr::null_mut();
935 };
936 list_index_range(context, list, range).unwrap_or(std::ptr::null_mut())
937}
938
939#[unsafe(no_mangle)]
940pub extern "C" fn yulang_native_list_splice(
941 context: *mut NativeRuntimeContext,
942 list: *mut runtime::VmValue,
943 range: *mut runtime::VmValue,
944 insert: *mut runtime::VmValue,
945) -> *mut runtime::VmValue {
946 let Some(context) = (unsafe { context.as_mut() }) else {
947 return std::ptr::null_mut();
948 };
949 list_splice(context, list, range, insert).unwrap_or(std::ptr::null_mut())
950}
951
952#[unsafe(no_mangle)]
953pub extern "C" fn yulang_native_list_index_range_raw(
954 context: *mut NativeRuntimeContext,
955 list: *mut runtime::VmValue,
956 start: *mut runtime::VmValue,
957 end: *mut runtime::VmValue,
958) -> *mut runtime::VmValue {
959 let Some(context) = (unsafe { context.as_mut() }) else {
960 return std::ptr::null_mut();
961 };
962 list_index_range_raw(context, list, start, end).unwrap_or(std::ptr::null_mut())
963}
964
965#[unsafe(no_mangle)]
966pub extern "C" fn yulang_native_list_splice_raw(
967 context: *mut NativeRuntimeContext,
968 list: *mut runtime::VmValue,
969 start: *mut runtime::VmValue,
970 end: *mut runtime::VmValue,
971 insert: *mut runtime::VmValue,
972) -> *mut runtime::VmValue {
973 let Some(context) = (unsafe { context.as_mut() }) else {
974 return std::ptr::null_mut();
975 };
976 list_splice_raw(context, list, start, end, insert).unwrap_or(std::ptr::null_mut())
977}
978
979#[unsafe(no_mangle)]
980pub extern "C" fn yulang_native_list_view_raw(
981 context: *mut NativeRuntimeContext,
982 list: *mut runtime::VmValue,
983) -> *mut runtime::VmValue {
984 let Some(context) = (unsafe { context.as_mut() }) else {
985 return std::ptr::null_mut();
986 };
987 list_view_raw(context, list).unwrap_or(std::ptr::null_mut())
988}
989
990#[unsafe(no_mangle)]
991pub extern "C" fn yulang_native_string_index_range(
992 context: *mut NativeRuntimeContext,
993 text: *mut runtime::VmValue,
994 range: *mut runtime::VmValue,
995) -> *mut runtime::VmValue {
996 let Some(context) = (unsafe { context.as_mut() }) else {
997 return std::ptr::null_mut();
998 };
999 string_index_range(context, text, range).unwrap_or(std::ptr::null_mut())
1000}
1001
1002#[unsafe(no_mangle)]
1003pub extern "C" fn yulang_native_string_splice(
1004 context: *mut NativeRuntimeContext,
1005 text: *mut runtime::VmValue,
1006 range: *mut runtime::VmValue,
1007 insert: *mut runtime::VmValue,
1008) -> *mut runtime::VmValue {
1009 let Some(context) = (unsafe { context.as_mut() }) else {
1010 return std::ptr::null_mut();
1011 };
1012 string_splice(context, text, range, insert).unwrap_or(std::ptr::null_mut())
1013}
1014
1015#[unsafe(no_mangle)]
1016pub extern "C" fn yulang_native_string_index_range_raw(
1017 context: *mut NativeRuntimeContext,
1018 text: *mut runtime::VmValue,
1019 start: *mut runtime::VmValue,
1020 end: *mut runtime::VmValue,
1021) -> *mut runtime::VmValue {
1022 let Some(context) = (unsafe { context.as_mut() }) else {
1023 return std::ptr::null_mut();
1024 };
1025 string_index_range_raw(context, text, start, end).unwrap_or(std::ptr::null_mut())
1026}
1027
1028#[unsafe(no_mangle)]
1029pub extern "C" fn yulang_native_string_splice_raw(
1030 context: *mut NativeRuntimeContext,
1031 text: *mut runtime::VmValue,
1032 start: *mut runtime::VmValue,
1033 end: *mut runtime::VmValue,
1034 insert: *mut runtime::VmValue,
1035) -> *mut runtime::VmValue {
1036 let Some(context) = (unsafe { context.as_mut() }) else {
1037 return std::ptr::null_mut();
1038 };
1039 string_splice_raw(context, text, start, end, insert).unwrap_or(std::ptr::null_mut())
1040}
1041
1042#[unsafe(no_mangle)]
1043pub extern "C" fn yulang_native_tuple_empty(
1044 context: *mut NativeRuntimeContext,
1045) -> *mut runtime::VmValue {
1046 let Some(context) = (unsafe { context.as_mut() }) else {
1047 return std::ptr::null_mut();
1048 };
1049 tuple_empty(context)
1050}
1051
1052#[unsafe(no_mangle)]
1053pub extern "C" fn yulang_native_tuple_push(
1054 context: *mut NativeRuntimeContext,
1055 tuple: *mut runtime::VmValue,
1056 value: *mut runtime::VmValue,
1057) -> *mut runtime::VmValue {
1058 let Some(context) = (unsafe { context.as_mut() }) else {
1059 return std::ptr::null_mut();
1060 };
1061 tuple_push(context, tuple, value).unwrap_or(std::ptr::null_mut())
1062}
1063
1064#[unsafe(no_mangle)]
1065pub extern "C" fn yulang_native_tuple_get(
1066 context: *mut NativeRuntimeContext,
1067 tuple: *mut runtime::VmValue,
1068 index: usize,
1069) -> *mut runtime::VmValue {
1070 let Some(context) = (unsafe { context.as_mut() }) else {
1071 return std::ptr::null_mut();
1072 };
1073 tuple_get(context, tuple, index).unwrap_or(std::ptr::null_mut())
1074}
1075
1076#[unsafe(no_mangle)]
1077pub extern "C" fn yulang_native_record_empty(
1078 context: *mut NativeRuntimeContext,
1079) -> *mut runtime::VmValue {
1080 let Some(context) = (unsafe { context.as_mut() }) else {
1081 return std::ptr::null_mut();
1082 };
1083 record_empty(context)
1084}
1085
1086#[unsafe(no_mangle)]
1087pub extern "C" fn yulang_native_record_insert(
1088 context: *mut NativeRuntimeContext,
1089 record: *mut runtime::VmValue,
1090 name_ptr: *const u8,
1091 name_len: usize,
1092 value: *mut runtime::VmValue,
1093) -> *mut runtime::VmValue {
1094 let Some(context) = (unsafe { context.as_mut() }) else {
1095 return std::ptr::null_mut();
1096 };
1097 let Some(name) = bytes_from_raw(name_ptr, name_len) else {
1098 return std::ptr::null_mut();
1099 };
1100 record_insert(context, record, name, value).unwrap_or(std::ptr::null_mut())
1101}
1102
1103#[unsafe(no_mangle)]
1104pub extern "C" fn yulang_native_record_select(
1105 context: *mut NativeRuntimeContext,
1106 record: *mut runtime::VmValue,
1107 name_ptr: *const u8,
1108 name_len: usize,
1109) -> *mut runtime::VmValue {
1110 let Some(context) = (unsafe { context.as_mut() }) else {
1111 return std::ptr::null_mut();
1112 };
1113 let Some(name) = bytes_from_raw(name_ptr, name_len) else {
1114 return std::ptr::null_mut();
1115 };
1116 record_select(context, record, name).unwrap_or(std::ptr::null_mut())
1117}
1118
1119#[unsafe(no_mangle)]
1120pub extern "C" fn yulang_native_record_without_field(
1121 context: *mut NativeRuntimeContext,
1122 record: *mut runtime::VmValue,
1123 name_ptr: *const u8,
1124 name_len: usize,
1125) -> *mut runtime::VmValue {
1126 let Some(context) = (unsafe { context.as_mut() }) else {
1127 return std::ptr::null_mut();
1128 };
1129 let Some(name) = bytes_from_raw(name_ptr, name_len) else {
1130 return std::ptr::null_mut();
1131 };
1132 record_without_field(context, record, name).unwrap_or(std::ptr::null_mut())
1133}
1134
1135#[unsafe(no_mangle)]
1136pub extern "C" fn yulang_native_variant(
1137 context: *mut NativeRuntimeContext,
1138 tag_ptr: *const u8,
1139 tag_len: usize,
1140 value: *mut runtime::VmValue,
1141) -> *mut runtime::VmValue {
1142 let Some(context) = (unsafe { context.as_mut() }) else {
1143 return std::ptr::null_mut();
1144 };
1145 let Some(tag) = bytes_from_raw(tag_ptr, tag_len) else {
1146 return std::ptr::null_mut();
1147 };
1148 variant(context, tag, value).unwrap_or(std::ptr::null_mut())
1149}
1150
1151#[unsafe(no_mangle)]
1152pub extern "C" fn yulang_native_variant_tag_eq(
1153 context: *mut NativeRuntimeContext,
1154 variant: *mut runtime::VmValue,
1155 tag_ptr: *const u8,
1156 tag_len: usize,
1157) -> *mut runtime::VmValue {
1158 let Some(context) = (unsafe { context.as_mut() }) else {
1159 return std::ptr::null_mut();
1160 };
1161 let Some(tag) = bytes_from_raw(tag_ptr, tag_len) else {
1162 return std::ptr::null_mut();
1163 };
1164 variant_tag_eq(context, variant, tag).unwrap_or(std::ptr::null_mut())
1165}
1166
1167#[unsafe(no_mangle)]
1168pub extern "C" fn yulang_native_variant_payload(
1169 context: *mut NativeRuntimeContext,
1170 variant: *mut runtime::VmValue,
1171) -> *mut runtime::VmValue {
1172 let Some(context) = (unsafe { context.as_mut() }) else {
1173 return std::ptr::null_mut();
1174 };
1175 variant_payload(context, variant).unwrap_or(std::ptr::null_mut())
1176}
1177
1178fn print_native_value(value: &runtime::VmValue) {
1179 match value {
1180 runtime::VmValue::Int(value) | runtime::VmValue::Float(value) => print!("{value}"),
1181 runtime::VmValue::String(value) => print!("{}", value.to_flat_string()),
1182 runtime::VmValue::Bool(value) => print!("{value}"),
1183 runtime::VmValue::Unit => print!("()"),
1184 runtime::VmValue::List(value) => {
1185 print!("[");
1186 for (index, item) in value.to_vec().into_iter().enumerate() {
1187 if index > 0 {
1188 print!(", ");
1189 }
1190 print_native_value(item.as_ref());
1191 }
1192 print!("]");
1193 }
1194 runtime::VmValue::Tuple(items) => {
1195 print!("(");
1196 for (index, item) in items.iter().enumerate() {
1197 if index > 0 {
1198 print!(", ");
1199 }
1200 print_native_value(item);
1201 }
1202 print!(")");
1203 }
1204 runtime::VmValue::Record(fields) => {
1205 print!("{{");
1206 for (index, (name, value)) in fields.iter().enumerate() {
1207 if index > 0 {
1208 print!(", ");
1209 }
1210 print!("{} = ", name.0);
1211 print_native_value(value);
1212 }
1213 print!("}}");
1214 }
1215 runtime::VmValue::Variant { tag, value } => {
1216 print!(":{}", tag.0);
1217 if let Some(value) = value {
1218 print!(" ");
1219 print_native_value(value);
1220 }
1221 }
1222 other => print!("{other:?}"),
1223 }
1224}
1225
1226fn typed_ir_name(name: &str) -> typed_ir::Name {
1227 typed_ir::Name(name.to_string())
1228}
1229
1230fn normalized_int_range_value(value: &runtime::VmValue, len: usize) -> Option<(usize, usize)> {
1231 let runtime::VmValue::Variant { tag, value } = value else {
1232 return None;
1233 };
1234 if tag.0 != "within" {
1235 return None;
1236 }
1237 let value = value.as_ref()?;
1238 let runtime::VmValue::Tuple(items) = value.as_ref() else {
1239 return None;
1240 };
1241 let [start, end] = items.as_slice() else {
1242 return None;
1243 };
1244 let start = normalized_start_bound_value(start)?;
1245 let end = normalized_end_bound_value(end, len)?;
1246 (start <= end && end <= len).then_some((start, end))
1247}
1248
1249fn normalized_start_bound_value(value: &runtime::VmValue) -> Option<usize> {
1250 let runtime::VmValue::Variant { tag, value } = value else {
1251 return None;
1252 };
1253 match tag.0.as_str() {
1254 "unbounded" => Some(0),
1255 "included" => int_variant_payload(value).and_then(|value| usize::try_from(value).ok()),
1256 "excluded" => int_variant_payload(value).and_then(|value| usize::try_from(value + 1).ok()),
1257 _ => None,
1258 }
1259}
1260
1261fn normalized_end_bound_value(value: &runtime::VmValue, len: usize) -> Option<usize> {
1262 let runtime::VmValue::Variant { tag, value } = value else {
1263 return None;
1264 };
1265 match tag.0.as_str() {
1266 "unbounded" => Some(len),
1267 "included" => int_variant_payload(value).and_then(|value| usize::try_from(value + 1).ok()),
1268 "excluded" => int_variant_payload(value).and_then(|value| usize::try_from(value).ok()),
1269 _ => None,
1270 }
1271}
1272
1273fn int_variant_payload(value: &Option<Box<runtime::VmValue>>) -> Option<i64> {
1274 int_value(value.as_ref()?)
1275}
1276
1277fn int_value(value: &runtime::VmValue) -> Option<i64> {
1278 let runtime::VmValue::Int(value) = value else {
1279 return None;
1280 };
1281 value.parse().ok()
1282}
1283
1284fn float_value(value: &runtime::VmValue) -> Option<f64> {
1285 let runtime::VmValue::Float(value) = value else {
1286 return None;
1287 };
1288 value.parse().ok()
1289}
1290
1291fn bool_value(value: &runtime::VmValue) -> Option<bool> {
1292 let runtime::VmValue::Bool(value) = value else {
1293 return None;
1294 };
1295 Some(*value)
1296}
1297
1298fn vm_value_eq(left: &runtime::VmValue, right: &runtime::VmValue) -> bool {
1299 match (left, right) {
1300 (runtime::VmValue::Int(left), runtime::VmValue::Int(right))
1301 | (runtime::VmValue::Float(left), runtime::VmValue::Float(right)) => left == right,
1302 (runtime::VmValue::String(left), runtime::VmValue::String(right)) => {
1303 left.to_flat_string() == right.to_flat_string()
1304 }
1305 (runtime::VmValue::Bool(left), runtime::VmValue::Bool(right)) => left == right,
1306 (runtime::VmValue::Unit, runtime::VmValue::Unit) => true,
1307 (runtime::VmValue::Tuple(left), runtime::VmValue::Tuple(right)) => {
1308 left.len() == right.len()
1309 && left
1310 .iter()
1311 .zip(right)
1312 .all(|(left, right)| vm_value_eq(left, right))
1313 }
1314 (runtime::VmValue::Record(left), runtime::VmValue::Record(right)) => {
1315 left.len() == right.len()
1316 && left.iter().all(|(name, left)| {
1317 right
1318 .get(name)
1319 .is_some_and(|right| vm_value_eq(left, right))
1320 })
1321 }
1322 (
1323 runtime::VmValue::Variant {
1324 tag: left_tag,
1325 value: left_value,
1326 },
1327 runtime::VmValue::Variant {
1328 tag: right_tag,
1329 value: right_value,
1330 },
1331 ) => {
1332 left_tag == right_tag
1333 && match (left_value, right_value) {
1334 (Some(left), Some(right)) => vm_value_eq(left, right),
1335 (None, None) => true,
1336 _ => false,
1337 }
1338 }
1339 (runtime::VmValue::List(left), runtime::VmValue::List(right)) => {
1340 let left = left.to_vec();
1341 let right = right.to_vec();
1342 left.len() == right.len()
1343 && left
1344 .iter()
1345 .zip(&right)
1346 .all(|(left, right)| vm_value_eq(left, right))
1347 }
1348 _ => left == right,
1349 }
1350}
1351
1352fn string_value(value: &runtime::VmValue) -> Option<&runtime::runtime::string_tree::StringTree> {
1353 let runtime::VmValue::String(value) = value else {
1354 return None;
1355 };
1356 Some(value)
1357}
1358
1359fn string_tree_from(value: impl Into<String>) -> runtime::runtime::string_tree::StringTree {
1360 runtime::runtime::string_tree::StringTree::from_str(&value.into())
1361}
1362
1363fn format_float_value(value: f64) -> String {
1364 let mut rendered = value.to_string();
1365 if !rendered.contains('.') && !rendered.contains('e') && !rendered.contains('E') {
1366 rendered.push_str(".0");
1367 }
1368 rendered
1369}
1370
1371fn flush_stdout() {
1372 let _ = std::io::stdout().flush();
1373}
1374
1375fn bytes_from_raw<'a>(ptr: *const u8, len: usize) -> Option<&'a [u8]> {
1376 if ptr.is_null() {
1377 return None;
1378 }
1379 Some(unsafe { std::slice::from_raw_parts(ptr, len) })
1380}
1381
1382#[cfg(test)]
1383mod tests {
1384 use super::*;
1385
1386 #[test]
1387 fn api_builds_string_concat() {
1388 let mut context = NativeRuntimeContext::new();
1389 let left = make_string(&mut context, b"yu").expect("left");
1390 let right = make_string(&mut context, b"lang").expect("right");
1391 let value = concat_string(&mut context, left, right).expect("concat");
1392
1393 let Some(runtime::VmValue::String(value)) = context.clone_value(value) else {
1394 panic!("expected string");
1395 };
1396 assert_eq!(value.to_flat_string(), "yulang");
1397 }
1398
1399 #[test]
1400 fn api_builds_shared_vm_list() {
1401 let mut context = NativeRuntimeContext::new();
1402 let one = make_int(&mut context, b"1").expect("one");
1403 let two = make_int(&mut context, b"2").expect("two");
1404 let left = list_singleton(&mut context, one).expect("left");
1405 let right = list_singleton(&mut context, two).expect("right");
1406 let value = list_merge(&mut context, left, right).expect("merge");
1407
1408 let Some(runtime::VmValue::List(value)) = context.clone_value(value) else {
1409 panic!("expected list");
1410 };
1411 let items = value
1412 .to_vec()
1413 .into_iter()
1414 .map(|value| match value.as_ref() {
1415 runtime::VmValue::Int(value) => value.clone(),
1416 other => panic!("expected int item, got {other:?}"),
1417 })
1418 .collect::<Vec<_>>();
1419 assert_eq!(items, ["1", "2"]);
1420 }
1421
1422 #[test]
1423 fn api_indexes_list_and_reports_length() {
1424 let mut context = NativeRuntimeContext::new();
1425 let one = make_int(&mut context, b"1").expect("one");
1426 let two = make_int(&mut context, b"2").expect("two");
1427 let left = list_singleton(&mut context, one).expect("left");
1428 let right = list_singleton(&mut context, two).expect("right");
1429 let list = list_merge(&mut context, left, right).expect("merge");
1430 let index = make_int(&mut context, b"1").expect("index");
1431
1432 let len = list_len(&mut context, list).expect("len");
1433 let item = list_index(&mut context, list, index).expect("index value");
1434
1435 assert!(matches!(
1436 context.clone_value(len),
1437 Some(runtime::VmValue::Int(value)) if value == "2"
1438 ));
1439 assert!(matches!(
1440 context.clone_value(item),
1441 Some(runtime::VmValue::Int(value)) if value == "2"
1442 ));
1443 }
1444
1445 #[test]
1446 fn api_indexes_list_range() {
1447 let mut context = NativeRuntimeContext::new();
1448 let one = make_int(&mut context, b"1").expect("one");
1449 let two = make_int(&mut context, b"2").expect("two");
1450 let three = make_int(&mut context, b"3").expect("three");
1451 let one_list = list_singleton(&mut context, one).expect("one list");
1452 let two_list = list_singleton(&mut context, two).expect("two list");
1453 let three_list = list_singleton(&mut context, three).expect("three list");
1454 let first = list_merge(&mut context, one_list, two_list).expect("first merge");
1455 let list = list_merge(&mut context, first, three_list).expect("second merge");
1456 let range = range_value(1, 3);
1457 let range = context.alloc(range);
1458
1459 let value = list_index_range(&mut context, list, range).expect("range");
1460
1461 let Some(runtime::VmValue::List(value)) = context.clone_value(value) else {
1462 panic!("expected list");
1463 };
1464 let items = value
1465 .to_vec()
1466 .into_iter()
1467 .map(|value| match value.as_ref() {
1468 runtime::VmValue::Int(value) => value.clone(),
1469 other => panic!("expected int item, got {other:?}"),
1470 })
1471 .collect::<Vec<_>>();
1472 assert_eq!(items, ["2", "3"]);
1473 }
1474
1475 #[test]
1476 fn api_splices_list_range() {
1477 let mut context = NativeRuntimeContext::new();
1478 let list = int_list(&mut context, ["1", "2", "3", "4"]);
1479 let insert = int_list(&mut context, ["9", "8"]);
1480 let range = context.alloc(range_value(1, 3));
1481
1482 let value = list_splice(&mut context, list, range, insert).expect("splice");
1483
1484 let Some(runtime::VmValue::List(value)) = context.clone_value(value) else {
1485 panic!("expected list");
1486 };
1487 let items = value
1488 .to_vec()
1489 .into_iter()
1490 .map(|value| match value.as_ref() {
1491 runtime::VmValue::Int(value) => value.clone(),
1492 other => panic!("expected int item, got {other:?}"),
1493 })
1494 .collect::<Vec<_>>();
1495 assert_eq!(items, ["1", "9", "8", "4"]);
1496 }
1497
1498 #[test]
1499 fn api_views_raw_list() {
1500 let mut context = NativeRuntimeContext::new();
1501 let empty = list_empty(&mut context);
1502 let leaf = int_list(&mut context, ["1"]);
1503 let node = int_list(&mut context, ["1", "2"]);
1504
1505 let empty = list_view_raw(&mut context, empty).expect("empty view");
1506 let leaf = list_view_raw(&mut context, leaf).expect("leaf view");
1507 let node = list_view_raw(&mut context, node).expect("node view");
1508
1509 assert!(matches!(
1510 context.clone_value(empty),
1511 Some(runtime::VmValue::Variant { tag, value: None }) if tag.0 == "empty"
1512 ));
1513 assert!(matches!(
1514 context.clone_value(leaf),
1515 Some(runtime::VmValue::Variant { tag, value: Some(value) })
1516 if tag.0 == "leaf" && matches!(value.as_ref(), runtime::VmValue::Int(value) if value == "1")
1517 ));
1518 assert!(matches!(
1519 context.clone_value(node),
1520 Some(runtime::VmValue::Variant { tag, value: Some(value) })
1521 if tag.0 == "node" && matches!(value.as_ref(), runtime::VmValue::Tuple(items) if items.len() == 2)
1522 ));
1523 }
1524
1525 #[test]
1526 fn api_indexes_and_splices_string_range() {
1527 let mut context = NativeRuntimeContext::new();
1528 let text = make_string(&mut context, "aćšz".as_bytes()).expect("text");
1529 let insert = make_string(&mut context, b"bc").expect("insert");
1530 let range = context.alloc(range_value(1, 3));
1531
1532 let indexed = string_index_range(&mut context, text, range).expect("index range");
1533 let spliced = string_splice(&mut context, text, range, insert).expect("splice");
1534
1535 assert!(matches!(
1536 context.clone_value(indexed),
1537 Some(runtime::VmValue::String(value)) if value.to_flat_string() == "ćš"
1538 ));
1539 assert!(matches!(
1540 context.clone_value(spliced),
1541 Some(runtime::VmValue::String(value)) if value.to_flat_string() == "abcz"
1542 ));
1543 }
1544
1545 #[test]
1546 fn api_runs_basic_primitives() {
1547 let mut context = NativeRuntimeContext::new();
1548 let one = make_int(&mut context, b"1").expect("one");
1549 let two = make_int(&mut context, b"2").expect("two");
1550 let sum = primitive_binary(&mut context, NATIVE_PRIMITIVE_INT_ADD, one, two).expect("sum");
1551 let lt = primitive_binary(&mut context, NATIVE_PRIMITIVE_INT_LT, one, two).expect("lt");
1552 let text =
1553 primitive_unary(&mut context, NATIVE_PRIMITIVE_INT_TO_STRING, sum).expect("text");
1554
1555 assert!(matches!(
1556 context.clone_value(sum),
1557 Some(runtime::VmValue::Int(value)) if value == "3"
1558 ));
1559 assert!(matches!(
1560 context.clone_value(lt),
1561 Some(runtime::VmValue::Bool(true))
1562 ));
1563 assert!(matches!(
1564 context.clone_value(text),
1565 Some(runtime::VmValue::String(value)) if value.to_flat_string() == "3"
1566 ));
1567 }
1568
1569 fn range_value(start: i64, end: i64) -> runtime::VmValue {
1570 runtime::VmValue::Variant {
1571 tag: typed_ir_name("within"),
1572 value: Some(Box::new(runtime::VmValue::Tuple(vec![
1573 runtime::VmValue::Variant {
1574 tag: typed_ir_name("included"),
1575 value: Some(Box::new(runtime::VmValue::Int(start.to_string()))),
1576 },
1577 runtime::VmValue::Variant {
1578 tag: typed_ir_name("excluded"),
1579 value: Some(Box::new(runtime::VmValue::Int(end.to_string()))),
1580 },
1581 ]))),
1582 }
1583 }
1584
1585 fn int_list<const N: usize>(
1586 context: &mut NativeRuntimeContext,
1587 values: [&'static str; N],
1588 ) -> *mut runtime::VmValue {
1589 let mut result = list_empty(context);
1590 for value in values {
1591 let value = make_int(context, value.as_bytes()).expect("int");
1592 let item = list_singleton(context, value).expect("singleton");
1593 result = list_merge(context, result, item).expect("merge");
1594 }
1595 result
1596 }
1597}