1use crate::cold_path;
2use crate::compiler::compiler_data::InstrSrc;
3use crate::compiler::compiler_data::Source;
4use crate::compiler::compiler_errors::error_cannot_find_dynlib_symbol;
5use crate::compiler::compiler_errors::error_cannot_load_dynlib;
6use crate::compiler::compiler_errors::error_cannot_push_type_to_array;
7use crate::compiler::compiler_errors::error_cannot_read_file;
8use crate::compiler::compiler_errors::error_conditional_expression_without_else;
9use crate::compiler::compiler_errors::error_division_by_zero;
10use crate::compiler::compiler_errors::error_duplicate_map_key;
11use crate::compiler::compiler_errors::error_invalid_index_type;
12use crate::compiler::compiler_errors::error_invalid_type;
13use crate::compiler::compiler_errors::error_map_diff_types;
14use crate::compiler::compiler_errors::error_not_literal_map_key;
15use crate::compiler::compiler_errors::error_range_invalid_type;
16use crate::compiler::compiler_errors::error_type_not_indexable;
17use crate::compiler::compiler_errors::error_unknown_namespace;
18use crate::data::NULL;
19use crate::errors::BLUE;
20use crate::errors::BOLD;
21use crate::errors::RED;
22use crate::errors::RESET;
23use crate::instr::LibFunc;
24use crate::parser;
25use crate::rt::TargetOs;
26use crate::rt::resolve_library_filename;
27use crate::vm::Pool;
28use crate::{data::Data, instr::Instr};
29use compiler_data::Ctx;
30use compiler_data::DynamicLibFn;
31use compiler_data::Dynamiclib;
32use compiler_data::EnumType;
33use compiler_data::EnumVariant;
34use compiler_data::FnSignature;
35use compiler_data::Function;
36use compiler_data::HostFnSig;
37use compiler_data::Pools;
38use compiler_data::State;
39use compiler_data::Struct;
40use compiler_data::Variable;
41use expr::Expr;
42use expr::Span;
43use expr::code_modifies_variable;
44use functions::handle_functions;
45use methods::handle_method_calls;
46use registers::move_reg_to_reg;
47use registers::move_to_id;
48use rustc_hash::FxHashMap;
49use rustc_hash::FxHashSet;
50use smol_strc::SmolStr;
51use smol_strc::ToSmolStr;
52use std::collections::HashMap;
53use std::hash::BuildHasherDefault;
54use std::hint::unreachable_unchecked;
55use std::path::{Path, PathBuf};
56use std::rc::Rc;
57use type_system::DataType;
58use type_system::TypeExpr;
59use type_system::check_if_returns_void;
60use type_system::collect_direct_fn_calls;
61use type_system::struct_field_type_matches;
62
63#[cfg(not(target_arch = "wasm32"))]
64use libloading::Library;
65
66#[cfg(target_arch = "wasm32")]
67use crate::errors::wasm_error;
68pub mod compiler_data;
69mod compiler_errors;
70pub mod type_system;
71
72pub mod expr;
73
74#[path = "functions/functions.rs"]
75mod functions;
76#[path = "functions/methods.rs"]
77mod methods;
78
79mod registers;
80
81pub trait UnwrapId {
82 fn unwrap_id(self) -> u16;
83}
84
85impl UnwrapId for Option<u16> {
86 #[inline(always)]
87 fn unwrap_id(self) -> u16 {
88 debug_assert!(self.is_some());
89 unsafe { self.unwrap_unchecked() }
90 }
91}
92
93fn add_cmp_false(condition_id: u16, len: &mut u16, output: &mut Vec<Instr>, jmp_backwards: bool) {
95 if output.is_empty() {
96 return output.push(Instr::IsFalseJmp(condition_id, *len));
97 }
98 *output.last_mut().unwrap() = match *output.last().unwrap() {
99 Instr::InfFloat(o1, o2, o3) if o3 == condition_id => Instr::SupEqFloatJmp(o1, o2, *len),
100 Instr::InfInt(o1, o2, o3) if o3 == condition_id => Instr::SupEqIntJmp(o1, o2, *len),
101 Instr::InfEqFloat(o1, o2, o3) if o3 == condition_id => Instr::SupFloatJmp(o1, o2, *len),
102 Instr::InfEqInt(o1, o2, o3) if o3 == condition_id => Instr::SupIntJmp(o1, o2, *len),
103 Instr::SupFloat(o1, o2, o3) if o3 == condition_id => Instr::InfEqFloatJmp(o1, o2, *len),
104 Instr::SupInt(o1, o2, o3) if o3 == condition_id => Instr::InfEqIntJmp(o1, o2, *len),
105 Instr::SupEqFloat(o1, o2, o3) if o3 == condition_id => Instr::InfFloatJmp(o1, o2, *len),
106 Instr::SupEqInt(o1, o2, o3) if o3 == condition_id => Instr::InfIntJmp(o1, o2, *len),
107 Instr::Eq(o1, o2, o3) if o3 == condition_id => Instr::NotEqJmp(o1, o2, *len),
108 Instr::ObjEq(o1, o2, o3) if o3 == condition_id => Instr::ObjNotEqJmp(o1, o2, *len),
109 Instr::StrEq(o1, o2, o3) if o3 == condition_id => Instr::StrNotEqJmp(o1, o2, *len),
110 Instr::NotEq(o1, o2, o3) if o3 == condition_id => Instr::EqJmp(o1, o2, *len),
111 Instr::ObjNotEq(o1, o2, o3) if o3 == condition_id => Instr::ObjEqJmp(o1, o2, *len),
112 Instr::StrNotEq(o1, o2, o3) if o3 == condition_id => Instr::StrEqJmp(o1, o2, *len),
113 _ => {
114 output.push(Instr::IsFalseJmp(condition_id, *len));
115 return;
116 }
117 };
118 if jmp_backwards {
119 *len -= 1;
120 }
121}
122
123#[inline(always)]
125fn add_cmp_true(condition_id: u16, output: &mut Vec<Instr>) {
126 if output.is_empty() {
127 return output.push(Instr::IsTrueJmp(condition_id, 0));
128 }
129 let new_instr = match *output.last().unwrap() {
130 Instr::InfFloat(o1, o2, o3) if o3 == condition_id => Instr::InfFloatJmp(o1, o2, 0),
131 Instr::InfInt(o1, o2, o3) if o3 == condition_id => Instr::InfIntJmp(o1, o2, 0),
132 Instr::InfEqFloat(o1, o2, o3) if o3 == condition_id => Instr::InfEqFloatJmp(o1, o2, 0),
133 Instr::InfEqInt(o1, o2, o3) if o3 == condition_id => Instr::InfEqIntJmp(o1, o2, 0),
134 Instr::SupFloat(o1, o2, o3) if o3 == condition_id => Instr::SupFloatJmp(o1, o2, 0),
135 Instr::SupInt(o1, o2, o3) if o3 == condition_id => Instr::SupIntJmp(o1, o2, 0),
136 Instr::SupEqFloat(o1, o2, o3) if o3 == condition_id => Instr::SupEqFloatJmp(o1, o2, 0),
137 Instr::SupEqInt(o1, o2, o3) if o3 == condition_id => Instr::SupEqIntJmp(o1, o2, 0),
138 Instr::Eq(o1, o2, o3) if o3 == condition_id => Instr::EqJmp(o1, o2, 0),
139 Instr::ObjEq(o1, o2, o3) if o3 == condition_id => Instr::ObjEqJmp(o1, o2, 0),
140 Instr::StrEq(o1, o2, o3) if o3 == condition_id => Instr::StrEqJmp(o1, o2, 0),
141 Instr::NotEq(o1, o2, o3) if o3 == condition_id => Instr::NotEqJmp(o1, o2, 0),
142 Instr::ObjNotEq(o1, o2, o3) if o3 == condition_id => Instr::ObjNotEqJmp(o1, o2, 0),
143 Instr::StrNotEq(o1, o2, o3) if o3 == condition_id => Instr::StrNotEqJmp(o1, o2, 0),
144 _ => {
145 output.push(Instr::IsTrueJmp(condition_id, 0));
146 return;
147 }
148 };
149 *output.last_mut().unwrap() = new_instr;
150}
151
152#[inline(always)]
154const fn set_jmp_size(instr: &mut Instr, size: u16) {
155 match instr {
156 Instr::IsFalseJmp(_, jump_size)
157 | Instr::IsTrueJmp(_, jump_size)
158 | Instr::Jmp(jump_size)
159 | Instr::SupEqFloatJmp(_, _, jump_size)
160 | Instr::SupEqIntJmp(_, _, jump_size)
161 | Instr::SupFloatJmp(_, _, jump_size)
162 | Instr::SupIntJmp(_, _, jump_size)
163 | Instr::InfEqFloatJmp(_, _, jump_size)
164 | Instr::InfEqIntJmp(_, _, jump_size)
165 | Instr::InfFloatJmp(_, _, jump_size)
166 | Instr::InfIntJmp(_, _, jump_size)
167 | Instr::InfIntJmpBack(_, _, jump_size)
168 | Instr::NotEqJmp(_, _, jump_size)
169 | Instr::EqJmp(_, _, jump_size)
170 | Instr::ObjNotEqJmp(_, _, jump_size)
171 | Instr::ObjEqJmp(_, _, jump_size)
172 | Instr::StrNotEqJmp(_, _, jump_size)
173 | Instr::StrEqJmp(_, _, jump_size) => *jump_size = size,
174 _ => unsafe { unreachable_unchecked() },
175 }
176}
177
178#[allow(clippy::too_many_arguments)]
183fn compile_short_circuit_condition(
184 expr: &Expr,
185 v: &mut Vec<Variable>,
186 ctx: Ctx,
187 state: &mut State<'_>,
188 output: &mut Vec<Instr>,
189 bool_or_mode: bool,
190) -> (Vec<usize>, Vec<usize>) {
191 match expr {
192 Expr::BoolOr(left, right, _, _) => {
193 let (mut true_jumps, left_false) =
195 compile_short_circuit_condition(left, v, ctx, state, output, true);
196 let right_start = output.len();
199 for j in left_false {
200 set_jmp_size(&mut output[j], (right_start - j) as u16);
201 }
202 let (right_true, right_false) =
203 compile_short_circuit_condition(right, v, ctx, state, output, bool_or_mode);
204 true_jumps.extend(right_true);
205 (true_jumps, right_false)
206 }
207 Expr::BoolAnd(left, right, _, _) => {
208 if bool_or_mode {
209 let (_, left_false) =
215 compile_short_circuit_condition(left, v, ctx, state, output, false);
216 let (right_true, _) =
217 compile_short_circuit_condition(right, v, ctx, state, output, true);
218 let fallthrough = output.len();
219 for j in left_false {
220 set_jmp_size(&mut output[j], (fallthrough - j) as u16);
221 }
222 (right_true, Vec::new())
223 } else {
224 let (left_true, mut false_jumps) =
226 compile_short_circuit_condition(left, v, ctx, state, output, false);
227 let right_start = output.len();
231 for j in left_true {
232 set_jmp_size(&mut output[j], (right_start - j) as u16);
233 }
234 let (right_true, right_false) =
235 compile_short_circuit_condition(right, v, ctx, state, output, false);
236 false_jumps.extend(right_false);
237 (right_true, false_jumps)
238 }
239 }
240 expr => {
241 let cond_id = expr
242 .compile(v, ctx, state, output, None, false, true)
243 .unwrap_id();
244 if bool_or_mode {
245 add_cmp_true(cond_id, output);
246 state.free_reg(cond_id, v);
247 (vec![output.len() - 1], Vec::new())
248 } else {
249 add_cmp_false(cond_id, &mut 0, output, false);
250 state.free_reg(cond_id, v);
251 (Vec::new(), vec![output.len() - 1])
252 }
253 }
254 }
255}
256
257fn parse_loop_flow_control(
258 loop_code: &mut [Instr],
259 loop_id: u16,
260 code_length: u16,
261 for_loop: bool,
262 indefinite: bool,
263) {
264 loop_code.iter_mut().enumerate().for_each(|(i, x)| {
265 if let Instr::NotEqJmp(break_id, 0, 0) = x
266 && *break_id == loop_id
267 {
268 if for_loop && !indefinite {
269 *x = Instr::Jmp(code_length - i as u16 - 1);
270 } else {
271 *x = Instr::Jmp(code_length - i as u16);
272 }
273 } else if let Instr::EqJmp(continue_id, 0, 0) = x
274 && *continue_id == loop_id
275 {
276 if for_loop {
277 *x = Instr::Jmp(code_length - i as u16 - 3);
278 } else {
279 *x = Instr::Jmp(code_length - i as u16 - 1);
281 }
282 }
283 });
284}
285
286#[inline(always)]
287fn compile_array_literal(
288 array_items: &[Expr],
289 spans: &[Span],
290 v: &mut Vec<Variable>,
291 ctx: Ctx,
292 state: &mut State<'_>,
293 output: &mut Vec<Instr>,
294) -> u16 {
295 if let Some(first) = array_items.first() {
296 let first_type = first.infer_type(v, ctx, state);
297 if let Some(failing_elem_idx) = array_items
298 .iter()
299 .skip(1)
300 .position(|x| x.infer_type(v, ctx, state) != first_type)
301 {
302 let failing_elem_type = array_items[failing_elem_idx + 1].infer_type(v, ctx, state);
303 let failing_elem_span = spans[failing_elem_idx + 2];
304 compiler_errors::error_array_diff_types(
305 ctx.file_idx,
306 state.sources,
307 spans[1],
308 &first_type,
309 failing_elem_span,
310 &failing_elem_type,
311 )
312 }
313 }
314 let array_id = {
315 state.pools.objs.push(Vec::with_capacity(array_items.len()));
316 state.pools.objs.len() - 1
317 };
318 if array_items.is_empty() && !ctx.single_run {
319 let array_reg = {
320 state.registers.push(Data::array(array_id as u32));
321 state.registers.len() - 1
322 } as u16;
323 output.push(Instr::EmptyArray(array_reg));
324 return array_reg;
325 }
326 if ctx.single_run {
327 for elem in array_items {
328 let id = elem
329 .compile(v, ctx, state, output, None, false, true)
330 .unwrap_id();
331 if elem.is_constant_literal() {
332 state
333 .pools
334 .objs
335 .get_mut(array_id)
336 .push(state.registers[id as usize]);
337 } else {
338 output.push(Instr::ObjElemMov(
339 id,
340 array_id as u16,
341 state.pools.objs[array_id].len() as u16,
342 ));
343 state.pools.objs.get_mut(array_id).push(NULL);
344 }
345 }
346 state.registers.push(Data::array(array_id as u32));
347 (state.registers.len() - 1) as u16
348 } else {
349 let mut constant_array = true;
351 let mut elem_ids: Vec<u16> = Vec::with_capacity(array_items.len());
352 for elem in array_items {
353 let id = elem
354 .compile(v, ctx, state, output, None, false, true)
355 .unwrap_id();
356 if elem.is_constant_literal() {
357 state
358 .pools
359 .objs
360 .get_mut(array_id)
361 .push(state.registers[id as usize]);
362 } else {
363 constant_array = false;
364 state.pools.objs.get_mut(array_id).push(NULL);
365 }
366 elem_ids.push(id);
367 }
368
369 if constant_array {
370 let template_reg = {
372 state.registers.push(Data::array(array_id as u32));
373 (state.registers.len() - 1) as u16
374 };
375 let dest_reg = {
376 state.registers.push(Data::array(0)); (state.registers.len() - 1) as u16
378 };
379 output.push(Instr::CloneArray(
380 template_reg,
381 dest_reg,
382 state.pools.objs[array_id].len() as u16,
383 ));
384 dest_reg
385 } else {
386 let dest_reg = {
387 state.registers.push(Data::array(0)); (state.registers.len() - 1) as u16
389 };
390 output.push(Instr::EmptyArray(dest_reg));
391 for elem_reg in elem_ids {
392 output.push(Instr::Push(dest_reg, elem_reg));
393 }
394 dest_reg
395 }
396 }
397}
398
399fn compile_struct_literal(
400 namespace: &[SmolStr],
401 fields: &[(SmolStr, Expr, Span, Span)],
402 span: Span,
403 v: &mut Vec<Variable>,
404 ctx: Ctx,
405 state: &mut State<'_>,
406 output: &mut Vec<Instr>,
407) -> u16 {
408 let name = &namespace[namespace.len() - 1];
409 let namespace = &namespace[..(namespace.len() - 1)];
410 let Some(expected_struct_idx) =
411 state
412 .namespace
413 .find_struct(namespace, name, span, ctx.file_idx, state.sources)
414 else {
415 compiler_errors::error_unknown_struct(name, span, state.sources, ctx.file_idx);
416 };
417 let type_id = state.structs[expected_struct_idx].id;
418 let expected_fields_len = state.structs[expected_struct_idx].fields.len();
419 if expected_fields_len < fields.len() {
420 let unexpected_field = &fields[expected_fields_len];
421 compiler_errors::error_struct_no_such_field(
422 ctx.file_idx,
423 name,
424 state.structs[expected_struct_idx].name_span,
425 unexpected_field.2,
426 &unexpected_field.0,
427 state.sources,
428 )
429 }
430 let struct_id = {
431 state.pools.objs.push(Vec::with_capacity(fields.len()));
432 state.pools.objs.len() - 1
433 };
434 if ctx.single_run {
435 for field_idx in 0..expected_fields_len {
436 if let Some((_, field_expr, _, field_value_span)) = fields
437 .iter()
438 .find(|(f, _, _, _)| f == &state.structs[expected_struct_idx].fields[field_idx].0)
439 {
440 let field_type = field_expr.infer_type(v, ctx, state);
441 let field = &state.structs[expected_struct_idx].fields[field_idx];
442 if !struct_field_type_matches(&field.1, &field_type) {
443 compiler_errors::error_struct_field_invalid_type(
444 ctx.file_idx,
445 name,
446 field.2,
447 &field.0,
448 &field.1,
449 *field_value_span,
450 &field_type,
451 state.sources,
452 );
453 }
454 let id = field_expr
455 .compile(v, ctx, state, output, None, false, true)
456 .unwrap_id();
457 if field_expr.is_constant_literal() {
458 state
459 .pools
460 .objs
461 .get_mut(struct_id)
462 .push(state.registers[id as usize]);
463 } else {
464 output.push(Instr::ObjElemMov(
465 id,
466 struct_id as u16,
467 state.pools.objs[struct_id].len() as u16,
468 ));
469 state.pools.objs.get_mut(struct_id).push(NULL);
470 }
471 } else {
472 let missing_elems = (0..expected_fields_len)
473 .into_iter()
474 .filter(|i| {
475 !fields.iter().any(|(f, _, _, _)| {
476 f == &state.structs[expected_struct_idx].fields[*i].0
477 })
478 })
479 .map(|i| &state.structs[struct_id].fields[i].0)
480 .collect::<Vec<&SmolStr>>();
481 compiler_errors::error_struct_missing_fields(
482 ctx.file_idx,
483 state.structs[expected_struct_idx].name_span,
484 span,
485 state.sources,
486 &missing_elems,
487 )
488 }
489 }
490
491 state
492 .registers
493 .push(Data::struct_instance(type_id, struct_id as u32));
494 (state.registers.len() - 1) as u16
495 } else {
496 let mut dynamic: Vec<(u16, u16)> = Vec::with_capacity(expected_fields_len);
497 for field_idx in 0..expected_fields_len {
498 if let Some((_, field_expr, _, field_value_span)) = fields
499 .iter()
500 .find(|(f, _, _, _)| f == &state.structs[expected_struct_idx].fields[field_idx].0)
501 {
502 let field_type = field_expr.infer_type(v, ctx, state);
503 let field = &state.structs[expected_struct_idx].fields[field_idx];
504 if !struct_field_type_matches(&field.1, &field_type) {
505 compiler_errors::error_struct_field_invalid_type(
506 ctx.file_idx,
507 name,
508 field.2,
509 &field.0,
510 &field.1,
511 *field_value_span,
512 &field_type,
513 state.sources,
514 );
515 }
516 let id = field_expr
517 .compile(v, ctx, state, output, None, false, true)
518 .unwrap_id();
519 if field_expr.is_constant_literal() {
520 state
521 .pools
522 .objs
523 .get_mut(struct_id)
524 .push(state.registers[id as usize]);
525 } else {
526 state.pools.objs.get_mut(struct_id).push(NULL);
527 dynamic.push((id, field_idx as u16));
528 }
529 } else {
530 let missing_elems = (0..expected_fields_len)
531 .into_iter()
532 .filter(|i| {
533 !fields.iter().any(|(f, _, _, _)| {
534 f == &state.structs[expected_struct_idx].fields[*i].0
535 })
536 })
537 .map(|i| &state.structs[struct_id].fields[i].0)
538 .collect::<Vec<&SmolStr>>();
539 compiler_errors::error_struct_missing_fields(
540 ctx.file_idx,
541 state.structs[expected_struct_idx].name_span,
542 span,
543 state.sources,
544 &missing_elems,
545 );
546 }
547 }
548
549 let template_reg = {
550 state
551 .registers
552 .push(Data::struct_instance(type_id, struct_id as u32));
553 (state.registers.len() - 1) as u16
554 };
555 let dest_reg = {
556 state.registers.push(Data::struct_instance(type_id, 0));
557 (state.registers.len() - 1) as u16
558 };
559 output.push(Instr::CloneStruct(template_reg, dest_reg));
560 for (val_reg, slot) in dynamic {
561 output.push(Instr::SetFieldStruct(dest_reg, val_reg, slot));
562 }
563 dest_reg
564 }
565}
566
567pub(crate) fn resolve_enum_variant(path: &[SmolStr], state: &State<'_>) -> Option<(u16, u16)> {
574 if path.len() >= 2 {
575 let variant = &path[path.len() - 1];
576 let enum_name = &path[path.len() - 2];
577 let module = &path[..path.len() - 2];
578 let eid = state.namespace.find_enum(module, enum_name)?;
579 let vidx = state.enums[eid]
580 .variants
581 .iter()
582 .position(|vt| &vt.name == variant)?;
583 Some((eid as u16, vidx as u16))
584 } else if let Some(name) = path.first() {
585 for e in state.enums.iter() {
586 if let Some(vidx) = e.variants.iter().position(|vt| &vt.name == name) {
587 return Some((e.id, vidx as u16));
588 }
589 }
590 None
591 } else {
592 None
593 }
594}
595
596#[allow(clippy::too_many_arguments)]
602pub(crate) fn compile_enum_construction(
603 enum_id: u16,
604 variant_idx: u16,
605 args: &[Expr],
606 span: Span,
607 args_indexes: &[Span],
608 v: &mut Vec<Variable>,
609 ctx: Ctx,
610 state: &mut State<'_>,
611 output: &mut Vec<Instr>,
612) -> u16 {
613 let variant = &state.enums[enum_id as usize].variants[variant_idx as usize];
614 let variant_name = variant.name.clone();
615 let payload_types = variant.payload.clone();
616 let arity = payload_types.len();
617
618 compiler_errors::check_args(
619 args,
620 arity,
621 &variant_name,
622 span,
623 state.sources,
624 ctx.file_idx,
625 );
626 for (i, expected) in payload_types.iter().enumerate() {
627 if *expected != DataType::Unknown {
629 functions::check_arg_type(
630 &variant_name,
631 v,
632 ctx,
633 state,
634 args,
635 args_indexes,
636 i,
637 std::slice::from_ref(expected),
638 );
639 }
640 }
641
642 let pool_idx = {
643 state.pools.objs.push(Vec::with_capacity(arity + 1));
644 state.pools.objs.len() - 1
645 };
646 state
647 .pools
648 .objs
649 .get_mut(pool_idx)
650 .push(Data::int(i32::from(variant_idx)));
651
652 let mut dynamic: Vec<(u16, u16)> = Vec::with_capacity(arity);
653 for (i, arg) in args.iter().enumerate() {
654 let id = arg
655 .compile(v, ctx, state, output, None, false, true)
656 .unwrap_id();
657 if arg.is_constant_literal() {
658 let d = state.registers[id as usize];
659 state.pools.objs.get_mut(pool_idx).push(d);
660 } else {
661 state.pools.objs.get_mut(pool_idx).push(NULL);
662 dynamic.push((id, (i + 1) as u16));
663 }
664 }
665
666 let template_reg = {
667 state
668 .registers
669 .push(Data::enum_instance(enum_id, pool_idx as u32));
670 (state.registers.len() - 1) as u16
671 };
672 let dest_reg = {
673 state.registers.push(Data::enum_instance(enum_id, 0));
674 (state.registers.len() - 1) as u16
675 };
676 output.push(Instr::CloneEnum(template_reg, dest_reg));
677 for (val_reg, slot) in dynamic {
678 output.push(Instr::SetFieldStruct(dest_reg, val_reg, slot));
679 }
680 dest_reg
681}
682
683fn compile_enum_definition(
687 name: &SmolStr,
688 variants: &[(SmolStr, Box<[TypeExpr]>, Span)],
689 span: Span,
690 ctx: Ctx,
691 state: &mut State<'_>,
692) {
693 let enum_id = state.enums.len() as u16;
694 state.enums.push(EnumType {
695 name: name.clone(),
696 variants: Box::from([]),
697 id: enum_id,
698 name_span: span,
699 });
700 state
701 .namespace
702 .symbols
703 .push((name.clone(), SymbolKind::Enum(enum_id)));
704 let resolved = variants
705 .iter()
706 .map(|(vn, payload, vspan)| EnumVariant {
707 name: vn.clone(),
708 payload: payload
709 .iter()
710 .map(|t| t.to_datatype(ctx.file_idx, state.namespace, state.sources))
711 .collect(),
712 name_span: *vspan,
713 })
714 .collect();
715 state.enums[enum_id as usize].variants = resolved;
716}
717
718pub(crate) fn resolve_variant_pattern(
723 enum_id: u16,
724 pattern: &Expr,
725 fallback_span: Span,
726 ctx: Ctx,
727 state: &State<'_>,
728) -> (u16, Vec<SmolStr>) {
729 let (variant_name, binders, span): (&SmolStr, Vec<SmolStr>, Span) = match pattern {
730 Expr::Var(name, span) => (name, Vec::new(), *span),
731 Expr::NamespacedRef(path, span) => (&path[path.len() - 1], Vec::new(), *span),
732 Expr::FunctionCall(args, namespace, span, _) => {
733 let mut binders = Vec::with_capacity(args.len());
734 for arg in args {
735 if let Expr::Var(binder, _) = arg {
736 binders.push(binder.clone());
737 } else {
738 compiler_errors::error_enum(
739 "Invalid match pattern",
740 "Enum variant patterns may only bind identifiers, e.g. Circle(r)",
741 *span,
742 ctx.file_idx,
743 state.sources,
744 );
745 }
746 }
747 (&namespace[namespace.len() - 1], binders, *span)
748 }
749 _ => compiler_errors::error_enum(
750 "Invalid match pattern",
751 "A match on an enum expects variant patterns, e.g. Circle(r) or Unit",
752 fallback_span,
753 ctx.file_idx,
754 state.sources,
755 ),
756 };
757 let e = &state.enums[enum_id as usize];
758 let Some(variant_idx) = e.variants.iter().position(|vt| &vt.name == variant_name) else {
759 compiler_errors::error_enum(
760 "Unknown enum variant",
761 &format!("{} is not a variant of enum {}", variant_name, e.name),
762 span,
763 ctx.file_idx,
764 state.sources,
765 );
766 };
767 let expected_arity = e.variants[variant_idx].payload.len();
768 if binders.len() != expected_arity {
769 compiler_errors::error_enum(
770 "Wrong variant payload arity",
771 &format!(
772 "Variant {} binds {} value(s) but the pattern has {}",
773 variant_name,
774 expected_arity,
775 binders.len()
776 ),
777 span,
778 ctx.file_idx,
779 state.sources,
780 );
781 }
782 (variant_idx as u16, binders)
783}
784
785#[allow(clippy::too_many_arguments)]
788fn compile_enum_match(
789 enum_id: u16,
790 scrutinee: &Expr,
791 arms: &[(Expr, Box<[Expr]>)],
792 wildcard: Option<&[Expr]>,
793 span: Span,
794 v: &mut Vec<Variable>,
795 ctx: Ctx,
796 state: &mut State<'_>,
797 output: &mut Vec<Instr>,
798) {
799 let scrut_reg = scrutinee
800 .compile(v, ctx, state, output, None, false, true)
801 .unwrap_id();
802 let v_base = v.len();
805 v.push(Variable {
806 name: SmolStr::new_static("[MATCH SCRUT]"),
807 register_id: scrut_reg,
808 var_type: DataType::Enum(enum_id),
809 });
810
811 let tag_reg = state.alloc_reg();
812 output.push(Instr::GetFieldStruct(scrut_reg, 0, tag_reg));
813
814 let variant_count = state.enums[enum_id as usize].variants.len();
815 let mut covered = vec![false; variant_count];
816 let mut false_jmps: Vec<usize> = Vec::with_capacity(arms.len());
817 let mut arm_starts: Vec<usize> = Vec::with_capacity(arms.len());
818 let mut end_jmps: Vec<usize> = Vec::with_capacity(arms.len());
819
820 for (pattern, body) in arms {
821 let (variant_idx, binders) = resolve_variant_pattern(enum_id, pattern, span, ctx, state);
822 covered[variant_idx as usize] = true;
823
824 arm_starts.push(output.len());
825 let idx_reg = state.alloc_reg();
826 output.push(Instr::SetInt(idx_reg, i32::from(variant_idx)));
827 false_jmps.push(output.len());
828 output.push(Instr::NotEqJmp(tag_reg, idx_reg, 0));
829 state.free_reg(idx_reg, v);
830
831 let v_arm = v.len();
833 for (i, binder) in binders.iter().enumerate() {
834 if binder.as_str() != "_" {
835 let binder_reg = state.alloc_reg();
836 output.push(Instr::GetFieldStruct(scrut_reg, (i + 1) as u16, binder_reg));
837 let payload_type =
838 state.enums[enum_id as usize].variants[variant_idx as usize].payload[i].clone();
839 v.push(Variable {
840 name: binder.clone(),
841 register_id: binder_reg,
842 var_type: payload_type,
843 });
844 }
845 }
846
847 let arm_code = compile_expr(body, v, ctx.advance_offset(output.len() as u16), state);
848 output.extend(arm_code);
849 v.truncate(v_arm);
850
851 end_jmps.push(output.len());
852 output.push(Instr::Jmp(0));
853 }
854
855 let after_arms = output.len();
857 if let Some(w) = wildcard {
858 let wild_code = compile_expr(w, v, ctx.advance_offset(output.len() as u16), state);
859 output.extend(wild_code);
860 }
861 let end = output.len();
862
863 for (k, &j) in false_jmps.iter().enumerate() {
864 let target = if k + 1 < arm_starts.len() {
865 arm_starts[k + 1]
866 } else {
867 after_arms
868 };
869 set_jmp_size(&mut output[j], (target - j) as u16);
870 }
871 for &j in &end_jmps {
872 set_jmp_size(&mut output[j], (end - j) as u16);
873 }
874
875 v.truncate(v_base);
876 state.free_reg(tag_reg, v);
877 state.free_reg(scrut_reg, v);
878
879 if wildcard.is_none() && !covered.iter().all(|&c| c) {
880 let missing: Vec<&str> = state.enums[enum_id as usize]
881 .variants
882 .iter()
883 .enumerate()
884 .filter(|(i, _)| !covered[*i])
885 .map(|(_, vt)| vt.name.as_str())
886 .collect();
887 compiler_errors::error_enum(
888 "Non-exhaustive match",
889 &format!(
890 "match on enum {} does not cover: {}. Add the missing arm(s) or a `_` wildcard",
891 state.enums[enum_id as usize].name,
892 missing.join(", ")
893 ),
894 span,
895 ctx.file_idx,
896 state.sources,
897 );
898 }
899}
900
901fn compile_match(
905 scrutinee: &Expr,
906 arms: &[(Expr, Box<[Expr]>)],
907 wildcard: Option<&[Expr]>,
908 span: Span,
909 v: &mut Vec<Variable>,
910 ctx: Ctx,
911 state: &mut State<'_>,
912 output: &mut Vec<Instr>,
913) {
914 if let DataType::Enum(enum_id) = scrutinee.infer_type(v, ctx, state) {
915 compile_enum_match(
916 enum_id, scrutinee, arms, wildcard, span, v, ctx, state, output,
917 );
918 } else {
919 let obj_var = SmolStr::new_static("[MATCH TEMP]");
920 let (first_pat, first_body) = &arms[0];
921 let mut output_code: Vec<Expr> = Vec::with_capacity(arms.len());
922 output_code.extend(first_body.iter().cloned());
923 for (pat, body) in &arms[1..] {
924 output_code.push(Expr::ElseIfBlock(
925 Box::new(Expr::Eq(
926 Box::new(Expr::Var(obj_var.clone(), span)),
927 Box::new(pat.clone()),
928 )),
929 body.clone(),
930 ));
931 }
932 if let Some(w) = wildcard {
933 output_code.push(Expr::ElseBlock(Box::from(w)));
934 }
935 let desugared = Expr::EvalBlock(Box::from([
936 Expr::VarDeclare(obj_var.clone(), Box::new(scrutinee.clone())),
937 Expr::Condition(
938 Box::new(Expr::Eq(
939 Box::new(Expr::Var(obj_var, span)),
940 Box::new(first_pat.clone()),
941 )),
942 Box::from(output_code),
943 span,
944 ),
945 ]));
946 desugared.compile(v, ctx, state, output, None, false, false);
947 }
948}
949
950fn compile_map_literal(
951 kv_pairs: &[(Expr, Span, Expr, Span)],
952 map_span: Span,
953 v: &mut Vec<Variable>,
954 ctx: Ctx,
955 state: &mut State<'_>,
956 output: &mut Vec<Instr>,
957) -> u16 {
958 let mut global_key_type: DataType = DataType::Unknown;
959 let mut global_val_type: DataType = DataType::Unknown;
960 let map_id = state.pools.maps.len();
961 state.pools.maps.push(HashMap::with_capacity_and_hasher(
962 kv_pairs.len(),
963 BuildHasherDefault::default(),
964 ));
965 if ctx.single_run {
966 for (i, (key, key_span, val, val_span)) in kv_pairs.iter().enumerate() {
967 if let Some((_, repeat_key_span, _, _)) =
968 kv_pairs.iter().skip(i + 1).find(|(k, _, _, _)| k == key)
969 {
970 error_duplicate_map_key(
971 *key_span,
972 *repeat_key_span,
973 map_span,
974 ctx.file_idx,
975 state.sources,
976 );
977 }
978 let key_t = key.infer_type(v, ctx, state);
979 let val_t = val.infer_type(v, ctx, state);
980 if i == 0 {
981 global_key_type = key_t;
982 global_val_type = val_t;
983 } else {
984 if key_t != global_key_type {
985 error_map_diff_types(
986 ctx.file_idx,
987 state.sources,
988 map_span,
989 &global_key_type,
990 *key_span,
991 &key_t,
992 )
993 }
994 if val_t != global_val_type {
995 error_map_diff_types(
996 ctx.file_idx,
997 state.sources,
998 map_span,
999 &global_val_type,
1000 *val_span,
1001 &val_t,
1002 )
1003 }
1004 }
1005 let output_len = output.len();
1006 let key_val_id = key
1007 .compile(v, ctx, state, output, None, false, true)
1008 .unwrap_id();
1009 if !(key.is_constant_literal()
1010 || matches!(key, Expr::Array(_, _)) && output_len == output.len())
1011 {
1012 error_not_literal_map_key(*key_span, map_span, ctx.file_idx, state.sources);
1013 }
1014 let key_val = state.registers[key_val_id as usize];
1015 let id = val
1016 .compile(v, ctx, state, output, None, false, true)
1017 .unwrap_id();
1018 if val.is_constant_literal() {
1019 state.pools.maps[map_id].insert(key_val, state.registers[id as usize]);
1020 } else {
1021 state.pools.maps[map_id].insert(key_val, NULL);
1022 output.push(Instr::MapInsert(
1023 map_id as u16,
1024 state.registers.len() as u16,
1025 id,
1026 ));
1027 state.registers.push(key_val);
1028 }
1029 }
1030 let dest_id = state.registers.len();
1031 state.registers.push(Data::map(map_id as u32));
1032 dest_id as u16
1033 } else {
1034 let mut dynamic: Vec<(Data, u16)> = Vec::with_capacity(kv_pairs.len());
1035 for (i, (key, key_span, val, val_span)) in kv_pairs.iter().enumerate() {
1036 if let Some((_, repeat_key_span, _, _)) =
1037 kv_pairs.iter().skip(i + 1).find(|(k, _, _, _)| k == key)
1038 {
1039 error_duplicate_map_key(
1040 *key_span,
1041 *repeat_key_span,
1042 map_span,
1043 ctx.file_idx,
1044 state.sources,
1045 );
1046 }
1047 let key_t = key.infer_type(v, ctx, state);
1048 let val_t = val.infer_type(v, ctx, state);
1049 if i == 0 {
1050 global_key_type = key_t;
1051 global_val_type = val_t;
1052 } else {
1053 if key_t != global_key_type {
1054 error_map_diff_types(
1055 ctx.file_idx,
1056 state.sources,
1057 map_span,
1058 &global_key_type,
1059 *key_span,
1060 &key_t,
1061 )
1062 }
1063 if val_t != global_val_type {
1064 error_map_diff_types(
1065 ctx.file_idx,
1066 state.sources,
1067 map_span,
1068 &global_val_type,
1069 *val_span,
1070 &val_t,
1071 )
1072 }
1073 }
1074 let output_len = output.len();
1075 let key_val_id = key
1076 .compile(v, ctx, state, output, None, false, true)
1077 .unwrap_id();
1078 if !(key.is_constant_literal()
1079 || matches!(key, Expr::Array(_, _)) && output_len == output.len())
1080 {
1081 error_not_literal_map_key(*key_span, map_span, ctx.file_idx, state.sources);
1082 }
1083 let key_val = state.registers[key_val_id as usize];
1084 let val_id = val
1085 .compile(v, ctx, state, output, None, false, true)
1086 .unwrap_id();
1087 if val.is_constant_literal() {
1088 state.pools.maps[map_id].insert(key_val, state.registers[val_id as usize]);
1089 } else {
1090 state.pools.maps[map_id].insert(key_val, NULL);
1091 dynamic.push((key_val, val_id));
1092 }
1093 }
1094
1095 let template_reg = {
1096 state.registers.push(Data::map(map_id as u32));
1097 (state.registers.len() - 1) as u16
1098 };
1099 let dest_reg = {
1100 state.registers.push(Data::map(0));
1101 (state.registers.len() - 1) as u16
1102 };
1103 output.push(Instr::CloneMap(template_reg, dest_reg));
1104 for (key_val, val_id) in dynamic {
1105 let key_reg = if let Some(&id) = state.const_registers.get(&key_val) {
1106 id
1107 } else {
1108 let id = state.registers.len() as u16;
1109 state.const_registers.insert(key_val, id);
1110 state.registers.push(key_val);
1111 id
1112 };
1113 output.push(Instr::MapInsertReg(dest_reg, key_reg, val_id));
1114 }
1115 dest_reg
1116 }
1117}
1118
1119fn compile_struct_field_access(
1120 struct_expr: &Expr,
1121 field: &SmolStr,
1122 struct_span: Span,
1123 field_span: Span,
1124 v: &mut Vec<Variable>,
1125 ctx: Ctx,
1126 state: &mut State<'_>,
1127 output: &mut Vec<Instr>,
1128) -> u16 {
1129 let t = struct_expr.infer_type(v, ctx, state);
1130 if let DataType::Struct(s_id) = t {
1131 let s = &state.structs[s_id as usize];
1132 let idx = s
1133 .fields
1134 .iter()
1135 .position(|f| &f.0 == field)
1136 .unwrap_or_else(|| {
1137 compiler_errors::error_struct_unknown_field(
1138 ctx.file_idx,
1139 field_span,
1140 field,
1141 &s.name,
1142 &s.fields,
1143 state.sources,
1144 );
1145 });
1146 let id = struct_expr
1147 .compile(v, ctx, state, output, None, false, true)
1148 .unwrap_id();
1149 let dest_reg_id = state.alloc_reg();
1150 output.push(Instr::GetFieldStruct(id, idx as u16, dest_reg_id));
1151 dest_reg_id
1152 } else {
1153 error_invalid_type(
1154 &DataType::Struct(0),
1155 &t,
1156 struct_span,
1157 None,
1158 None,
1159 ctx.file_idx,
1160 state.sources,
1161 );
1162 }
1163}
1164
1165fn compile_array_indexing(
1166 array: &Expr,
1167 index: &Expr,
1168 span: Span,
1169 v: &mut Vec<Variable>,
1170 ctx: Ctx,
1171 state: &mut State<'_>,
1172 output: &mut Vec<Instr>,
1173) -> u16 {
1174 let inferred = array.infer_type(v, ctx, state);
1175 if !inferred.is_indexable() {
1176 error_type_not_indexable(&inferred, span, false, ctx.file_idx, state.sources);
1177 }
1178
1179 let id = array
1180 .compile(v, ctx, state, output, None, false, true)
1181 .unwrap_id();
1182
1183 let index_inferred = index.infer_type(v, ctx, state);
1184 if index_inferred != DataType::Int {
1185 error_invalid_index_type(&index_inferred, span, ctx.file_idx, state.sources);
1186 }
1187 let index_id = index
1188 .compile(v, ctx, state, output, None, false, true)
1189 .unwrap_id();
1190 state.free_reg(index_id, v);
1191 let dest_reg_id = state.alloc_reg();
1192
1193 let to_push = if inferred == DataType::String {
1194 Instr::GetIndexString(id, index_id, dest_reg_id)
1195 } else {
1196 Instr::GetIndexArray(id, index_id, dest_reg_id)
1197 };
1198 output.push(to_push);
1199 state.add_to_src(ctx, output, span);
1200 dest_reg_id
1201}
1202
1203fn compile_array_slice(
1204 array: &Expr,
1205 idx_start: &Expr,
1206 idx_end: &Expr,
1207 span: Span,
1208 v: &mut Vec<Variable>,
1209 ctx: Ctx,
1210 state: &mut State<'_>,
1211 output: &mut Vec<Instr>,
1212) -> u16 {
1213 let inferred = array.infer_type(v, ctx, state);
1214 if !inferred.is_indexable() {
1215 error_type_not_indexable(&inferred, span, false, ctx.file_idx, state.sources);
1216 }
1217 let id = array
1218 .compile(v, ctx, state, output, None, false, true)
1219 .unwrap_id();
1220 let idx_start_inferred = idx_start.infer_type(v, ctx, state);
1221 if idx_start_inferred != DataType::Int {
1222 error_invalid_index_type(&idx_start_inferred, span, ctx.file_idx, state.sources);
1223 }
1224 let idx_start_id = idx_start
1225 .compile(v, ctx, state, output, None, false, true)
1226 .unwrap_id();
1227 let idx_end_inferred = idx_end.infer_type(v, ctx, state);
1228 if idx_end_inferred != DataType::Int {
1229 error_invalid_index_type(&idx_end_inferred, span, ctx.file_idx, state.sources);
1230 }
1231 let idx_end_id = idx_end
1232 .compile(v, ctx, state, output, None, false, true)
1233 .unwrap_id();
1234 output.push(Instr::StoreFuncArg(idx_end_id));
1235 state.free_reg(idx_start_id, v);
1236 state.free_reg(idx_end_id, v);
1237 let dest_reg_id = state.alloc_reg();
1238 let to_push = if inferred == DataType::String {
1239 Instr::GetSliceString(id, idx_start_id, dest_reg_id)
1240 } else {
1241 Instr::GetSliceArray(id, idx_start_id, dest_reg_id)
1242 };
1243 output.push(to_push);
1244 state.add_to_src(ctx, output, span);
1245 dest_reg_id
1246}
1247
1248#[inline]
1249fn uniform_op2(
1250 instr: fn(u16, u16, u16) -> Instr,
1251 t_1: &'static DataType,
1252 instr2: fn(u16, u16, u16) -> Instr,
1253 t_2: &'static DataType,
1254 symbol: &'static str,
1255 l: &Expr,
1256 r: &Expr,
1257 span_l: Span,
1258 span_r: Span,
1259 tgt_id: Option<u16>,
1260 v: &mut Vec<Variable>,
1261 ctx: Ctx,
1262 state: &mut State<'_>,
1263 output: &mut Vec<Instr>,
1264) -> u16 {
1265 let (t_l, t_r) = (l.infer_type(v, ctx, state), r.infer_type(v, ctx, state));
1266 if !((&t_l == t_1 && &t_r == t_1) || (&t_l == t_2 && &t_r == t_2)) {
1267 compiler_errors::error_op(
1268 &t_l,
1269 &t_r,
1270 symbol,
1271 span_l,
1272 span_r,
1273 ctx.file_idx,
1274 state.sources,
1275 );
1276 }
1277 let id_l = l
1278 .compile(v, ctx, state, output, None, false, true)
1279 .unwrap_id();
1280 let id_r = r
1281 .compile(v, ctx, state, output, None, false, true)
1282 .unwrap_id();
1283 state.free_reg(id_l, v);
1284 state.free_reg(id_r, v);
1285 let id = state.alloc_reg_tgt(tgt_id);
1286 output.push(if &t_l == t_1 {
1287 instr(id_l, id_r, id)
1288 } else {
1289 instr2(id_l, id_r, id)
1290 });
1291 id
1292}
1293
1294fn compile_div_op(
1295 l: &Expr,
1296 r: &Expr,
1297 span_l: Span,
1298 span_r: Span,
1299 tgt_id: Option<u16>,
1300 v: &mut Vec<Variable>,
1301 ctx: Ctx,
1302 state: &mut State<'_>,
1303 output: &mut Vec<Instr>,
1304) -> u16 {
1305 if let Expr::Int(n) = r
1309 && *n == 0
1310 && l.infer_type(v, ctx, state) != DataType::Float
1311 {
1312 error_division_by_zero(false, span_l.extend(span_r), ctx.file_idx, state.sources);
1313 }
1314 let id = uniform_op2(
1315 Instr::DivFloat,
1316 &DataType::Float,
1317 Instr::DivInt,
1318 &DataType::Int,
1319 "/",
1320 l,
1321 r,
1322 span_l,
1323 span_r,
1324 tgt_id,
1325 v,
1326 ctx,
1327 state,
1328 output,
1329 );
1330 if matches!(output.last(), Some(Instr::DivInt(..))) {
1331 state.add_to_src(ctx, output, span_l.extend(span_r));
1332 }
1333 id
1334}
1335
1336fn compile_add_op(
1337 l: &Expr,
1338 r: &Expr,
1339 span_l: Span,
1340 span_r: Span,
1341 tgt_id: Option<u16>,
1342 v: &mut Vec<Variable>,
1343 ctx: Ctx,
1344 state: &mut State<'_>,
1345 output: &mut Vec<Instr>,
1346) -> u16 {
1347 let t_l = l.infer_type(v, ctx, state);
1348 let t_r = r.infer_type(v, ctx, state);
1349 if t_l != t_r
1350 || !matches!(
1351 t_l,
1352 DataType::String | DataType::Array(_) | DataType::Float | DataType::Int
1353 )
1354 {
1355 compiler_errors::error_op(&t_l, &t_r, "+", span_l, span_r, ctx.file_idx, state.sources);
1356 }
1357 if t_l == DataType::Int
1359 && let Some(Expr::Var(src_name, _)) = {
1360 if matches!(r, Expr::Int(1)) {
1361 Some(l)
1362 } else if matches!(l, Expr::Int(1)) {
1363 Some(r)
1364 } else {
1365 None
1366 }
1367 }
1368 && let Some(src_var) = v.iter().rfind(|x| x.name == *src_name)
1369 {
1370 let src_id = src_var.register_id;
1371 let id = tgt_id.unwrap_or_else(|| state.alloc_reg());
1372 output.push(if src_id == id {
1373 Instr::IncInt(id)
1374 } else {
1375 Instr::IncIntTo(src_id, id)
1376 });
1377 return id;
1378 }
1379 let id_l = l
1380 .compile(v, ctx, state, output, None, false, true)
1381 .unwrap_id();
1382 let id_r = r
1383 .compile(v, ctx, state, output, None, false, true)
1384 .unwrap_id();
1385 state.free_reg(id_l, v);
1386 state.free_reg(id_r, v);
1387 let id = state.alloc_reg_tgt(tgt_id);
1388 if matches!(t_l, DataType::Array(_)) {
1389 output.push(Instr::AddArray(id_l, id_r, id));
1390 } else if t_l == DataType::String {
1391 output.push(Instr::AddStr(id_l, id_r, id));
1392 } else if t_l == DataType::Float {
1393 output.push(Instr::AddFloat(id_l, id_r, id));
1394 } else {
1395 output.push(Instr::AddInt(id_l, id_r, id));
1396 }
1397 id
1398}
1399
1400fn compile_sub_op(
1401 l: &Expr,
1402 r: &Expr,
1403 span_l: Span,
1404 span_r: Span,
1405 tgt_id: Option<u16>,
1406 v: &mut Vec<Variable>,
1407 ctx: Ctx,
1408 state: &mut State<'_>,
1409 output: &mut Vec<Instr>,
1410) -> u16 {
1411 let t_l = l.infer_type(v, ctx, state);
1412 let t_r = r.infer_type(v, ctx, state);
1413 if !((t_l == DataType::Float && t_r == DataType::Float)
1414 || (t_l == DataType::Int && t_r == DataType::Int))
1415 {
1416 compiler_errors::error_op(&t_l, &t_r, "-", span_l, span_r, ctx.file_idx, state.sources);
1417 }
1418 if t_l == DataType::Int
1420 && matches!(r, Expr::Int(1))
1421 && let Expr::Var(src_name, _) = l
1422 && let Some(src_var) = v.iter().rfind(|x| x.name == *src_name)
1423 {
1424 let src_id = src_var.register_id;
1425 let id = tgt_id.unwrap_or_else(|| state.alloc_reg());
1426 output.push(if src_id == id {
1427 Instr::DecInt(id)
1428 } else {
1429 Instr::DecIntTo(src_id, id)
1430 });
1431 return id;
1432 }
1433 let id_l = l
1434 .compile(v, ctx, state, output, None, false, true)
1435 .unwrap_id();
1436 let id_r = r
1437 .compile(v, ctx, state, output, None, false, true)
1438 .unwrap_id();
1439 state.free_reg(id_l, v);
1440 state.free_reg(id_r, v);
1441 let id = state.alloc_reg_tgt(tgt_id);
1442 output.push(if t_l == DataType::Float {
1443 Instr::SubFloat(id_l, id_r, id)
1444 } else {
1445 Instr::SubInt(id_l, id_r, id)
1446 });
1447 id
1448}
1449
1450fn compile_mod_op(
1451 l: &Expr,
1452 r: &Expr,
1453 span_l: Span,
1454 span_r: Span,
1455 tgt_id: Option<u16>,
1456 v: &mut Vec<Variable>,
1457 ctx: Ctx,
1458 state: &mut State<'_>,
1459 output: &mut Vec<Instr>,
1460) -> u16 {
1461 if let Expr::Int(n) = r
1464 && *n == 0
1465 && l.infer_type(v, ctx, state) != DataType::Float
1466 {
1467 error_division_by_zero(true, span_l.extend(span_r), ctx.file_idx, state.sources);
1468 }
1469 let id = uniform_op2(
1470 Instr::ModFloat,
1471 &DataType::Float,
1472 Instr::ModInt,
1473 &DataType::Int,
1474 "%",
1475 l,
1476 r,
1477 span_l,
1478 span_r,
1479 tgt_id,
1480 v,
1481 ctx,
1482 state,
1483 output,
1484 );
1485 if matches!(output.last(), Some(Instr::ModInt(..))) {
1486 state.add_to_src(ctx, output, span_l.extend(span_r));
1487 }
1488 id
1489}
1490
1491fn compile_short_circuit_value(
1503 l: &Expr,
1504 r: &Expr,
1505 span_l: Span,
1506 span_r: Span,
1507 symbol: &'static str,
1508 tgt_id: Option<u16>,
1509 v: &mut Vec<Variable>,
1510 ctx: Ctx,
1511 state: &mut State<'_>,
1512 output: &mut Vec<Instr>,
1513) -> u16 {
1514 let (t_l, t_r) = (l.infer_type(v, ctx, state), r.infer_type(v, ctx, state));
1515 if t_l != DataType::Bool || t_r != DataType::Bool {
1516 cold_path();
1517 compiler_errors::error_op(
1518 &t_l,
1519 &t_r,
1520 symbol,
1521 span_l,
1522 span_r,
1523 ctx.file_idx,
1524 state.sources,
1525 );
1526 }
1527
1528 let id = state.alloc_reg_tgt(tgt_id);
1529 let left_id = l
1530 .compile(v, ctx, state, output, Some(id), false, true)
1531 .unwrap_id();
1532 if left_id != id {
1533 output.push(Instr::Mov(left_id, id));
1534 }
1535
1536 let skip_idx = output.len();
1537 output.push(if symbol == "&&" {
1540 Instr::IsFalseJmp(id, 0)
1541 } else {
1542 Instr::IsTrueJmp(id, 0)
1543 });
1544
1545 let right_id = r
1546 .compile(v, ctx, state, output, None, false, true)
1547 .unwrap_id();
1548 state.free_reg(right_id, v);
1549 output.push(Instr::Mov(right_id, id));
1550 let skip_size = (output.len() - skip_idx) as u16;
1551 set_jmp_size(&mut output[skip_idx], skip_size);
1552 id
1553}
1554
1555fn compile_eq_op(
1556 l: &Expr,
1557 r: &Expr,
1558 tgt_id: Option<u16>,
1559 v: &mut Vec<Variable>,
1560 ctx: Ctx,
1561 state: &mut State<'_>,
1562 output: &mut Vec<Instr>,
1563) -> u16 {
1564 let l_type = l.infer_type(v, ctx, state);
1565 let r_type = r.infer_type(v, ctx, state);
1566 let is_array = matches!(
1567 l_type,
1568 DataType::Array(_) | DataType::Struct(_) | DataType::Enum(_)
1569 ) && matches!(
1570 r_type,
1571 DataType::Array(_) | DataType::Struct(_) | DataType::Enum(_)
1572 );
1573 let is_string = l_type == DataType::String || r_type == DataType::String;
1574 let id_l = l
1575 .compile(v, ctx, state, output, None, false, true)
1576 .unwrap_id();
1577 let id_r = r
1578 .compile(v, ctx, state, output, None, false, true)
1579 .unwrap_id();
1580 state.free_reg(id_l, v);
1581 state.free_reg(id_r, v);
1582 let id = state.alloc_reg_tgt(tgt_id);
1583 output.push(if is_array {
1584 Instr::ObjEq(id_l, id_r, id)
1585 } else if is_string {
1586 Instr::StrEq(id_l, id_r, id)
1587 } else {
1588 Instr::Eq(id_l, id_r, id)
1589 });
1590 id
1591}
1592
1593fn compile_neq_op(
1594 l: &Expr,
1595 r: &Expr,
1596 tgt_id: Option<u16>,
1597 v: &mut Vec<Variable>,
1598 ctx: Ctx,
1599 state: &mut State<'_>,
1600 output: &mut Vec<Instr>,
1601) -> u16 {
1602 let l_type = l.infer_type(v, ctx, state);
1603 let r_type = r.infer_type(v, ctx, state);
1604 let is_array = matches!(
1605 l_type,
1606 DataType::Array(_) | DataType::Struct(_) | DataType::Enum(_)
1607 ) && matches!(
1608 r_type,
1609 DataType::Array(_) | DataType::Struct(_) | DataType::Enum(_)
1610 );
1611 let is_string = l_type == DataType::String || r_type == DataType::String;
1612 let id_l = l
1613 .compile(v, ctx, state, output, None, false, true)
1614 .unwrap_id();
1615 let id_r = r
1616 .compile(v, ctx, state, output, None, false, true)
1617 .unwrap_id();
1618 state.free_reg(id_l, v);
1619 state.free_reg(id_r, v);
1620 let id = state.alloc_reg_tgt(tgt_id);
1621 if is_array {
1622 output.push(Instr::ObjNotEq(id_l, id_r, id));
1623 } else if is_string {
1624 output.push(Instr::StrNotEq(id_l, id_r, id));
1625 } else {
1626 output.push(Instr::NotEq(id_l, id_r, id));
1627 }
1628 id
1629}
1630
1631fn compile_neg_op(
1632 l: &Expr,
1633 span_l: Span,
1634 span_r: Span,
1635 tgt_id: Option<u16>,
1636 v: &mut Vec<Variable>,
1637 ctx: Ctx,
1638 state: &mut State<'_>,
1639 output: &mut Vec<Instr>,
1640) -> u16 {
1641 let operand_type = l.infer_type(v, ctx, state);
1642 let id_l = l
1643 .compile(v, ctx, state, output, None, false, true)
1644 .unwrap_id();
1645 state.free_reg(id_l, v);
1646 let id = state.alloc_reg_tgt(tgt_id);
1647 if operand_type == DataType::Float {
1648 output.push(Instr::NegFloat(id_l, id));
1649 } else if operand_type == DataType::Int {
1650 output.push(Instr::NegInt(id_l, id));
1651 } else {
1652 compiler_errors::error_op(
1653 &DataType::Null,
1654 &operand_type,
1655 "-",
1656 span_l,
1657 span_r,
1658 ctx.file_idx,
1659 state.sources,
1660 );
1661 }
1662 id
1663}
1664
1665fn compile_bool_neg_op(
1666 l: &Expr,
1667 span_l: Span,
1668 span_r: Span,
1669 tgt_id: Option<u16>,
1670 v: &mut Vec<Variable>,
1671 ctx: Ctx,
1672 state: &mut State<'_>,
1673 output: &mut Vec<Instr>,
1674) -> u16 {
1675 let operand_type = l.infer_type(v, ctx, state);
1676 let id_l = l
1677 .compile(v, ctx, state, output, None, false, true)
1678 .unwrap_id();
1679 state.free_reg(id_l, v);
1680 let id = state.alloc_reg_tgt(tgt_id);
1681 if operand_type != DataType::Bool {
1682 compiler_errors::error_op(
1683 &DataType::Null,
1684 &operand_type,
1685 "!",
1686 span_l,
1687 span_r,
1688 ctx.file_idx,
1689 state.sources,
1690 );
1691 }
1692 output.push(Instr::NegBool(id_l, id));
1693 id
1694}
1695
1696fn compile_inline_condition_branch(
1697 branch: &[Expr],
1698 v: &mut Vec<Variable>,
1699 ctx: Ctx,
1700 state: &mut State<'_>,
1701 output: &mut Vec<Instr>,
1702 tgt_id: u16,
1703) {
1704 let regs_before = state.registers.len() as u16;
1705 let output_len = output.len();
1706 output.extend(compile_expr(
1707 &branch[..branch.len() - 1],
1708 v,
1709 ctx.advance_offset(output.len() as u16),
1710 state,
1711 ));
1712 let val_id = branch[branch.len() - 1]
1713 .compile(
1714 v,
1715 ctx.advance_offset(output.len() as u16),
1716 state,
1717 output,
1718 Some(tgt_id),
1719 false,
1720 true,
1721 )
1722 .unwrap_id();
1723 state.free_scope_registers(regs_before, &output[output_len..], v);
1724 if val_id != tgt_id {
1725 output.push(Instr::Mov(val_id, tgt_id));
1726 }
1727}
1728
1729fn compile_inline_condition(
1730 main_condition: &Expr,
1731 code: &[Expr],
1732 span: Span,
1733 v: &mut Vec<Variable>,
1734 ctx: Ctx,
1735 state: &mut State<'_>,
1736 output: &mut Vec<Instr>,
1737 tgt_id: Option<u16>,
1738) -> u16 {
1739 let return_id = state.alloc_reg_tgt(tgt_id);
1740
1741 let main_code_limit = code
1743 .iter()
1744 .position(|x| matches!(x, Expr::ElseIfBlock(_, _) | Expr::ElseBlock(_)))
1745 .unwrap_or(code.len());
1746
1747 let condition_blocks_count = code.len() - main_code_limit;
1748 let mut cmp_markers: Vec<usize> = Vec::with_capacity(condition_blocks_count);
1749 let mut jmp_markers: Vec<usize> = Vec::with_capacity(condition_blocks_count);
1750 let mut condition_markers: Vec<usize> = Vec::with_capacity(condition_blocks_count);
1751
1752 let condition_id = main_condition
1754 .compile(v, ctx, state, output, None, false, true)
1755 .unwrap_id();
1756 add_cmp_false(condition_id, &mut 0, output, false);
1757 cmp_markers.push(output.len() - 1);
1758
1759 compile_inline_condition_branch(&code[..main_code_limit], v, ctx, state, output, return_id);
1760 if main_code_limit != code.len() {
1761 output.push(Instr::Jmp(0));
1762 jmp_markers.push(output.len() - 1);
1763 }
1764
1765 let mut else_exists = false;
1766 for elem in &code[main_code_limit..] {
1767 if let Expr::ElseIfBlock(condition, code) = elem {
1768 condition_markers.push(output.len());
1769 let condition_id = condition
1770 .compile(v, ctx, state, output, None, false, true)
1771 .unwrap_id();
1772 add_cmp_false(condition_id, &mut 0, output, false);
1773 state.free_reg(condition_id, v);
1774 cmp_markers.push(output.len() - 1);
1775 compile_inline_condition_branch(code, v, ctx, state, output, return_id);
1776 output.push(Instr::Jmp(0));
1777 jmp_markers.push(output.len() - 1);
1778 } else if let Expr::ElseBlock(code) = elem {
1779 else_exists = true;
1780 condition_markers.push(output.len());
1781 compile_inline_condition_branch(code, v, ctx, state, output, return_id);
1782 }
1783 }
1784 if !else_exists {
1785 error_conditional_expression_without_else(span, ctx.file_idx, state.sources);
1786 }
1787
1788 for y in jmp_markers {
1789 let diff = output.len() - y;
1790 output[y] = Instr::Jmp(diff as u16);
1791 }
1792 for (i, y) in cmp_markers.iter().enumerate() {
1793 let diff = if i >= condition_markers.len() {
1794 output.len() - 1 - y
1795 } else {
1796 condition_markers[i] - y
1797 };
1798 if let Some(
1799 Instr::IsFalseJmp(_, jump_size)
1800 | Instr::SupEqFloatJmp(_, _, jump_size)
1801 | Instr::SupEqIntJmp(_, _, jump_size)
1802 | Instr::SupFloatJmp(_, _, jump_size)
1803 | Instr::SupIntJmp(_, _, jump_size)
1804 | Instr::InfEqFloatJmp(_, _, jump_size)
1805 | Instr::InfEqIntJmp(_, _, jump_size)
1806 | Instr::InfFloatJmp(_, _, jump_size)
1807 | Instr::InfIntJmp(_, _, jump_size)
1808 | Instr::NotEqJmp(_, _, jump_size)
1809 | Instr::ObjNotEqJmp(_, _, jump_size)
1810 | Instr::EqJmp(_, _, jump_size)
1811 | Instr::ObjEqJmp(_, _, jump_size),
1812 ) = output.get_mut(*y)
1813 {
1814 *jump_size = diff as u16;
1815 }
1816 }
1817 state.free_reg(condition_id, v);
1818 return_id
1819}
1820
1821fn compile_array_index_assignment(
1822 array: &Expr,
1823 index: &Expr,
1824 value: &Expr,
1825 index_span: Span,
1826 elem_span: Span,
1827 v: &mut Vec<Variable>,
1828 ctx: Ctx,
1829 state: &mut State<'_>,
1830 output: &mut Vec<Instr>,
1831) {
1832 let array_type = array.infer_type(v, ctx, state);
1833 if !array_type.is_indexable() {
1834 error_type_not_indexable(&array_type, index_span, false, ctx.file_idx, state.sources);
1835 }
1836 let id = array
1838 .compile(v, ctx, state, output, None, false, true)
1839 .unwrap_id();
1840
1841 let final_id = index
1842 .compile(v, ctx, state, output, None, false, true)
1843 .unwrap_id();
1844
1845 let elem_type = value.infer_type(v, ctx, state);
1846 let elem_id = value
1847 .compile(v, ctx, state, output, None, false, true)
1848 .unwrap_id();
1849 state.free_reg(elem_id, v);
1850 if {
1851 if let DataType::Array(Some(array_type)) = &array_type
1852 && array_type.as_ref() != &elem_type
1853 {
1854 true
1855 } else {
1856 false
1857 }
1858 } || (array_type == DataType::String && elem_type != DataType::String)
1859 {
1860 error_cannot_push_type_to_array(
1861 &array_type,
1862 &elem_type,
1863 index_span,
1864 elem_span,
1865 ctx.file_idx,
1866 state.sources,
1867 );
1868 }
1869
1870 let to_push = if array_type == DataType::String {
1871 Instr::SetElementString(id, elem_id, final_id)
1872 } else {
1873 Instr::SetElementObj(id, elem_id, final_id)
1874 };
1875 output.push(to_push);
1876 state.add_to_src(ctx, output, index_span);
1877 state.free_reg(id, v);
1878}
1879
1880fn compile_struct_field_assignment(
1881 struct_expr: &Expr,
1882 field: &SmolStr,
1883 new_val: &Expr,
1884 struct_span: Span,
1885 field_span: Span,
1886 value_span: Span,
1887 v: &mut Vec<Variable>,
1888 ctx: Ctx,
1889 state: &mut State<'_>,
1890 output: &mut Vec<Instr>,
1891) {
1892 let t = struct_expr.infer_type(v, ctx, state);
1893 let new_val_type = new_val.infer_type(v, ctx, state);
1894 let DataType::Struct(struct_id) = t else {
1895 error_invalid_type(
1896 &DataType::Struct(0),
1897 &t,
1898 struct_span,
1899 None,
1900 None,
1901 ctx.file_idx,
1902 state.sources,
1903 );
1904 };
1905 let mut field_index: Option<u16> = None;
1906 let field_struct = &state.structs[struct_id as usize];
1907 let struct_name = &field_struct.name;
1908 for (i, (expected_field_name, expected_field_type, expected_field_span)) in
1909 field_struct.fields.iter().enumerate()
1910 {
1911 if expected_field_name == field {
1912 if !struct_field_type_matches(expected_field_type, &new_val_type) {
1913 compiler_errors::error_struct_field_invalid_type(
1914 ctx.file_idx,
1915 struct_name,
1916 *expected_field_span,
1917 expected_field_name,
1918 expected_field_type,
1919 value_span,
1920 &new_val_type,
1921 state.sources,
1922 );
1923 }
1924 field_index = Some(i as u16);
1925 break;
1926 }
1927 }
1928 let Some(field_index) = field_index else {
1929 compiler_errors::error_struct_unknown_field(
1930 ctx.file_idx,
1931 field_span,
1932 field,
1933 struct_name,
1934 &field_struct.fields,
1935 state.sources,
1936 );
1937 };
1938 let id = struct_expr
1939 .compile(v, ctx, state, output, None, false, true)
1940 .unwrap_id();
1941 let new_elem_reg_id = new_val
1942 .compile(v, ctx, state, output, None, false, true)
1943 .unwrap_id();
1944 output.push(Instr::SetFieldStruct(id, new_elem_reg_id, field_index));
1945}
1946
1947fn compile_condition(
1948 main_condition: &Expr,
1949 code: &[Expr],
1950 v: &mut Vec<Variable>,
1951 ctx: Ctx,
1952 state: &mut State<'_>,
1953 output: &mut Vec<Instr>,
1954) {
1955 let main_code_limit = code
1957 .iter()
1958 .position(|x| matches!(x, Expr::ElseIfBlock(_, _) | Expr::ElseBlock(_)))
1959 .unwrap_or(code.len());
1960
1961 let condition_blocks_count = code.len() - main_code_limit;
1962 let mut conditional_false_jmp_idxs: Vec<Vec<usize>> =
1964 Vec::with_capacity(condition_blocks_count + 1);
1965 let mut jmp_instr_idx: Vec<usize> = Vec::with_capacity(condition_blocks_count);
1966 let mut condition_markers: Vec<usize> = Vec::with_capacity(condition_blocks_count);
1967
1968 let (true_jump_idxs, false_jump_idxs) =
1970 compile_short_circuit_condition(main_condition, v, ctx, state, output, false);
1971 conditional_false_jmp_idxs.push(false_jump_idxs);
1972
1973 let body_start = output.len();
1975 for j in true_jump_idxs {
1976 set_jmp_size(&mut output[j], (body_start - j) as u16);
1977 }
1978
1979 let cond_code = compile_expr(
1981 &code[0..main_code_limit],
1982 v,
1983 ctx.advance_offset(output.len() as u16),
1984 state,
1985 );
1986 output.extend(cond_code);
1987 if main_code_limit != code.len() {
1988 output.push(Instr::Jmp(0));
1989 jmp_instr_idx.push(output.len() - 1);
1990 }
1991
1992 for elem in &code[main_code_limit..] {
1993 if let Expr::ElseIfBlock(condition, code) = elem {
1994 condition_markers.push(output.len());
1995 let condition_id = condition
1996 .compile(v, ctx, state, output, None, false, true)
1997 .unwrap_id();
1998 state.free_reg(condition_id, v);
1999 add_cmp_false(condition_id, &mut 0, output, false);
2000 conditional_false_jmp_idxs.push(vec![output.len() - 1]);
2001 let cond_code = compile_expr(code, v, ctx.advance_offset(output.len() as u16), state);
2002 output.extend(cond_code);
2003 output.push(Instr::Jmp(0));
2004 jmp_instr_idx.push(output.len() - 1);
2005 } else if let Expr::ElseBlock(code) = elem {
2006 condition_markers.push(output.len());
2007 let cond_code = compile_expr(code, v, ctx.advance_offset(output.len() as u16), state);
2008 output.extend(cond_code);
2009 }
2010 }
2011
2012 for y in jmp_instr_idx {
2013 let diff = output.len() - y;
2014 output[y] = Instr::Jmp(diff as u16);
2015 }
2016 for (cm_idx, false_idxs) in conditional_false_jmp_idxs.iter().enumerate() {
2018 let target = if cm_idx < condition_markers.len() {
2019 condition_markers[cm_idx]
2020 } else {
2021 output.len()
2022 };
2023 for &y in false_idxs {
2024 set_jmp_size(&mut output[y], (target - y) as u16);
2025 }
2026 }
2027}
2028
2029fn compile_while_loop(
2030 condition: &Expr,
2031 code: &[Expr],
2032 v: &mut Vec<Variable>,
2033 ctx: Ctx,
2034 state: &mut State<'_>,
2035 output: &mut Vec<Instr>,
2036) {
2037 let output_len_before = output.len();
2038
2039 let (true_jump_idxs, false_jump_idxs) =
2040 compile_short_circuit_condition(condition, v, ctx, state, output, false);
2041
2042 let body_start = output.len();
2043 for j in true_jump_idxs {
2044 set_jmp_size(&mut output[j], (body_start - j) as u16);
2045 }
2046
2047 let loop_id = ctx.block_id + 1;
2049
2050 let mut cond_code = compile_expr(
2051 code,
2052 v,
2053 ctx.no_single_run().advance_offset(output.len() as u16),
2054 state,
2055 );
2056
2057 let exit = output.len() + cond_code.len() + 1;
2058 for j in false_jump_idxs {
2059 set_jmp_size(&mut output[j], (exit - j) as u16);
2060 }
2061
2062 let cond_len = (output.len() - output_len_before) as u16;
2063 let body_len = cond_code.len() as u16;
2064 let len = cond_len + body_len; parse_loop_flow_control(&mut cond_code, loop_id, body_len + 1, false, false);
2067 output.extend(cond_code);
2068 output.push(Instr::JmpBack(len));
2069}
2070
2071fn compile_for_loop(
2072 var_name: &SmolStr,
2073 array: &Expr,
2074 code: &[Expr],
2075 span: Span,
2076 v: &mut Vec<Variable>,
2077 ctx: Ctx,
2078 state: &mut State<'_>,
2079 output: &mut Vec<Instr>,
2080) {
2081 let real_var = var_name.as_str() != "_";
2082
2083 let array_type = array.infer_type(v, ctx, state);
2085 let mut array = array
2086 .compile(v, ctx, state, output, None, false, true)
2087 .unwrap_id();
2088
2089 if matches!(array_type, DataType::Map(_)) {
2092 let keys_reg = state.alloc_reg();
2093 output.push(Instr::CallLibFunc(LibFunc::Keys, array, keys_reg));
2094 array = keys_reg;
2095 }
2096
2097 let array_len_id = state.alloc_reg();
2098
2099 output.push(Instr::CallLibFunc(LibFunc::Len, array, array_len_id));
2100
2101 let index_id = if ctx.single_run {
2103 state.registers.push(0.into());
2104 (state.registers.len() - 1) as u16
2105 } else {
2106 let id = state.alloc_reg();
2107 output.push(Instr::SetInt(id, 0));
2108 id
2109 };
2110
2111 let condition_id = state.alloc_reg();
2113
2114 output.push(Instr::InfInt(index_id, array_len_id, condition_id));
2115
2116 let current_element_id = if real_var { state.alloc_reg() } else { 0 };
2118
2119 let v_len = v.len();
2120
2121 let is_str = array_type == DataType::String;
2122
2123 if real_var {
2124 v.push(Variable {
2125 name: var_name.clone(),
2126 register_id: current_element_id,
2127 var_type: match array_type {
2128 DataType::String => DataType::String,
2129 DataType::Array(a_type) => a_type.map_or(DataType::Null, |t| *t),
2130 DataType::Map(m) => m.0.map_or(DataType::Unknown, |t| t),
2132 t => {
2133 error_type_not_indexable(&t, span, true, ctx.file_idx, state.sources);
2134 }
2135 },
2136 });
2137 }
2138 let loop_id = ctx.block_id + 1;
2139
2140 let pending = real_var as u16;
2142
2143 let regs_before = state.registers.len() as u16;
2144 let mut cond_code = compile_expr(
2145 code,
2146 v,
2147 ctx.no_single_run()
2148 .advance_offset(output.len() as u16 + pending),
2149 state,
2150 );
2151 v.truncate(v_len);
2153 state.free_loop_scope_registers(regs_before, &cond_code, v);
2154
2155 let mut len = (cond_code.len() + 3) as u16 + pending;
2157 add_cmp_false(condition_id, &mut len, output, true);
2158
2159 if real_var {
2161 if is_str {
2162 output.push(Instr::GetIndexString(array, index_id, current_element_id));
2163 } else {
2164 output.push(Instr::GetIndexArray(array, index_id, current_element_id));
2165 }
2166 }
2167 parse_loop_flow_control(&mut cond_code, loop_id, len, true, false);
2168 output.extend(cond_code);
2170 output.push(Instr::IncInt(index_id));
2172
2173 output.push(Instr::JmpBack(len));
2175
2176 if ctx.single_run {
2177 state.free_reg(array_len_id, v);
2178 state.free_reg(index_id, v);
2179 state.free_reg(condition_id, v);
2180 if real_var {
2181 state.free_reg(current_element_id, v);
2182 }
2183 }
2184}
2185
2186fn compile_int_for_loop(
2187 var_name: &SmolStr,
2188 start_elem: &Expr,
2189 end_elem: &Expr,
2190 code: &[Expr],
2191 span1: Span,
2192 span2: Span,
2193 v: &mut Vec<Variable>,
2194 ctx: Ctx,
2195 state: &mut State<'_>,
2196 output: &mut Vec<Instr>,
2197) {
2198 let t1 = start_elem.infer_type(v, ctx, state);
2209 let t2 = end_elem.infer_type(v, ctx, state);
2210 if t1 != DataType::Int {
2211 error_range_invalid_type(span1, &t1, ctx.file_idx, state.sources);
2212 }
2213 if t2 != DataType::Int {
2214 error_range_invalid_type(span2, &t2, ctx.file_idx, state.sources);
2215 }
2216 let elem_id = if ctx.single_run {
2217 start_elem
2218 .compile(v, ctx, state, output, None, false, true)
2219 .unwrap_id()
2220 } else {
2221 let start_elem_id = start_elem
2222 .compile(v, ctx, state, output, None, false, true)
2223 .unwrap_id();
2224 let start_val = state.registers[start_elem_id as usize];
2225 let elem_id = state.alloc_reg();
2226 if state.const_registers.values().any(|&v| v == start_elem_id) && start_val.is_int() {
2227 output.push(Instr::SetInt(elem_id, start_val.as_int()));
2228 } else {
2229 output.push(Instr::Mov(start_elem_id, elem_id));
2230 }
2231 elem_id
2232 };
2233 let end_elem_id = end_elem
2234 .compile(v, ctx, state, output, None, false, true)
2235 .unwrap_id();
2236
2237 state.const_registers.retain(|_, &mut v| v != elem_id);
2239
2240 let v_len = v.len();
2241 v.push(Variable {
2242 name: var_name.clone(),
2243 register_id: elem_id,
2244 var_type: DataType::Int,
2245 });
2246 let loop_id = ctx.block_id + 1;
2247
2248 let jmp_idx = output.len();
2250 output.push(Instr::SupEqIntJmp(elem_id, end_elem_id, 0));
2251
2252 let regs_before = state.registers.len() as u16;
2253 let compiled_loop_code = compile_expr(
2254 code,
2255 v,
2256 ctx.no_single_run().advance_offset(output.len() as u16),
2257 state,
2258 );
2259 state.free_loop_scope_registers(regs_before, &compiled_loop_code, v);
2260 let compiled_loop_code_len = compiled_loop_code.len() as u16;
2261
2262 output.extend(compiled_loop_code);
2264
2265 output.push(Instr::IncInt(elem_id));
2267
2268 output.push(Instr::InfIntJmpBack(
2270 elem_id,
2271 end_elem_id,
2272 compiled_loop_code_len + 1,
2273 ));
2274
2275 let exit_size = (output.len() - jmp_idx) as u16;
2276 output[jmp_idx] = Instr::SupEqIntJmp(elem_id, end_elem_id, exit_size);
2277
2278 parse_loop_flow_control(&mut output[jmp_idx + 1..], loop_id, exit_size, true, false);
2279 v.truncate(v_len);
2280
2281 if ctx.single_run {
2282 state.free_reg(end_elem_id, v);
2283 state.free_reg(elem_id, v);
2284 }
2285}
2286
2287fn compile_loop_block(
2288 code: &[Expr],
2289 v: &mut Vec<Variable>,
2290 ctx: Ctx,
2291 state: &mut State<'_>,
2292 output: &mut Vec<Instr>,
2293) {
2294 let loop_id = ctx.block_id + 1;
2295 let regs_before = state.registers.len() as u16;
2296 let mut compiled = compile_expr(
2297 code,
2298 v,
2299 ctx.no_single_run().advance_offset(output.len() as u16),
2300 state,
2301 );
2302 state.free_loop_scope_registers(regs_before, &compiled, v);
2303 let code_length = compiled.len() as u16;
2304 parse_loop_flow_control(&mut compiled, loop_id, code_length + 1, false, true);
2305 output.extend(compiled);
2306 output.push(Instr::JmpBack(code_length));
2307}
2308
2309fn compile_try_catch_block(
2310 e: &[Expr],
2311 err_var: &SmolStr,
2312 catch_code: &[Expr],
2313 v: &mut Vec<Variable>,
2314 ctx: Ctx,
2315 state: &mut State<'_>,
2316 output: &mut Vec<Instr>,
2317) {
2318 output.push(Instr::StartErrorCatch(0, 0)); let err_catch_instr = output.len() - 1;
2320 let main_code = compile_expr(e, v, ctx.advance_offset(output.len() as u16), state);
2326 output.extend(main_code);
2327 output.push(Instr::StopErrorCatch);
2328 output.push(Instr::Jmp(0)); let jmp_catch_instr = output.len() - 1;
2330
2331 let v_len = v.len();
2332 let err_reg_id = state.alloc_reg();
2333 v.push(Variable {
2334 name: err_var.clone(),
2335 register_id: err_reg_id,
2336 var_type: DataType::String,
2337 });
2338 output[err_catch_instr] =
2339 Instr::StartErrorCatch((output.len() - err_catch_instr) as u16, err_reg_id);
2340 let catch_code = compile_expr(
2341 catch_code,
2342 v,
2343 ctx.advance_offset(output.len() as u16),
2344 state,
2345 );
2346 v.truncate(v_len);
2347 output.extend(catch_code);
2348 output[jmp_catch_instr] = Instr::Jmp((output.len() - jmp_catch_instr) as u16);
2349 state.free_reg(err_reg_id, v);
2350}
2351
2352fn compile_var_declaration(
2353 name: &SmolStr,
2354 value: &Expr,
2355 remaining_code: &[Expr],
2356 v: &mut Vec<Variable>,
2357 ctx: Ctx,
2358 state: &mut State<'_>,
2359 output: &mut Vec<Instr>,
2360) {
2361 let var_type = value.infer_type(v, ctx, state);
2362
2363 let var_id = if ctx.single_run {
2364 value
2365 .compile(v, ctx, state, output, None, true, true)
2366 .unwrap_id()
2367 } else {
2368 let src_id = value
2369 .compile(v, ctx, state, output, None, false, true)
2370 .unwrap_id();
2371 if code_modifies_variable(name, remaining_code) {
2372 let mutable_id = state.alloc_reg();
2373 move_reg_to_reg(output, src_id, mutable_id, state.registers[src_id as usize]);
2374 mutable_id
2375 } else {
2376 src_id
2377 }
2378 };
2379
2380 if let DataType::Fn(fn_id) = &var_type {
2381 state
2382 .namespace
2383 .symbols
2384 .push((name.clone(), SymbolKind::Fn(*fn_id)));
2385 }
2386 v.push(Variable {
2387 name: name.clone(),
2388 register_id: var_id,
2389 var_type,
2390 });
2391}
2392
2393fn compile_var_assignment(
2394 name: &SmolStr,
2395 value: &Expr,
2396 span: Span,
2397 v: &mut Vec<Variable>,
2398 ctx: Ctx,
2399 state: &mut State<'_>,
2400 output: &mut Vec<Instr>,
2401) {
2402 let var_type = value.infer_type(v, ctx, state);
2403 let var_pos = v.iter().rposition(|x| x.name == *name).unwrap_or_else(|| {
2404 compiler_errors::error_unknown_variable(name, span, v, ctx.file_idx, state.sources);
2405 });
2406 let id = v[var_pos].register_id;
2407
2408 if var_type == DataType::Int {
2409 let inc_dec: Option<(bool, &str)> = match value {
2411 Expr::Add(l, r, _, _) => {
2413 let src = if matches!(r.as_ref(), Expr::Int(1)) {
2414 Some(l.as_ref())
2415 } else if matches!(l.as_ref(), Expr::Int(1)) {
2416 Some(r.as_ref())
2417 } else {
2418 None
2419 };
2420 src.and_then(|e| {
2421 if let Expr::Var(src_name, _) = e {
2422 v.iter()
2423 .rfind(|x| x.name == *src_name)
2424 .filter(|x| x.var_type == DataType::Int)
2425 .map(|_| (true, src_name.as_str()))
2426 } else {
2427 None
2428 }
2429 })
2430 }
2431 Expr::Sub(l, r, _, _) => {
2433 if matches!(r.as_ref(), Expr::Int(1)) {
2434 if let Expr::Var(src_name, _) = l.as_ref() {
2435 v.iter()
2436 .rfind(|x| x.name == *src_name)
2437 .filter(|x| x.var_type == DataType::Int)
2438 .map(|_| (false, src_name.as_str()))
2439 } else {
2440 None
2441 }
2442 } else {
2443 None
2444 }
2445 }
2446 _ => None,
2447 };
2448 if let Some((is_inc, src_name)) = inc_dec {
2449 let src_id = v.iter().rfind(|x| x.name == src_name).unwrap().register_id;
2450 output.push(if src_id == id {
2451 if is_inc {
2452 Instr::IncInt(id)
2453 } else {
2454 Instr::DecInt(id)
2455 }
2456 } else {
2457 if is_inc {
2458 Instr::IncIntTo(src_id, id)
2459 } else {
2460 Instr::DecIntTo(src_id, id)
2461 }
2462 });
2463 return;
2464 }
2465 }
2466
2467 let output_len = output.len();
2468 let obj_id = value
2469 .compile(v, ctx, state, output, Some(id), false, true)
2470 .unwrap_id();
2471 if output.len() != output_len {
2472 if !move_to_id(output, id) {
2473 output.push(Instr::Mov(obj_id, id));
2474 }
2475 } else if state.const_registers.values().any(|&v| v == obj_id) {
2476 move_reg_to_reg(output, obj_id, id, state.registers[obj_id as usize]);
2477 } else {
2478 output.push(Instr::Mov(obj_id, id));
2479 }
2480 if !v
2481 .iter()
2482 .any(|var| &var.name != name && var.register_id == obj_id)
2483 {
2484 state.free_reg(obj_id, v);
2485 }
2486 v[var_pos].var_type = var_type;
2487}
2488
2489fn compile_struct_definition(
2490 name: &SmolStr,
2491 fields: &[(SmolStr, TypeExpr, Span)],
2492 span: Span,
2493 ctx: Ctx,
2494 state: &mut State<'_>,
2495 _output: &mut Vec<Instr>,
2496) {
2497 let struct_id = state.structs.len() as u16;
2498 state.structs.push(Struct {
2499 name: name.clone(),
2501 fields: Box::from([]),
2502 id: struct_id,
2503 name_span: span,
2504 });
2505 state.namespace.symbols.push((
2506 name.clone(),
2507 SymbolKind::Struct((state.structs.len() - 1) as u16),
2508 ));
2509 let parsed_fields = fields
2510 .iter()
2511 .map(|(f, f_t, f_span)| {
2512 (
2513 f.clone(),
2514 f_t.to_datatype(ctx.file_idx, state.namespace, state.sources),
2515 *f_span,
2516 )
2517 })
2518 .collect();
2519 state.structs[struct_id as usize].fields = parsed_fields;
2520}
2521
2522fn compile_function_definition(
2523 fn_name: &SmolStr,
2524 fn_args: &[(SmolStr, Option<TypeExpr>)],
2525 fn_code: &Rc<[Expr]>,
2526 span: Span,
2527 declared_return_type: Option<&(TypeExpr, Span)>,
2528 _v: &mut Vec<Variable>,
2529 ctx: Ctx,
2530 state: &mut State<'_>,
2531 _output: &mut Vec<Instr>,
2532) {
2533 if let Some(func) = state.fns.iter().find(|func| &func.name == fn_name) {
2534 compiler_errors::error_function_already_defined(func, span, ctx.file_idx, state.sources);
2535 }
2536 let mut callees = Vec::new();
2537 collect_direct_fn_calls(fn_code, &mut callees);
2538 state
2539 .namespace
2540 .symbols
2541 .push((fn_name.clone(), SymbolKind::Fn(state.fns.len() as u16)));
2542 state.fns.push(Function {
2543 name: fn_name.clone(),
2544 args: Box::from(fn_args.iter().map(|(a, t)| {
2545 (
2546 a.clone(),
2547 t.clone()
2548 .map(|t_e| t_e.to_datatype(ctx.file_idx, state.namespace, state.sources)),
2549 )
2550 }))
2551 .collect(),
2552 code: fn_code.clone(),
2553 impls: Vec::new(),
2554 is_recursive: None,
2555 returns_null: check_if_returns_void(fn_code),
2556 src_file: ctx.file_idx,
2557 return_type_cache: Vec::new(),
2558 direct_calls: callees.into_boxed_slice(),
2559 name_span: span,
2560 return_type: declared_return_type.map(|(t_e, t_span)| {
2561 (
2562 t_e.to_datatype(ctx.file_idx, state.namespace, state.sources),
2563 *t_span,
2564 )
2565 }),
2566 });
2567 state.fn_registers.push(Vec::new());
2568}
2569
2570fn compile_return(
2571 return_value: Option<&Expr>,
2572 v: &mut Vec<Variable>,
2573 ctx: Ctx,
2574 state: &mut State<'_>,
2575 output: &mut Vec<Instr>,
2576) {
2577 if let Some(x) = return_value {
2578 let id = x
2579 .compile(v, ctx, state, output, None, false, true)
2580 .unwrap_id();
2581 if ctx.is_compiling_recursive {
2582 output.push(Instr::RecursiveReturn(id));
2583 } else {
2584 output.push(Instr::Return(id));
2585 }
2586 }
2587}
2588
2589#[inline]
2590fn compile_loop_break(ctx: Ctx, output: &mut Vec<Instr>) {
2591 output.push(Instr::NotEqJmp(ctx.block_id + 1, 0, 0));
2592}
2593
2594#[inline]
2595fn compile_loop_continue(ctx: Ctx, output: &mut Vec<Instr>) {
2596 output.push(Instr::EqJmp(ctx.block_id + 1, 0, 0));
2597}
2598
2599#[inline]
2600fn compile_eval_block(
2601 code: &[Expr],
2602 v: &mut Vec<Variable>,
2603 ctx: Ctx,
2604 state: &mut State<'_>,
2605 output: &mut Vec<Instr>,
2606) {
2607 output.extend(compile_expr(
2608 code,
2609 v,
2610 ctx.set_offset(output.len() as u16),
2611 state,
2612 ));
2613}
2614
2615pub fn compile_expr(
2616 input: &[Expr],
2617 v: &mut Vec<Variable>,
2618 ctx: Ctx,
2619 state: &mut State<'_>,
2620) -> Vec<Instr> {
2621 let v_len = v.len();
2622 let fn_len = state.fns.len();
2623 let symbols_len = state.namespace.symbols.len();
2624 let mut output: Vec<Instr> = Vec::with_capacity(input.len());
2625 for (idx, x) in input.iter().enumerate() {
2626 if let Some(id) = x.compile_with_code_context(
2627 v,
2628 ctx,
2629 state,
2630 &mut output,
2631 None,
2632 false,
2633 &input[idx + 1..],
2634 false,
2635 ) {
2636 state.free_reg(id, v);
2637 }
2638 }
2639 v.truncate(v_len);
2640 state.fns.truncate(fn_len);
2641 state.namespace.symbols.truncate(symbols_len);
2642 output
2643}
2644
2645impl Expr {
2646 #[must_use]
2647 pub const fn is_constant_literal(&self) -> bool {
2648 matches!(
2649 self,
2650 Self::Int(_) | Self::Float(_) | Self::String(_) | Self::Bool(_) | Self::Null
2651 )
2652 }
2653 #[inline(always)]
2654 pub fn compile(
2655 &self,
2656 v: &mut Vec<Variable>,
2657 ctx: Ctx,
2658 state: &mut State<'_>,
2659 output: &mut Vec<Instr>,
2660 tgt_id: Option<u16>,
2661 var_assignment: bool,
2662 uses_id: bool,
2663 ) -> Option<u16> {
2664 self.compile_with_code_context(v, ctx, state, output, tgt_id, var_assignment, &[], uses_id)
2665 }
2666 pub fn compile_with_code_context(
2667 &self,
2668 v: &mut Vec<Variable>,
2669 ctx: Ctx,
2670 state: &mut State<'_>,
2671 output: &mut Vec<Instr>,
2672 tgt_id: Option<u16>,
2673 var_assignment: bool,
2674 remaining_code: &[Self],
2675 uses_id: bool,
2676 ) -> Option<u16> {
2677 match self {
2678 Self::Int(num) => {
2679 debug_assert!(uses_id);
2680 if var_assignment {
2681 state.registers.push((*num).into());
2682 return Some((state.registers.len() - 1) as u16);
2683 }
2684 let data = (*num).into();
2685 if let Some(&id) = state.const_registers.get(&data) {
2686 Some(id)
2687 } else {
2688 let id = state.registers.len() as u16;
2689 state.const_registers.insert(data, id);
2690 state.registers.push(data);
2691 Some(id)
2692 }
2693 }
2694 Self::Float(num) => {
2695 debug_assert!(uses_id);
2696 if var_assignment {
2697 state.registers.push((*num).into());
2698 return Some((state.registers.len() - 1) as u16);
2699 }
2700 let data = (*num).into();
2701 if let Some(&id) = state.const_registers.get(&data) {
2702 Some(id)
2703 } else {
2704 state.registers.push(data);
2705 let id = (state.registers.len() - 1) as u16;
2706 state.const_registers.insert(data, id);
2707 Some(id)
2708 }
2709 }
2710 Self::String(str) => {
2711 debug_assert!(uses_id);
2712 if var_assignment {
2713 state
2714 .registers
2715 .push(Data::p_str(str, &mut state.pools.strings));
2716 return Some((state.registers.len() - 1) as u16);
2717 }
2718 let data = Data::p_str(str, &mut state.pools.strings);
2719 if let Some(&id) = state.const_registers.get(&data) {
2720 Some(id)
2721 } else {
2722 let id = state.registers.len() as u16;
2723 state.const_registers.insert(data, id);
2724 state.registers.push(data);
2725 Some(id)
2726 }
2727 }
2728 Self::Null => {
2729 debug_assert!(uses_id);
2730 if var_assignment {
2731 state.registers.push(NULL);
2732 return Some((state.registers.len() - 1) as u16);
2733 }
2734 if let Some(&id) = state.const_registers.get(&NULL) {
2735 Some(id)
2736 } else {
2737 let id = state.registers.len() as u16;
2738 state.const_registers.insert(NULL, id);
2739 state.registers.push(NULL);
2740 Some(id)
2741 }
2742 }
2743 Self::Bool(bool) => {
2744 debug_assert!(uses_id);
2745 if var_assignment {
2746 state.registers.push((*bool).into());
2747 return Some((state.registers.len() - 1) as u16);
2748 }
2749 let data: Data = (*bool).into();
2750 if let Some(&id) = state.const_registers.get(&data) {
2751 Some(id)
2752 } else {
2753 let id = state.registers.len() as u16;
2754 state.const_registers.insert(data, id);
2755 state.registers.push(data);
2756 Some(id)
2757 }
2758 }
2759 Self::Var(name, span) => {
2760 debug_assert!(uses_id);
2761 if let Some(Variable {
2762 name: _,
2763 register_id,
2764 var_type: _,
2765 }) = v.iter().rfind(|v_temp| *name == v_temp.name)
2766 {
2767 Some(*register_id)
2768 } else if let Some((enum_id, variant_idx)) =
2769 resolve_enum_variant(std::slice::from_ref(name), state)
2770 {
2771 Some(compile_enum_construction(
2772 enum_id,
2773 variant_idx,
2774 &[],
2775 *span,
2776 &[],
2777 v,
2778 ctx,
2779 state,
2780 output,
2781 ))
2782 } else {
2783 compiler_errors::error_unknown_variable(
2784 name,
2785 *span,
2786 v,
2787 ctx.file_idx,
2788 state.sources,
2789 );
2790 }
2791 }
2792 Self::Array(array_items, spans) => {
2793 debug_assert!(uses_id);
2794 Some(compile_array_literal(
2795 array_items,
2796 spans,
2797 v,
2798 ctx,
2799 state,
2800 output,
2801 ))
2802 }
2803 Self::Struct(namespace, fields, span) => {
2804 debug_assert!(uses_id);
2805 Some(compile_struct_literal(
2806 namespace, fields, *span, v, ctx, state, output,
2807 ))
2808 }
2809 Self::Map(kv_pairs, span) => {
2810 debug_assert!(uses_id);
2811 Some(compile_map_literal(kv_pairs, *span, v, ctx, state, output))
2812 }
2813 Self::GetStructField(struct_expr, field, struct_span, field_span) => {
2814 debug_assert!(uses_id);
2815 Some(compile_struct_field_access(
2816 struct_expr,
2817 field,
2818 *struct_span,
2819 *field_span,
2820 v,
2821 ctx,
2822 state,
2823 output,
2824 ))
2825 }
2826 Self::ArrayGetIndex(array, index, span) => {
2828 debug_assert!(uses_id);
2829 Some(compile_array_indexing(
2830 array, index, *span, v, ctx, state, output,
2831 ))
2832 }
2833 Self::ArrayGetSlice(array, idx_start, idx_end, span) => {
2835 debug_assert!(uses_id);
2836 Some(compile_array_slice(
2837 array, idx_start, idx_end, *span, v, ctx, state, output,
2838 ))
2839 }
2840 Self::Mul(l, r, span1, span2) => {
2841 debug_assert!(uses_id);
2842 Some(uniform_op2(
2843 Instr::MulFloat,
2844 &DataType::Float,
2845 Instr::MulInt,
2846 &DataType::Int,
2847 "*",
2848 l,
2849 r,
2850 *span1,
2851 *span2,
2852 tgt_id,
2853 v,
2854 ctx,
2855 state,
2856 output,
2857 ))
2858 }
2859 Self::Div(l, r, span1, span2) => {
2860 debug_assert!(uses_id);
2861 Some(compile_div_op(
2862 l, r, *span1, *span2, tgt_id, v, ctx, state, output,
2863 ))
2864 }
2865 Self::Add(l, r, span1, span2) => {
2866 debug_assert!(uses_id);
2867 Some(compile_add_op(
2868 l, r, *span1, *span2, tgt_id, v, ctx, state, output,
2869 ))
2870 }
2871 Self::Sub(l, r, span1, span2) => {
2872 debug_assert!(uses_id);
2873 Some(compile_sub_op(
2874 l, r, *span1, *span2, tgt_id, v, ctx, state, output,
2875 ))
2876 }
2877 Self::Mod(l, r, span1, span2) => {
2878 debug_assert!(uses_id);
2879 Some(compile_mod_op(
2880 l, r, *span1, *span2, tgt_id, v, ctx, state, output,
2881 ))
2882 }
2883 Self::Pow(l, r, span1, span2) => {
2884 debug_assert!(uses_id);
2885 Some(uniform_op2(
2886 Instr::PowFloat,
2887 &DataType::Float,
2888 Instr::PowInt,
2889 &DataType::Int,
2890 "^",
2891 l,
2892 r,
2893 *span1,
2894 *span2,
2895 tgt_id,
2896 v,
2897 ctx,
2898 state,
2899 output,
2900 ))
2901 }
2902 Self::Eq(l, r) => {
2903 debug_assert!(uses_id);
2904 Some(compile_eq_op(l, r, tgt_id, v, ctx, state, output))
2905 }
2906 Self::NotEq(l, r) => {
2907 debug_assert!(uses_id);
2908 Some(compile_neq_op(l, r, tgt_id, v, ctx, state, output))
2909 }
2910 Self::Sup(l, r, span1, span2) => {
2911 debug_assert!(uses_id);
2912 Some(uniform_op2(
2913 Instr::SupFloat,
2914 &DataType::Float,
2915 Instr::SupInt,
2916 &DataType::Int,
2917 ">",
2918 l,
2919 r,
2920 *span1,
2921 *span2,
2922 tgt_id,
2923 v,
2924 ctx,
2925 state,
2926 output,
2927 ))
2928 }
2929 Self::SupEq(l, r, span1, span2) => {
2930 debug_assert!(uses_id);
2931 Some(uniform_op2(
2932 Instr::SupEqFloat,
2933 &DataType::Float,
2934 Instr::SupEqInt,
2935 &DataType::Int,
2936 ">=",
2937 l,
2938 r,
2939 *span1,
2940 *span2,
2941 tgt_id,
2942 v,
2943 ctx,
2944 state,
2945 output,
2946 ))
2947 }
2948 Self::Inf(l, r, span1, span2) => {
2949 debug_assert!(uses_id);
2950 Some(uniform_op2(
2951 Instr::InfFloat,
2952 &DataType::Float,
2953 Instr::InfInt,
2954 &DataType::Int,
2955 "<",
2956 l,
2957 r,
2958 *span1,
2959 *span2,
2960 tgt_id,
2961 v,
2962 ctx,
2963 state,
2964 output,
2965 ))
2966 }
2967 Self::InfEq(l, r, span1, span2) => {
2968 debug_assert!(uses_id);
2969 Some(uniform_op2(
2970 Instr::InfEqFloat,
2971 &DataType::Float,
2972 Instr::InfEqInt,
2973 &DataType::Int,
2974 "<=",
2975 l,
2976 r,
2977 *span1,
2978 *span2,
2979 tgt_id,
2980 v,
2981 ctx,
2982 state,
2983 output,
2984 ))
2985 }
2986 Self::BoolAnd(l, r, span1, span2) => {
2987 debug_assert!(uses_id);
2988 Some(compile_short_circuit_value(
2989 l, r, *span1, *span2, "&&", tgt_id, v, ctx, state, output,
2990 ))
2991 }
2992 Self::BoolOr(l, r, span1, span2) => {
2993 debug_assert!(uses_id);
2994 Some(compile_short_circuit_value(
2995 l, r, *span1, *span2, "||", tgt_id, v, ctx, state, output,
2996 ))
2997 }
2998 Self::Neg(l, span1, span2) => {
2999 debug_assert!(uses_id);
3000 Some(compile_neg_op(
3001 l, *span1, *span2, tgt_id, v, ctx, state, output,
3002 ))
3003 }
3004 Self::BoolNeg(l, span1, span2) => {
3005 debug_assert!(uses_id);
3006 Some(compile_bool_neg_op(
3007 l, *span1, *span2, tgt_id, v, ctx, state, output,
3008 ))
3009 }
3010 Self::InlineCondition(main_condition, code, span) => {
3011 debug_assert!(uses_id);
3012 Some(compile_inline_condition(
3013 main_condition,
3014 code,
3015 *span,
3016 v,
3017 ctx,
3018 state,
3019 output,
3020 tgt_id,
3021 ))
3022 }
3023 Self::FunctionCall(args, namespace, markers, args_indexes) if uses_id => Some(
3024 handle_functions(
3025 output,
3026 v,
3027 ctx,
3028 state,
3029 tgt_id,
3030 args,
3031 namespace,
3032 *markers,
3033 args_indexes,
3034 )
3035 .unwrap_or_else(|| {
3036 if let Some(&id) = state.const_registers.get(&NULL) {
3037 id
3038 } else {
3039 let id = state.registers.len() as u16;
3040 state.const_registers.insert(NULL, id);
3041 state.registers.push(NULL);
3042 id
3043 }
3044 }),
3045 ),
3046 Self::AnonymousFunction(_, _, _) => {
3047 debug_assert!(uses_id);
3048 if let Some(&id) = state.const_registers.get(&NULL) {
3049 Some(id)
3050 } else {
3051 let id = state.registers.len() as u16;
3052 state.const_registers.insert(NULL, id);
3053 state.registers.push(NULL);
3054 Some(id)
3055 }
3056 }
3057
3058 Self::ArrayModify(array, index, value, index_markers, elem_markers) => {
3064 debug_assert!(!uses_id);
3065 compile_array_index_assignment(
3066 array,
3067 index,
3068 value,
3069 *index_markers,
3070 *elem_markers,
3071 v,
3072 ctx,
3073 state,
3074 output,
3075 );
3076 None
3077 }
3078 Self::SetStructField(
3079 struct_expr,
3080 field,
3081 new_val,
3082 struct_span,
3083 field_span,
3084 value_span,
3085 ) => {
3086 debug_assert!(!uses_id);
3087 compile_struct_field_assignment(
3088 struct_expr,
3089 field,
3090 new_val,
3091 *struct_span,
3092 *field_span,
3093 *value_span,
3094 v,
3095 ctx,
3096 state,
3097 output,
3098 );
3099 None
3100 }
3101 Self::Condition(main_condition, code, _) => {
3102 debug_assert!(!uses_id);
3103 compile_condition(main_condition, code, v, ctx, state, output);
3104 None
3105 }
3106 Self::WhileBlock(condition, code) => {
3107 debug_assert!(!uses_id);
3108 compile_while_loop(condition, code, v, ctx, state, output);
3109 None
3110 }
3111 Self::ForLoop(var_name, array, code, span) => {
3112 debug_assert!(!uses_id);
3113 compile_for_loop(var_name, array, code, *span, v, ctx, state, output);
3114 None
3115 }
3116 Self::IntForLoop(var_name, start_elem, end_elem, code, span1, span2) => {
3117 debug_assert!(!uses_id);
3118 compile_int_for_loop(
3119 var_name, start_elem, end_elem, code, *span1, *span2, v, ctx, state, output,
3120 );
3121 None
3122 }
3123 Self::LoopBlock(code) => {
3124 debug_assert!(!uses_id);
3125 compile_loop_block(code, v, ctx, state, output);
3126 None
3127 }
3128 Self::TryCatchBlock(e, err_var, catch_code) => {
3129 debug_assert!(!uses_id);
3130 compile_try_catch_block(e, err_var, catch_code, v, ctx, state, output);
3131 None
3132 }
3133 Self::VarDeclare(name, value) => {
3134 debug_assert!(!uses_id);
3135 compile_var_declaration(name, value, remaining_code, v, ctx, state, output);
3136 None
3137 }
3138 Self::VarAssign(name, value, span) => {
3139 debug_assert!(!uses_id);
3140 compile_var_assignment(name, value, *span, v, ctx, state, output);
3141 None
3142 }
3143 Self::StructDeclare(name, fields, span) => {
3144 debug_assert!(!uses_id);
3145 compile_struct_definition(name, fields, *span, ctx, state, output);
3146 None
3147 }
3148 Self::EnumDeclare(name, variants, span) => {
3149 debug_assert!(!uses_id);
3150 compile_enum_definition(name, variants, *span, ctx, state);
3151 None
3152 }
3153 Self::Match(scrutinee, arms, wildcard, span) => {
3154 debug_assert!(!uses_id);
3155 compile_match(
3156 scrutinee,
3157 arms,
3158 wildcard.as_deref(),
3159 *span,
3160 v,
3161 ctx,
3162 state,
3163 output,
3164 );
3165 None
3166 }
3167 Self::NamespacedRef(path, span) => {
3168 debug_assert!(uses_id);
3169 if let Some((enum_id, variant_idx)) = resolve_enum_variant(path, state) {
3170 Some(compile_enum_construction(
3171 enum_id,
3172 variant_idx,
3173 &[],
3174 *span,
3175 &[],
3176 v,
3177 ctx,
3178 state,
3179 output,
3180 ))
3181 } else {
3182 compiler_errors::error_enum(
3183 "Unknown enum variant",
3184 &format!("{} does not name an enum variant", path.join("::")),
3185 *span,
3186 ctx.file_idx,
3187 state.sources,
3188 );
3189 }
3190 }
3191 Self::FunctionCall(args, namespace, markers, args_indexes) if !uses_id => {
3192 let output_id = handle_functions(
3193 output,
3194 v,
3195 ctx,
3196 state,
3197 tgt_id,
3198 args,
3199 namespace,
3200 *markers,
3201 args_indexes,
3202 );
3203 if let Some(id) = output_id {
3204 state.free_reg(id, v);
3205 }
3206 None
3207 }
3208 Self::ObjFunctionCall(obj, args, namespace, obj_span, fn_span, args_indexes)
3209 if !uses_id =>
3210 {
3211 let output_id = handle_method_calls(
3212 output,
3213 v,
3214 ctx,
3215 state,
3216 tgt_id,
3217 obj,
3218 args,
3219 namespace,
3220 *obj_span,
3221 *fn_span,
3222 args_indexes,
3223 );
3224 if let Some(id) = output_id {
3225 state.free_reg(id, v);
3226 }
3227 None
3228 }
3229 Self::ObjFunctionCall(obj, args, namespace, obj_span, fn_span, args_indexes)
3230 if uses_id =>
3231 {
3232 Some(
3233 handle_method_calls(
3234 output,
3235 v,
3236 ctx,
3237 state,
3238 tgt_id,
3239 obj,
3240 args,
3241 namespace,
3242 *obj_span,
3243 *fn_span,
3244 args_indexes,
3245 )
3246 .unwrap_or_else(|| {
3247 if let Some(&id) = state.const_registers.get(&NULL) {
3248 id
3249 } else {
3250 let id = state.registers.len() as u16;
3251 state.const_registers.insert(NULL, id);
3252 state.registers.push(NULL);
3253 id
3254 }
3255 }),
3256 )
3257 }
3258 Self::FunctionDecl(fn_name, fn_args, fn_code, span, return_type) => {
3259 debug_assert!(!uses_id);
3260 compile_function_definition(
3261 fn_name,
3262 fn_args,
3263 fn_code,
3264 *span,
3265 return_type.as_ref(),
3266 v,
3267 ctx,
3268 state,
3269 output,
3270 );
3271 None
3272 }
3273 Self::ReturnVal(return_value) => {
3274 debug_assert!(!uses_id);
3275 compile_return(return_value.as_ref().as_ref(), v, ctx, state, output);
3276 None
3277 }
3278 Self::Break => {
3279 debug_assert!(!uses_id);
3280 compile_loop_break(ctx, output);
3281 None
3282 }
3283 Self::Continue => {
3284 debug_assert!(!uses_id);
3285 compile_loop_continue(ctx, output);
3286 None
3287 }
3288 Self::EvalBlock(code) => {
3289 debug_assert!(!uses_id);
3290 compile_eval_block(code, v, ctx, state, output);
3291 None
3292 }
3293 _ => unsafe { unreachable_unchecked() },
3294 }
3295 }
3296}
3297
3298#[cfg(target_arch = "aarch64")]
3299const ARCH_SUFFIX: &str = "-aarch64";
3300#[cfg(target_arch = "x86_64")]
3301const ARCH_SUFFIX: &str = "-x86_64";
3302#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
3303const ARCH_SUFFIX: &str = "";
3304
3305#[derive(Debug, Copy, Clone)]
3306pub enum SymbolKind {
3307 Fn(u16),
3308 Struct(u16),
3309 Enum(u16),
3310}
3311
3312const fn symbol_ids_equal(a: SymbolKind, b: SymbolKind) -> bool {
3315 match (a, b) {
3316 (SymbolKind::Fn(x), SymbolKind::Fn(y))
3317 | (SymbolKind::Struct(x), SymbolKind::Struct(y))
3318 | (SymbolKind::Enum(x), SymbolKind::Enum(y)) => x == y,
3319 _ => false,
3320 }
3321}
3322
3323#[derive(Debug, Clone, Default)]
3324pub struct Namespace {
3325 pub symbols: Vec<(SmolStr, SymbolKind)>,
3326 pub children: Vec<(SmolStr, Self)>,
3327}
3328
3329impl Namespace {
3330 pub fn fns(&self) -> impl Iterator<Item = &(SmolStr, SymbolKind)> {
3331 self.symbols
3332 .iter()
3333 .filter(|(_, kind)| matches!(kind, SymbolKind::Fn(_)))
3334 }
3335 pub fn structs(&self) -> impl Iterator<Item = &(SmolStr, SymbolKind)> {
3336 self.symbols
3337 .iter()
3338 .filter(|(_, kind)| matches!(kind, SymbolKind::Struct(_)))
3339 }
3340 #[must_use]
3341 pub fn find_function(
3342 &self,
3343 path: &[SmolStr],
3344 function_name: &str,
3345 span: Span,
3346 file_idx: u16,
3347 sources: &[Source],
3348 ) -> Option<usize> {
3349 self.walk_to_namespace(path, span, file_idx, sources)
3350 .symbols
3351 .iter()
3352 .find_map(|(name, kind)| {
3353 if name.as_str() == function_name
3354 && let SymbolKind::Fn(fn_id) = kind
3355 {
3356 Some(*fn_id as usize)
3357 } else {
3358 None
3359 }
3360 })
3361 }
3362 #[must_use]
3363 pub fn find_struct(
3364 &self,
3365 path: &[SmolStr],
3366 struct_name: &str,
3367 span: Span,
3368 file_idx: u16,
3369 sources: &[Source],
3370 ) -> Option<usize> {
3371 self.walk_to_namespace(path, span, file_idx, sources)
3372 .symbols
3373 .iter()
3374 .find_map(|(name, kind)| {
3375 if name.as_str() == struct_name
3376 && let SymbolKind::Struct(struct_id) = kind
3377 {
3378 Some(*struct_id as usize)
3379 } else {
3380 None
3381 }
3382 })
3383 }
3384 #[must_use]
3389 pub fn find_enum(&self, path: &[SmolStr], enum_name: &str) -> Option<usize> {
3390 let mut current = self;
3391 for sub in path {
3392 current = ¤t.children.iter().find(|(name, _)| name == sub)?.1;
3393 }
3394 current.symbols.iter().find_map(|(name, kind)| {
3395 if name.as_str() == enum_name
3396 && let SymbolKind::Enum(enum_id) = kind
3397 {
3398 Some(*enum_id as usize)
3399 } else {
3400 None
3401 }
3402 })
3403 }
3404 #[must_use]
3408 pub fn try_find_function(&self, path: &[SmolStr], function_name: &str) -> Option<usize> {
3409 let mut current = self;
3410 for sub in path {
3411 current = ¤t.children.iter().find(|(name, _)| name == sub)?.1;
3412 }
3413 current.symbols.iter().find_map(|(name, kind)| {
3414 if name.as_str() == function_name
3415 && let SymbolKind::Fn(fn_id) = kind
3416 {
3417 Some(*fn_id as usize)
3418 } else {
3419 None
3420 }
3421 })
3422 }
3423 #[must_use]
3424 pub fn walk_to_namespace(
3425 &self,
3426 path: &[SmolStr],
3427 span: Span,
3428 file_idx: u16,
3429 sources: &[Source],
3430 ) -> &Self {
3431 let mut current = self;
3432 for sub in path {
3433 current = if let Some((_, child_namespace)) =
3434 current.children.iter().find(|(name, _)| name == sub)
3435 {
3436 child_namespace
3437 } else {
3438 error_unknown_namespace(path, span, file_idx, sources);
3439 };
3440 }
3441 current
3442 }
3443}
3444
3445#[cfg(not(target_arch = "wasm32"))]
3451fn load_auto_prelude(
3452 fns: &mut Vec<Function>,
3453 structs: &mut Vec<Struct>,
3454 enums: &mut Vec<EnumType>,
3455 fn_registers: &mut Vec<Vec<u16>>,
3456 dynamic_libs: &mut Vec<Dynamiclib>,
3457 sources: &mut Vec<Source>,
3458 namespace: &mut Namespace,
3459 files: &mut FxHashMap<PathBuf, Namespace>,
3460 file_namespaces: &mut FxHashMap<u16, Namespace>,
3461 pending_structs: &mut Vec<(u16, u16, Box<[(SmolStr, TypeExpr, Span)]>)>,
3462 pending_enums: &mut PendingEnums,
3463 pending_fns: &mut Vec<(
3464 u16,
3465 u16,
3466 Box<[(SmolStr, Option<TypeExpr>)]>,
3467 Option<(TypeExpr, Span)>,
3468 )>,
3469 pending_dylibs: &mut Vec<(
3470 u16,
3471 u16,
3472 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
3473 Rc<Library>,
3474 SmolStr,
3475 Span,
3476 )>,
3477 pending_host: &mut Vec<(
3478 u16,
3479 u16,
3480 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
3481 SmolStr,
3482 Span,
3483 )>,
3484) {
3485 const PRELUDE_REL: &str = "std/list.cdl";
3486 const PRELUDE_CHILD: &str = "list";
3487
3488 if namespace
3489 .children
3490 .iter()
3491 .any(|(name, _)| name.as_str() == PRELUDE_CHILD)
3492 {
3493 return;
3494 }
3495
3496 let path = if let Some(base) = std::env::var_os("CANDELA_LIB_PATH") {
3497 PathBuf::from(base).join(PRELUDE_REL)
3498 } else if let Ok(exe) = std::env::current_exe() {
3499 exe.canonicalize()
3500 .unwrap_or(exe)
3501 .parent()
3502 .unwrap_or_else(|| Path::new("."))
3503 .join("libs")
3504 .join(PRELUDE_REL)
3505 } else {
3506 return;
3507 };
3508
3509 if let Some(cached) = files.get(&path) {
3510 namespace
3511 .children
3512 .push((PRELUDE_CHILD.into(), cached.clone()));
3513 return;
3514 }
3515
3516 let Ok(contents) = std::fs::read_to_string(&path) else {
3517 return;
3518 };
3519
3520 let child_src_idx = sources.len() as u16;
3521 let file_name: SmolStr = path.to_str().unwrap_or(PRELUDE_REL).into();
3522 sources.push(Source {
3523 filename: file_name,
3524 contents,
3525 });
3526 let file_code = parser::parse(&sources.last().unwrap().contents, sources.last().unwrap());
3527
3528 let mut child_namespace = Namespace {
3529 symbols: Vec::new(),
3530 children: Vec::new(),
3531 };
3532 parse_toplevel(
3533 file_code,
3534 &path,
3535 child_src_idx,
3536 fns,
3537 structs,
3538 enums,
3539 fn_registers,
3540 dynamic_libs,
3541 sources,
3542 &mut child_namespace,
3543 files,
3544 file_namespaces,
3545 pending_structs,
3546 pending_enums,
3547 pending_fns,
3548 pending_dylibs,
3549 pending_host,
3550 );
3551 files.insert(path, child_namespace.clone());
3552 namespace
3553 .children
3554 .push((PRELUDE_CHILD.into(), child_namespace));
3555}
3556
3557#[cfg(not(target_arch = "wasm32"))]
3576fn open_dylib(file_path: &Path, filename: &str, is_logical: bool) -> Option<libloading::Library> {
3577 if is_logical {
3578 let file_dir = file_path.parent().unwrap_or_else(|| Path::new("."));
3579 let mut candidates = vec![file_dir.join(filename)];
3580 let cwd_candidate = Path::new(".").join(filename);
3581 if !candidates.contains(&cwd_candidate) {
3582 candidates.push(cwd_candidate);
3583 }
3584 for candidate in &candidates {
3585 if let Ok(lib) = unsafe { libloading::Library::new(candidate) } {
3586 return Some(lib);
3587 }
3588 }
3589 }
3590 unsafe { libloading::Library::new(filename) }.ok()
3591}
3592
3593type PendingEnums = Vec<(u16, u16, Box<[(SmolStr, Box<[TypeExpr]>, Span)]>)>;
3598
3599fn parse_toplevel(
3600 code: Vec<Expr>,
3601 file_path: &Path,
3602 src_file_idx: u16,
3603 fns: &mut Vec<Function>,
3604 structs: &mut Vec<Struct>,
3605 enums: &mut Vec<EnumType>,
3606 fn_registers: &mut Vec<Vec<u16>>,
3607 dynamic_libs: &mut Vec<Dynamiclib>,
3608 sources: &mut Vec<Source>,
3609 namespace: &mut Namespace,
3610 files: &mut FxHashMap<PathBuf, Namespace>,
3611 file_namespaces: &mut FxHashMap<u16, Namespace>,
3612 pending_structs: &mut Vec<(u16, u16, Box<[(SmolStr, TypeExpr, Span)]>)>,
3613 pending_enums: &mut PendingEnums,
3614 pending_fns: &mut Vec<(
3615 u16,
3616 u16,
3617 Box<[(SmolStr, Option<TypeExpr>)]>,
3618 Option<(TypeExpr, Span)>,
3619 )>,
3620 #[cfg(not(target_arch = "wasm32"))] pending_dylibs: &mut Vec<(
3621 u16,
3622 u16,
3623 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
3624 Rc<Library>,
3625 SmolStr,
3628 Span,
3629 )>,
3630 pending_host: &mut Vec<(
3631 u16,
3632 u16,
3633 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
3634 SmolStr,
3635 Span,
3636 )>,
3637) {
3638 let mut imports = Vec::new();
3639 for expr in code {
3640 match expr {
3641 Expr::FunctionDecl(fn_name, fn_args, fn_code, span, fn_return_type) => {
3642 if let Some((_, SymbolKind::Fn(func_id))) =
3643 namespace.symbols.iter().rfind(|(f, _)| f == &fn_name)
3644 {
3645 let func = &fns[*func_id as usize];
3646 compiler_errors::error_function_already_defined(
3647 func,
3648 span,
3649 src_file_idx,
3650 sources,
3651 );
3652 }
3653 fn_registers.push(Vec::new());
3654 let returns_void = check_if_returns_void(&fn_code);
3655 let mut callees = Vec::new();
3656 collect_direct_fn_calls(&fn_code, &mut callees);
3657
3658 let fn_id = fns.len() as u16;
3659 fns.push(Function {
3660 name: fn_name.clone(),
3661 args: Box::new([]),
3662 code: fn_code,
3663 impls: Vec::new(),
3664 is_recursive: None,
3665 returns_null: returns_void,
3666 src_file: src_file_idx,
3667 return_type_cache: Vec::new(),
3668 direct_calls: callees.into_boxed_slice(),
3669 name_span: span,
3670 return_type: None,
3673 });
3674 pending_fns.push((fn_id, src_file_idx, fn_args, fn_return_type));
3675 namespace.symbols.push((fn_name, SymbolKind::Fn(fn_id)));
3676 }
3677 Expr::StructDeclare(name, fields, span) => {
3678 let struct_id = structs.len() as u16;
3679 structs.push(Struct {
3680 name: name.clone(),
3681 fields: Box::from([]),
3682 id: struct_id,
3683 name_span: span,
3684 });
3685 namespace
3686 .symbols
3687 .push((name, SymbolKind::Struct(struct_id)));
3688 pending_structs.push((struct_id, src_file_idx, fields));
3689 }
3690 Expr::EnumDeclare(name, variants, span) => {
3691 let enum_id = enums.len() as u16;
3692 enums.push(EnumType {
3693 name: name.clone(),
3694 variants: Box::from([]),
3695 id: enum_id,
3696 name_span: span,
3697 });
3698 namespace.symbols.push((name, SymbolKind::Enum(enum_id)));
3699 pending_enums.push((enum_id, src_file_idx, variants));
3700 }
3701 #[cfg(target_arch = "wasm32")]
3702 Expr::ImportDylib(..) => wasm_error("WASM does not support loading dynamic libraries"),
3703 #[cfg(target_arch = "wasm32")]
3704 Expr::ImportFile(..) => wasm_error("WASM does not support importing files"),
3705 import @ (Expr::ImportFile(..) | Expr::ImportDylib(..) | Expr::HostBlock(..)) => {
3706 imports.push(import);
3707 }
3708 _ => {}
3709 }
3710 }
3711
3712 files.insert(file_path.to_path_buf(), namespace.clone());
3713
3714 #[cfg(not(target_arch = "wasm32"))]
3720 if src_file_idx == 0 {
3721 load_auto_prelude(
3722 fns,
3723 structs,
3724 enums,
3725 fn_registers,
3726 dynamic_libs,
3727 sources,
3728 namespace,
3729 files,
3730 file_namespaces,
3731 pending_structs,
3732 pending_enums,
3733 pending_fns,
3734 pending_dylibs,
3735 pending_host,
3736 );
3737 }
3738
3739 let mut merged_symbol_origins: Vec<(SmolStr, SmolStr)> = Vec::new();
3742
3743 for import in imports {
3744 match import {
3745 #[cfg(not(target_arch = "wasm32"))]
3746 Expr::ImportDylib(path, fn_signatures, span) => {
3747 let spec = path.clone();
3750 let is_logical = !spec.contains('/')
3755 && !spec.contains('\\')
3756 && !Path::new(spec.as_str()).is_absolute()
3757 && Path::new(spec.as_str()).extension().is_none();
3758
3759 let (open_target, dylib_name): (SmolStr, SmolStr) = if is_logical {
3760 (
3763 resolve_library_filename(spec.as_str(), TargetOs::CURRENT).into(),
3764 spec.clone(),
3765 )
3766 } else {
3767 let base_path = if Path::new(spec.as_str()).is_relative() {
3768 file_path
3769 .parent()
3770 .unwrap_or_else(|| Path::new("."))
3771 .join(spec.as_str())
3772 .to_string_lossy()
3773 .to_smolstr()
3774 } else {
3775 spec.clone()
3776 };
3777 let dylib_name = PathBuf::from(base_path.as_str())
3778 .file_prefix()
3779 .and_then(|s| s.to_str())
3780 .unwrap_or(base_path.as_str())
3781 .to_smolstr();
3782 let resolved = if Path::new(base_path.as_str()).extension().is_none() {
3786 let arch_path = format!(
3787 "{base_path}{ARCH_SUFFIX}.{}",
3788 TargetOs::CURRENT.dynamic_lib_extension()
3789 );
3790 if Path::new(&arch_path).exists() {
3791 SmolStr::from(arch_path)
3792 } else {
3793 resolve_library_filename(base_path.as_str(), TargetOs::CURRENT).into()
3794 }
3795 } else {
3796 base_path
3797 };
3798 (resolved, dylib_name)
3799 };
3800
3801 let lib = Rc::new(
3802 open_dylib(file_path, open_target.as_str(), is_logical)
3803 .unwrap_or_else(|| error_cannot_load_dynlib(span, src_file_idx, sources)),
3804 );
3805 pending_dylibs.push((
3806 src_file_idx,
3807 dynamic_libs.len() as u16,
3808 fn_signatures,
3809 lib,
3810 spec,
3811 span,
3812 ));
3813 dynamic_libs.push(Dynamiclib {
3814 name: dylib_name,
3815 fns: Box::new([]),
3816 is_host: false,
3817 });
3818 }
3819 Expr::HostBlock(host_namespace, fn_signatures, span) => {
3820 pending_host.push((
3821 src_file_idx,
3822 dynamic_libs.len() as u16,
3823 fn_signatures,
3824 host_namespace.clone(),
3825 span,
3826 ));
3827 dynamic_libs.push(Dynamiclib {
3828 name: host_namespace,
3829 fns: Box::new([]),
3830 is_host: true,
3831 });
3832 }
3833 Expr::ImportFile(path, alias, is_logical, span) => {
3834 let shipped_lib = |path: &SmolStr| -> Option<PathBuf> {
3840 if let Some(base) = std::env::var_os("CANDELA_LIB_PATH") {
3841 return Some(PathBuf::from(base).join(path.as_str()));
3842 }
3843 std::env::current_exe().ok().map(|p| {
3844 p.canonicalize()
3845 .unwrap_or(p)
3846 .parent()
3847 .unwrap_or_else(|| Path::new("."))
3848 .join("libs")
3849 .join(path.as_str())
3850 })
3851 };
3852
3853 let file_path = if is_logical {
3854 shipped_lib(&path).unwrap_or_else(|| {
3859 error_cannot_read_file(span, src_file_idx, sources);
3860 })
3861 } else {
3862 file_path
3865 .parent()
3866 .unwrap_or_else(|| Path::new("."))
3867 .join(path.as_str())
3868 .canonicalize()
3869 .unwrap_or_else(|_| {
3870 shipped_lib(&path).unwrap_or_else(|| {
3871 error_cannot_read_file(span, src_file_idx, sources);
3872 })
3873 })
3874 };
3875
3876 let child_namespace = if let Some(cached) = files.get(&file_path) {
3877 cached.clone()
3878 } else {
3879 let file_contents = std::fs::read_to_string(&file_path).unwrap_or_else(|_| {
3880 error_cannot_read_file(span, src_file_idx, sources);
3881 });
3882 let file_name: SmolStr = file_path.to_str().unwrap_or(path.as_str()).into();
3883
3884 let child_src_idx = sources.len() as u16;
3885
3886 sources.push(Source {
3887 filename: file_name.clone(),
3888 contents: file_contents,
3889 });
3890
3891 let file_code =
3893 parser::parse(&sources.last().unwrap().contents, sources.last().unwrap());
3894
3895 let mut child_namespace = Namespace {
3896 symbols: Vec::new(),
3897 children: Vec::new(),
3898 };
3899
3900 parse_toplevel(
3901 file_code,
3902 &file_path,
3903 child_src_idx,
3904 fns,
3905 structs,
3906 enums,
3907 fn_registers,
3908 dynamic_libs,
3909 sources,
3910 &mut child_namespace,
3911 files,
3912 file_namespaces,
3913 pending_structs,
3914 pending_enums,
3915 pending_fns,
3916 #[cfg(not(target_arch = "wasm32"))]
3917 pending_dylibs,
3918 pending_host,
3919 );
3920 files.insert(file_path.clone(), child_namespace.clone());
3921 child_namespace
3922 };
3923
3924 if let Some(alias) = alias {
3925 namespace.children.push((alias, child_namespace));
3928 } else {
3929 let module_display: SmolStr = if is_logical {
3933 path.strip_suffix(".cdl").unwrap_or(path.as_str()).into()
3934 } else {
3935 path.clone()
3936 };
3937 for (name, kind) in child_namespace.symbols {
3938 if let Some((_, existing)) =
3939 namespace.symbols.iter().find(|(n, _)| n == &name)
3940 {
3941 if symbol_ids_equal(*existing, kind) {
3945 continue;
3946 }
3947 let existing_origin = merged_symbol_origins
3948 .iter()
3949 .find(|(n, _)| n == &name)
3950 .map_or_else(
3951 || String::from("defined in this file"),
3952 |(_, module)| format!("imported from \"{module}\""),
3953 );
3954 compiler_errors::error_import_symbol_collision(
3955 &name,
3956 &existing_origin,
3957 &module_display,
3958 span,
3959 src_file_idx,
3960 sources,
3961 );
3962 }
3963 merged_symbol_origins.push((name.clone(), module_display.clone()));
3964 namespace.symbols.push((name, kind));
3965 }
3966 }
3967 }
3968 _ => unsafe { unreachable_unchecked() },
3969 }
3970 }
3971 file_namespaces.insert(src_file_idx, namespace.clone());
3972}
3973
3974fn resolve_types(
3975 structs: &mut [Struct],
3976 enums: &mut [EnumType],
3977 fns: &mut [Function],
3978 pending_structs: Vec<(u16, u16, Box<[(SmolStr, TypeExpr, Span)]>)>,
3979 pending_enums: PendingEnums,
3980 pending_fns: Vec<(
3981 u16,
3982 u16,
3983 Box<[(SmolStr, Option<TypeExpr>)]>,
3984 Option<(TypeExpr, Span)>,
3985 )>,
3986 #[cfg(not(target_arch = "wasm32"))] pending_dylibs: Vec<(
3987 u16,
3988 u16,
3989 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
3990 Rc<Library>,
3991 SmolStr,
3994 Span,
3995 )>,
3996 pending_host: Vec<(
3997 u16,
3998 u16,
3999 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
4000 SmolStr,
4001 Span,
4002 )>,
4003 file_namespaces: &FxHashMap<u16, Namespace>,
4004 dynamic_libs_fns: &mut Vec<DynamicLibFn>,
4005 host_fns: &mut Vec<HostFnSig>,
4006 dynamic_libs: &mut [Dynamiclib],
4007 sources: &[Source],
4008) {
4009 for (struct_id, src_file_idx, fields) in pending_structs {
4010 let resolved_fields = fields
4011 .iter()
4012 .map(|(field_name, field_type, field_span)| {
4013 (
4014 field_name.clone(),
4015 field_type.to_datatype(src_file_idx, &file_namespaces[&src_file_idx], sources),
4016 *field_span,
4017 )
4018 })
4019 .collect();
4020 structs[struct_id as usize].fields = resolved_fields;
4021 }
4022 for (enum_id, src_file_idx, variants) in pending_enums {
4023 let resolved_variants = variants
4024 .iter()
4025 .map(|(variant_name, payload, name_span)| EnumVariant {
4026 name: variant_name.clone(),
4027 payload: payload
4028 .iter()
4029 .map(|t| t.to_datatype(src_file_idx, &file_namespaces[&src_file_idx], sources))
4030 .collect(),
4031 name_span: *name_span,
4032 })
4033 .collect();
4034 enums[enum_id as usize].variants = resolved_variants;
4035 }
4036 for (fn_id, src_file_idx, args, return_type) in pending_fns {
4037 let resolved_args = args
4038 .iter()
4039 .map(|(arg_name, arg_type)| {
4040 (
4041 arg_name.clone(),
4042 arg_type.clone().map(|t_e| {
4043 t_e.to_datatype(src_file_idx, &file_namespaces[&src_file_idx], sources)
4044 }),
4045 )
4046 })
4047 .collect();
4048 fns[fn_id as usize].args = resolved_args;
4049 fns[fn_id as usize].return_type = return_type.map(|(t_e, t_span)| {
4050 (
4051 t_e.to_datatype(src_file_idx, &file_namespaces[&src_file_idx], sources),
4052 t_span,
4053 )
4054 });
4055 }
4056 #[cfg(not(target_arch = "wasm32"))]
4057 for (src_file_idx, dynlib_id, fn_signatures, lib, library_spec, span) in pending_dylibs {
4058 let namespace = &file_namespaces[&src_file_idx];
4059 let fns = fn_signatures
4060 .iter()
4061 .map(|(fn_name, fn_args, fn_return_type, fn_name_span)| {
4062 let fn_args = fn_args
4063 .iter()
4064 .map(|t| t.to_datatype(src_file_idx, namespace, sources))
4065 .collect::<Vec<DataType>>()
4066 .into_boxed_slice();
4067 let fn_return_type = fn_return_type.to_datatype(src_file_idx, namespace, sources);
4068 let return_val = FnSignature {
4069 name: fn_name.clone(),
4070 args: fn_args.clone(),
4071 return_type: fn_return_type.clone(),
4072 id: dynamic_libs_fns.len() as u16,
4073 variadic: false,
4074 };
4075 let arg_types: Vec<_> = fn_args.iter().map(|t| t.to_c_type(structs)).collect();
4076 let return_type = fn_return_type.to_c_type(structs);
4077 let cif = libffi::middle::Cif::new(arg_types, return_type);
4078 let ptr = unsafe {
4079 libffi::middle::CodePtr(
4080 lib.get::<*const ()>(fn_name.as_bytes())
4081 .unwrap_or_else(|_| {
4082 error_cannot_find_dynlib_symbol(
4083 fn_name,
4084 *fn_name_span,
4085 span,
4086 src_file_idx,
4087 sources,
4088 );
4089 })
4090 .try_as_raw_ptr()
4091 .unwrap_unchecked(),
4092 )
4093 };
4094
4095 let mut types = vec![fn_return_type];
4096 types.extend(fn_args);
4097
4098 dynamic_libs_fns.push(DynamicLibFn {
4099 types: Box::from(types),
4100 library: library_spec.clone(),
4101 symbol: fn_name.clone(),
4102 _lib: Rc::clone(&lib),
4103 ptr,
4104 cif,
4105 });
4106 return_val
4107 })
4108 .collect();
4109 dynamic_libs[dynlib_id as usize].fns = fns;
4110 }
4111
4112 for (src_file_idx, dynlib_id, fn_signatures, host_namespace, _span) in pending_host {
4117 let namespace = &file_namespaces[&src_file_idx];
4118 let fns = fn_signatures
4119 .iter()
4120 .map(|(fn_name, fn_args, fn_return_type, _fn_name_span)| {
4121 let variadic = fn_args.len() == 1
4125 && matches!(&fn_args[0], TypeExpr::Identifier(s, _) if s.as_str() == "...");
4126 let fn_args = if variadic {
4127 Box::from([])
4128 } else {
4129 fn_args
4130 .iter()
4131 .map(|t| t.to_datatype(src_file_idx, namespace, sources))
4132 .collect::<Vec<DataType>>()
4133 .into_boxed_slice()
4134 };
4135 let fn_return_type = fn_return_type.to_datatype(src_file_idx, namespace, sources);
4136 let return_val = FnSignature {
4137 name: fn_name.clone(),
4138 args: fn_args.clone(),
4139 return_type: fn_return_type.clone(),
4140 id: host_fns.len() as u16,
4141 variadic,
4142 };
4143
4144 let mut types = vec![fn_return_type];
4145 types.extend(fn_args);
4146 host_fns.push(HostFnSig {
4147 types: types.into_boxed_slice(),
4148 namespace: host_namespace.clone(),
4149 name: fn_name.clone(),
4150 variadic,
4151 });
4152 return_val
4153 })
4154 .collect();
4155 dynamic_libs[dynlib_id as usize].fns = fns;
4156 }
4157}
4158
4159pub struct CompileOutput {
4167 pub instructions: Vec<Instr>,
4168 pub registers: Vec<Data>,
4169 pub pools: Pools,
4170 pub instr_src: Vec<InstrSrc>,
4171 pub fn_registers: Vec<Vec<u16>>,
4172 pub dyn_lib_fns: Vec<DynamicLibFn>,
4173 pub host_fns: Vec<HostFnSig>,
4174 pub allocated_arg_count: usize,
4175 pub allocated_call_depth: usize,
4176 pub sources: Vec<Source>,
4177 pub structs: Vec<Struct>,
4178 pub enums: Vec<EnumType>,
4179 pub functions: Vec<Function>,
4180 pub dyn_libs: Vec<Dynamiclib>,
4181 pub namespace: Namespace,
4182 pub const_registers: FxHashMap<Data, u16>,
4183 pub free_registers: Vec<u16>,
4184}
4185
4186#[must_use]
4187pub fn compile(contents: String, filename: &str, debug: bool) -> CompileOutput {
4188 #[cfg(not(target_arch = "wasm32"))]
4189 let now = std::time::Instant::now();
4190
4191 type_system::reset_inference_state();
4194
4195 let main_src = Source {
4196 filename: SmolStr::from(filename),
4197 contents,
4198 };
4199
4200 let code = parser::parse(&main_src.contents, &main_src);
4201
4202 #[cfg(not(target_arch = "wasm32"))]
4203 if debug {
4204 println!("PARSING TIME: {:.2?}", now.elapsed());
4205 }
4206
4207 let mut variables: Vec<Variable> = Vec::new();
4208 let mut registers: Vec<Data> = Vec::new();
4209 let mut pools: Pools = Pools {
4210 objs: Pool::with_capacity(10),
4211 maps: Pool::with_capacity(2),
4212 strings: Pool::with_capacity(10),
4213 };
4214 let mut instr_src: Vec<InstrSrc> = Vec::new();
4215 let mut fn_registers: Vec<Vec<u16>> = Vec::new();
4216 let mut functions: Vec<Function> = Vec::new();
4217 let mut structs: Vec<Struct> = Vec::new();
4218 let mut enums: Vec<EnumType> = Vec::new();
4219 let mut dyn_libs: Vec<Dynamiclib> = Vec::new();
4220 let mut dyn_lib_fns: Vec<DynamicLibFn> = Vec::new();
4221 let mut host_fns: Vec<HostFnSig> = Vec::new();
4222 let mut allocated_arg_count = 0;
4223 let mut allocated_call_depth = 0;
4224 let mut const_registers: FxHashMap<Data, u16> = FxHashMap::default();
4225 let mut free_registers = Vec::new();
4226
4227 let mut sources: Vec<Source> = vec![main_src];
4228 let main_path = PathBuf::from(filename)
4229 .canonicalize()
4230 .unwrap_or_else(|_| PathBuf::from(filename));
4231 let mut namespace = Namespace::default();
4232
4233 let mut files: FxHashMap<PathBuf, Namespace> = FxHashMap::default();
4234 let mut file_namespaces: FxHashMap<u16, Namespace> = FxHashMap::default();
4235 let mut pending_structs: Vec<(u16, u16, Box<[(SmolStr, TypeExpr, Span)]>)> = Vec::new();
4236 let mut pending_enums: PendingEnums = Vec::new();
4237 let mut pending_fns: Vec<(
4238 u16,
4239 u16,
4240 Box<[(SmolStr, Option<TypeExpr>)]>,
4241 Option<(TypeExpr, Span)>,
4242 )> = Vec::with_capacity(2);
4243 #[cfg(not(target_arch = "wasm32"))]
4244 let mut pending_dylibs: Vec<(
4245 u16,
4246 u16,
4247 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
4248 Rc<Library>,
4249 SmolStr,
4250 Span,
4251 )> = Vec::new();
4252 let mut pending_host: Vec<(
4253 u16,
4254 u16,
4255 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
4256 SmolStr,
4257 Span,
4258 )> = Vec::new();
4259
4260 parse_toplevel(
4261 code,
4262 &main_path,
4263 0,
4264 &mut functions,
4265 &mut structs,
4266 &mut enums,
4267 &mut fn_registers,
4268 &mut dyn_libs,
4269 &mut sources,
4270 &mut namespace,
4271 &mut files,
4272 &mut file_namespaces,
4273 &mut pending_structs,
4274 &mut pending_enums,
4275 &mut pending_fns,
4276 #[cfg(not(target_arch = "wasm32"))]
4277 &mut pending_dylibs,
4278 &mut pending_host,
4279 );
4280 resolve_types(
4281 &mut structs,
4282 &mut enums,
4283 &mut functions,
4284 pending_structs,
4285 pending_enums,
4286 pending_fns,
4287 #[cfg(not(target_arch = "wasm32"))]
4288 pending_dylibs,
4289 pending_host,
4290 &file_namespaces,
4291 &mut dyn_lib_fns,
4292 &mut host_fns,
4293 &mut dyn_libs,
4294 &sources,
4295 );
4296
4297 let ctx = Ctx {
4298 block_id: 0,
4299 is_compiling_recursive: false,
4300 file_idx: 0,
4301 single_run: true,
4302 offset: 0,
4303 };
4304 let mut state = State {
4305 registers: &mut registers,
4306 fns: &mut functions,
4307 structs: &mut structs,
4308 enums: &mut enums,
4309 pools: &mut pools,
4310 instr_src: &mut instr_src,
4311 fn_registers: &mut fn_registers,
4312 dyn_libs: &mut dyn_libs,
4313 allocated_arg_count: &mut allocated_arg_count,
4314 allocated_call_depth: &mut allocated_call_depth,
4315 const_registers: &mut const_registers,
4316 free_registers: &mut free_registers,
4317 sources: &mut sources,
4318 reserved_registers: FxHashSet::default(),
4319 namespace: &mut namespace,
4320 };
4321 let mut instructions = compile_expr(
4322 &state.fns
4323 .iter()
4324 .find(|func| func.name == "main" && func.src_file == 0)
4325 .unwrap_or_else(|| {
4326 #[cfg(target_arch = "wasm32")]
4327 wasm_error("Cannot find main function");
4328
4329 if crate::errors::diagnostics_enabled() {
4330 crate::errors::emit_diagnostic(
4331 state.sources[0].filename.as_str(),
4332 0..0,
4333 String::from("Cannot find main function"),
4334 "no_main_function",
4335 );
4336 }
4337 eprintln!(
4338 "--------------\n{RED}CANDELA RUNTIME ERROR:{RESET}\nCannot find {BLUE}{BOLD}main{RESET} function\n--------------",
4339 );
4340 std::process::exit(1);
4341 })
4342 .code
4343 .clone(),
4344 &mut variables,
4345 ctx,
4346 &mut state,
4347 );
4348 instructions.push(Instr::Halt(0));
4349
4350 #[cfg(debug_assertions)]
4351 if debug {
4352 println!("---- DEBUG ----");
4353 if !pools.objs.is_empty() {
4354 println!("--- ARRAYS ---");
4355 for (i, data) in pools.objs.iter().enumerate() {
4356 println!(" {i} {data:?}");
4357 }
4358 }
4359 println!("-- REGISTERS --");
4360 for (i, data) in registers.iter().enumerate() {
4361 println!(
4362 " [{i}] {}",
4363 data.format(
4364 &pools.objs,
4365 &pools.strings,
4366 &pools.maps,
4367 &structs,
4368 &enums,
4369 true
4370 )
4371 );
4372 }
4373 if !instructions.is_empty() {
4374 println!("-- INSTRUCTIONS --");
4375 for (i, instr) in instructions.iter().enumerate() {
4376 println!(" {i}: {instr:?}");
4377 }
4378 }
4379 println!("------------------");
4380 }
4381
4382 CompileOutput {
4383 instructions,
4384 registers,
4385 pools,
4386 instr_src,
4387 fn_registers,
4388 dyn_lib_fns,
4389 host_fns,
4390 allocated_arg_count,
4391 allocated_call_depth,
4392 sources,
4393 structs,
4394 enums,
4395 functions,
4396 dyn_libs,
4397 namespace,
4398 const_registers,
4399 free_registers,
4400 }
4401}