1pub(crate) mod algebra;
2mod binding;
3mod data;
4pub(crate) mod invert;
5mod modifier;
6pub(crate) mod optimize;
7mod pre_eval;
8
9use std::{
10 cell::RefCell,
11 cmp::Ordering,
12 collections::{BTreeSet, HashMap, HashSet, VecDeque},
13 env::current_dir,
14 fmt, fs,
15 iter::{once, repeat_n},
16 mem::{replace, swap, take},
17 panic::{AssertUnwindSafe, catch_unwind},
18 path::{Path, PathBuf},
19 slice,
20 sync::Arc,
21};
22
23use ecow::{EcoString, EcoVec, eco_vec};
24use indexmap::{IndexMap, IndexSet};
25use serde::{Deserialize, Serialize};
26
27use crate::{
28 Array, ArrayValue, Assembly, BindingKind, BindingMeta, Boxed, CONSTANTS, CodeSpan,
29 CustomInverse, Diagnostic, DiagnosticKind, DocComment, DocCommentSig, EXAMPLE_UA,
30 ExactDoubleIterator, Function, FunctionId, GitTarget, Ident, ImplPrimitive, InputSrc,
31 IntoInputSrc, IntoSysBackend, Node, NumericSubscript, PrimClass, Primitive, Purity, RunMode,
32 SUBSCRIPT_DIGITS, SemanticComment, SigNode, Signature, Sp, Span, SubSide, Subscript,
33 SysBackend, Uiua, UiuaError, UiuaErrorKind, UiuaResult, VERSION, Value,
34 algorithm::ga::{self, Spec},
35 ast::*,
36 check::nodes_sig,
37 format::{format_word, format_words},
38 function::DynamicFunction,
39 lsp::{CodeMeta, Completion, ImportSrc, SetInverses, SigDecl},
40 parse::{
41 flip_unsplit_items, flip_unsplit_lines, ident_modifier_args, max_placeholder, parse,
42 split_items, split_words,
43 },
44};
45pub(crate) use modifier::*;
46pub use pre_eval::PreEvalMode;
47
48#[derive(Clone)]
50pub struct Compiler {
51 pub(crate) asm: Assembly,
52 pub(crate) code_meta: CodeMeta,
53 current_bindings: Vec<CurrentBinding>,
55 next_global: usize,
57 pub(crate) scope: Scope,
59 higher_scopes: Vec<Scope>,
61 mode: RunMode,
63 current_imports: Vec<PathBuf>,
65 imports: HashMap<PathBuf, Module>,
67 index_macros: HashMap<usize, IndexMacro>,
69 code_macros: HashMap<usize, CodeMacro>,
71 externals: HashMap<Ident, usize>,
73 comptime_depth: usize,
75 in_try: bool,
77 errors: Vec<UiuaError>,
79 deprecated_prim_errors: HashSet<Primitive>,
81 deprecated_const_errors: HashSet<&'static str>,
83 diagnostics: BTreeSet<Diagnostic>,
85 pub(crate) print_diagnostics: bool,
87 comptime: bool,
89 pre_eval_mode: PreEvalMode,
91 macro_env: Uiua,
93 start_addrs: Vec<usize>,
95 prim_arg_bindings: HashMap<(Primitive, Ident), usize>,
97}
98
99impl Default for Compiler {
100 fn default() -> Self {
101 Compiler {
102 asm: Assembly::default(),
103 code_meta: CodeMeta::default(),
104 current_bindings: Vec::new(),
105 next_global: 0,
106 scope: Scope::default(),
107 higher_scopes: Vec::new(),
108 mode: RunMode::All,
109 current_imports: Vec::new(),
110 imports: HashMap::new(),
111 index_macros: HashMap::new(),
112 code_macros: HashMap::new(),
113 externals: HashMap::new(),
114 comptime_depth: 0,
115 in_try: false,
116 errors: Vec::new(),
117 deprecated_prim_errors: HashSet::new(),
118 deprecated_const_errors: HashSet::new(),
119 diagnostics: BTreeSet::new(),
120 print_diagnostics: false,
121 comptime: true,
122 pre_eval_mode: PreEvalMode::default(),
123 macro_env: Uiua::default(),
124 start_addrs: Vec::new(),
125 prim_arg_bindings: HashMap::new(),
126 }
127 }
128}
129
130#[derive(Debug, Default)]
131struct BindingPrelude {
132 comment: Option<EcoString>,
133 track_caller: bool,
134 no_inline: bool,
135 external: bool,
136 deprecation: Option<EcoString>,
137}
138
139#[derive(Debug, Clone, Default, Serialize, Deserialize)]
140#[serde(transparent)]
141pub struct LocalNames(IndexMap<Ident, Vec<LocalIndex>>);
143
144#[allow(missing_docs)]
145impl LocalNames {
146 pub fn insert(&mut self, name: impl Into<Ident>, local: LocalIndex) {
147 self.0.entry(name.into()).or_default().push(local);
148 }
149 pub fn remove(&mut self, name: &str) {
150 self.0.swap_remove(name);
151 }
152 pub fn visible_iter(&self) -> impl Iterator<Item = (&Ident, LocalIndex)> {
153 (self.0.iter()).map(|(name, locals)| (name, *locals.last().unwrap()))
154 }
155 pub fn into_visible_iter(self) -> impl Iterator<Item = (Ident, LocalIndex)> {
156 (self.0.into_iter()).map(|(name, locals)| (name, locals.into_iter().next_back().unwrap()))
157 }
158 pub fn all_iter(&self) -> impl Iterator<Item = (&Ident, LocalIndex)> {
159 (self.0.iter()).flat_map(|(name, locals)| locals.iter().map(move |local| (name, *local)))
160 }
161 pub fn get_only_module(&self, name: &str, asm: &Assembly) -> Option<LocalIndex> {
162 let locals = self.0.get(name)?;
163 (locals.iter().rev())
164 .find(|local| asm.bindings[local.index].kind.is_module())
165 .copied()
166 }
167 pub fn get_only_function(&self, name: &str, asm: &Assembly) -> Option<LocalIndex> {
168 let locals = self.0.get(name)?;
169 (locals.iter().rev())
170 .find(|local| asm.bindings[local.index].kind.has_sig())
171 .copied()
172 }
173 pub fn get_prefer_module(&self, name: &str, asm: &Assembly) -> Option<LocalIndex> {
174 self.get_only_module(name, asm)
175 .or_else(|| self.get_last(name))
176 }
177 pub fn get_prefer_function(&self, name: &str, asm: &Assembly) -> Option<LocalIndex> {
178 self.get_only_function(name, asm)
179 .or_else(|| self.get_last(name))
180 }
181 pub fn get_last(&self, name: &str) -> Option<LocalIndex> {
182 self.0.get(name)?.last().copied()
183 }
184 pub fn extend_from_other(&mut self, other: Self) {
185 for (name, locals) in other.0 {
186 self.0.entry(name).or_default().extend(locals);
187 }
188 }
189 pub fn contains_key(&self, name: &str) -> bool {
190 self.0.contains_key(name)
191 }
192}
193
194impl Extend<(Ident, LocalIndex)> for LocalNames {
195 fn extend<T: IntoIterator<Item = (Ident, LocalIndex)>>(&mut self, iter: T) {
196 for (name, local) in iter {
197 self.0.entry(name).or_default().push(local);
198 }
199 }
200}
201
202#[derive(Debug, Clone, Default, Serialize, Deserialize)]
204pub struct Module {
205 pub comment: Option<EcoString>,
207 pub names: LocalNames,
209 data_func: bool,
211 experimental: bool,
213}
214
215#[derive(Clone)]
217struct IndexMacro {
218 words: Vec<Sp<Word>>,
219 locals: HashMap<CodeSpan, usize>,
225 sig: Option<Signature>,
226 recursive: bool,
227}
228
229#[derive(Clone)]
231struct CodeMacro {
232 root: SigNode,
233 names: LocalNames,
234}
235
236impl AsRef<Assembly> for Compiler {
237 fn as_ref(&self) -> &Assembly {
238 &self.asm
239 }
240}
241
242impl AsMut<Assembly> for Compiler {
243 fn as_mut(&mut self) -> &mut Assembly {
244 &mut self.asm
245 }
246}
247
248#[derive(Debug, Clone)]
249struct CurrentBinding {
250 name: Ident,
251 signature: Option<Signature>,
252 recurses: usize,
253 global_index: usize,
254}
255
256#[derive(Debug, Clone)]
258pub(crate) struct Scope {
259 kind: ScopeKind,
260 file_path: Option<PathBuf>,
262 comment: Option<EcoString>,
264 names: LocalNames,
266 has_data_def: bool,
268 is_data_func: bool,
270 data_variants: IndexSet<EcoString>,
272 pub experimental: bool,
274 experimental_error: bool,
276}
277
278#[derive(Debug, Clone, PartialEq, Eq)]
279enum ScopeKind {
280 File(FileScopeKind),
282 Module(Ident),
284 AllInModule,
286 Macro(Option<MacroLocal>),
288 Binding,
290 Function,
292 Test,
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq)]
297enum FileScopeKind {
298 Source,
299 Git,
300}
301
302#[derive(Debug, Clone, Copy, PartialEq, Eq)]
304struct MacroLocal {
305 macro_index: usize,
306 expansion_index: Option<usize>,
307}
308
309impl Default for Scope {
310 fn default() -> Self {
311 Self {
312 kind: ScopeKind::File(FileScopeKind::Source),
313 file_path: None,
314 comment: None,
315 names: LocalNames::default(),
316 has_data_def: false,
317 is_data_func: false,
318 data_variants: IndexSet::new(),
319 experimental: false,
320 experimental_error: false,
321 }
322 }
323}
324
325impl Scope {
326 pub(crate) fn add_module_name(&mut self, name: EcoString, local: LocalIndex) {
327 self.names.remove(format!("{name}!").as_str());
328 self.names.insert(name, local);
329 }
330}
331
332#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
334pub struct LocalIndex {
335 pub index: usize,
337 pub public: bool,
339}
340
341impl Compiler {
342 pub fn new() -> Self {
344 Self::default()
345 }
346 pub fn with_backend(backend: impl IntoSysBackend) -> Self {
348 Self {
349 macro_env: Uiua::with_backend(backend.into_sys_backend()),
350 ..Self::default()
351 }
352 }
353 pub fn with_assembly(self, asm: Assembly) -> Self {
355 Self { asm, ..self }
356 }
357 pub fn assembly(&self) -> &Assembly {
359 &self.asm
360 }
361 pub fn assembly_mut(&mut self) -> &mut Assembly {
363 &mut self.asm
364 }
365 pub fn code_meta(&self) -> &CodeMeta {
367 &self.code_meta
368 }
369 pub fn code_meta_mut(&mut self) -> &mut CodeMeta {
371 &mut self.code_meta
372 }
373 pub fn finish(&mut self) -> Assembly {
375 take(&mut self.asm)
376 }
377 pub fn comptime(&mut self, comptime: bool) -> &mut Self {
379 self.comptime = comptime;
380 self
381 }
382 pub fn pre_eval_mode(&mut self, mode: PreEvalMode) -> &mut Self {
384 self.pre_eval_mode = mode;
385 self
386 }
387 pub fn print_diagnostics(&mut self, print_diagnostics: bool) -> &mut Self {
393 self.print_diagnostics = print_diagnostics;
394 self
395 }
396 pub fn mode(&mut self, mode: RunMode) -> &mut Self {
398 self.mode = mode;
399 self
400 }
401 pub fn experimental(&mut self, experimental: bool) -> &mut Self {
403 self.scope.experimental = experimental;
404 self
405 }
406 pub fn backend(&self) -> Arc<dyn SysBackend> {
408 self.macro_env.rt.backend.clone()
409 }
410 pub fn downcast_backend<T: SysBackend>(&self) -> Option<&T> {
412 self.macro_env.downcast_backend()
413 }
414 pub fn downcast_backend_mut<T: SysBackend>(&mut self) -> Option<&mut T> {
416 self.macro_env.downcast_backend_mut()
417 }
418 pub fn take_backend<T: SysBackend + Default>(&mut self) -> Option<T> {
420 self.macro_env.take_backend()
421 }
422 pub fn set_backend<T: SysBackend>(&mut self, backend: T) {
424 self.macro_env.rt.backend = Arc::new(backend);
425 }
426 pub fn load_file<P: AsRef<Path>>(&mut self, path: P) -> UiuaResult<&mut Self> {
428 let path = path.as_ref();
429 let input: EcoString = fs::read_to_string(path)
430 .map_err(|e| UiuaErrorKind::Load(path.into(), e.into()))?
431 .into();
432 self.asm.inputs.files.insert(path.into(), input.clone());
434 self.load_impl(&input, InputSrc::File(path.into()))
435 }
436 pub fn load_str(&mut self, input: &str) -> UiuaResult<&mut Self> {
438 let src = self.asm.inputs.add_src((), input);
439 self.load_impl(input, src)
440 }
441 pub fn load_str_src(&mut self, input: &str, src: impl IntoInputSrc) -> UiuaResult<&mut Self> {
443 let src = self.asm.inputs.add_src(src, input);
444 self.load_impl(input, src)
445 }
446 fn scopes(&self) -> impl ExactDoubleIterator<Item = &Scope> {
447 once(&self.scope)
448 .chain(self.higher_scopes.iter().rev())
449 .collect::<Vec<_>>()
450 .into_iter()
451 }
452 #[allow(dead_code)]
453 fn scopes_mut(&mut self) -> impl Iterator<Item = &mut Scope> {
454 once(&mut self.scope).chain(self.higher_scopes.iter_mut().rev())
455 }
456 fn scopes_to_file(&self) -> impl Iterator<Item = &Scope> {
457 let already_file = matches!(self.scope.kind, ScopeKind::File(_));
458 once(&self.scope).chain(
459 (!already_file)
460 .then(|| {
461 let file_index = self
462 .higher_scopes
463 .iter()
464 .rposition(|s| matches!(s.kind, ScopeKind::File(_)))?;
465 Some(self.higher_scopes[file_index..].iter().rev())
466 })
467 .flatten()
468 .into_iter()
469 .flatten(),
470 )
471 }
472 fn in_scope<T>(
479 &mut self,
480 kind: ScopeKind,
481 f: impl FnOnce(&mut Self) -> UiuaResult<T>,
482 ) -> UiuaResult<(Module, T)> {
483 self.higher_scopes.push(take(&mut self.scope));
484 self.scope.kind = kind;
485
486 let res = f(self);
487
488 let scope = replace(&mut self.scope, self.higher_scopes.pop().unwrap());
489
490 let res = res?;
491 let module = Module {
492 comment: scope.comment,
493 names: scope.names,
494 data_func: scope.is_data_func,
495 experimental: scope.experimental,
496 };
497 Ok((module, res))
498 }
499 fn load_impl(&mut self, input: &str, src: InputSrc) -> UiuaResult<&mut Self> {
500 let node_start = self.asm.root.len();
501 let (items, errors, diagnostics) = parse(input, src.clone(), &mut self.asm.inputs);
502 for diagnostic in diagnostics {
503 self.emit_diagnostic_impl(diagnostic);
504 }
505 if !errors.is_empty() {
506 return Err(UiuaErrorKind::Parse(errors, self.asm.inputs.clone().into()).into());
507 }
508 if let InputSrc::File(path) = &src {
509 self.current_imports.push(path.to_path_buf());
510 self.scope.file_path = Some(if path.is_absolute() {
511 current_dir()
512 .ok()
513 .and_then(|dir| pathdiff::diff_paths(path, dir))
514 .unwrap_or_else(|| path.to_path_buf())
515 } else {
516 path.to_path_buf()
517 });
518 }
519
520 let base = 0u8;
521 self.start_addrs.push(&base as *const u8 as usize);
522 let res = self.catching_crash(input, |env| env.items(items, ItemCompMode::TopLevel));
523 self.start_addrs.pop();
524
525 let do_optimize = match src {
528 InputSrc::File(_) => self.current_imports.len() <= 1,
529 _ => self.current_imports.is_empty(),
530 };
531 if do_optimize {
532 self.asm.root.optimize_full();
533 for i in 0..self.asm.functions.len() {
535 self.asm.functions.make_mut()[i].optimize_full();
536 if let Some((root, errs)) = self.pre_eval(&self.asm.functions[i]) {
537 self.asm.functions.make_mut()[i] = root;
538 self.errors.extend(errs);
539 self.asm.functions.make_mut()[i].optimize_full();
540 }
541 }
542 }
543 if self.print_diagnostics {
547 for diagnostic in self.take_diagnostics() {
548 eprintln!("{}", diagnostic.report());
549 }
550 }
551
552 self.code_meta.top_level_names = (self.scope.names.all_iter())
554 .map(|(name, local)| (name.clone(), local))
555 .collect();
556
557 if let InputSrc::File(_) = &src {
558 self.current_imports.pop();
559 }
560 match res {
562 Err(e) | Ok(Err(e)) => {
563 self.asm.root.truncate(node_start);
564 self.errors.push(e);
565 }
566 _ => {}
567 }
568 match self.errors.len() {
569 0 => Ok(self),
570 1 => Err(self.errors.pop().unwrap()),
571 _ => Err(UiuaError::from_multi(take(&mut self.errors))),
572 }
573 }
574 fn catching_crash<T>(
575 &mut self,
576 input: impl fmt::Display,
577 f: impl FnOnce(&mut Self) -> T,
578 ) -> UiuaResult<T> {
579 match catch_unwind(AssertUnwindSafe(|| f(self))) {
580 Ok(res) => Ok(res),
581 Err(_) => Err(UiuaErrorKind::CompilerPanic(format!(
582 "\
583The compiler has crashed!
584Hooray! You found a bug!
585Please report this at http://github.com/uiua-lang/uiua/issues/new \
586or on Discord at https://discord.gg/9CU2ME4kmn.
587
588Uiua version {VERSION}
589
590code:
591{input}"
592 ))
593 .into()),
594 }
595 }
596}
597
598#[derive(Clone, Copy, PartialEq, Eq)]
599enum ItemCompMode {
600 TopLevel,
601 Function,
602 CodeMacro,
603}
604
605impl Compiler {
606 fn items(&mut self, items: Vec<Item>, mode: ItemCompMode) -> UiuaResult {
607 let mut started = false;
609 let mut comment = String::new();
610 let mut items_iter = items.iter();
611 while let Some(Item::Words(line)) = items_iter.next() {
612 for word in line {
613 match &word.value {
614 Word::Comment(c) => {
615 let mut c = c.as_str();
616 if c.starts_with(' ') {
617 c = &c[1..];
618 }
619 comment.push_str(c);
620 started = true;
621 }
622 Word::Spaces => {}
623 _ => {
624 comment.clear();
625 break;
626 }
627 }
628 }
629 if line.is_empty() && started {
630 break;
631 }
632 }
633 comment.push('\n');
634 if !comment.trim().is_empty() {
635 self.scope.comment = Some(comment.trim().into());
636 }
637
638 let mut prelude = BindingPrelude::default();
639 let mut item_errored = false;
640 let mut item_queue = VecDeque::from(flip_unsplit_items(split_items(items)));
641 while let Some(item) = item_queue.pop_front() {
642 let must_run = mode == ItemCompMode::CodeMacro
643 || matches!(&item, Item::Words(_))
644 && item_queue.iter().any(|item| match item {
645 Item::Binding(binding)
646 if (binding.words.iter())
647 .filter(|word| word.value.is_code())
648 .count()
649 == 0 =>
650 {
651 true
652 }
653 Item::Words(line) => line
654 .iter()
655 .find(|w| w.value.is_code())
656 .is_some_and(|w| matches!(w.value, Word::Primitive(Primitive::Assert))),
657 _ => false,
658 });
659 if let Err(e) = self.item(item, must_run, mode, &mut prelude) {
660 if !item_errored || self.errors.is_empty() {
661 self.errors.push(e);
662 }
663 item_errored = true;
664 }
665 }
666 Ok(())
667 }
668 fn item(
669 &mut self,
670 item: Item,
671 must_run: bool,
672 mode: ItemCompMode,
673 prelude: &mut BindingPrelude,
674 ) -> UiuaResult {
675 match item {
676 Item::Module(m) => self.module(m, take(prelude)),
677 Item::Words(line) => self.top_level_words(line, must_run, mode, prelude),
678 Item::Binding(binding) => self.binding(binding, take(prelude)),
679 Item::Import(import) => self.import(import, take(prelude).comment),
680 Item::Data(defs) => {
681 for data in defs {
682 self.data_def(data, true, take(prelude))?
683 }
684 Ok(())
685 }
686 }
687 }
688 fn top_level_words(
689 &mut self,
690 mut line: Vec<Sp<Word>>,
691 must_run: bool,
692 mode: ItemCompMode,
693 prelude: &mut BindingPrelude,
694 ) -> UiuaResult {
695 let mut words = line.iter().filter(|w| !matches!(w.value, Word::Spaces));
697 if words.clone().count() == 1 {
698 let word = words.next().unwrap();
699 match &word.value {
700 Word::Comment(c) => {
701 if let Some(curr_com) = &mut prelude.comment {
702 curr_com.push('\n');
703 curr_com.push_str(c);
704 } else {
705 prelude.comment = Some(c.as_str().into());
706 }
707 line.clear();
708 }
709 Word::SemanticComment(SemanticComment::NoInline) => prelude.no_inline = true,
710 Word::SemanticComment(SemanticComment::TrackCaller) => prelude.track_caller = true,
711 Word::SemanticComment(SemanticComment::External) => prelude.external = true,
712 Word::SemanticComment(SemanticComment::Deprecated(s)) => {
713 prelude.deprecation = Some(s.clone())
714 }
715 _ => *prelude = BindingPrelude::default(),
716 }
717 } else {
718 *prelude = BindingPrelude::default();
719 }
720
721 fn words_should_run_anyway(words: &[Sp<Word>]) -> bool {
722 let mut anyway = false;
723 recurse_words(words, &mut |w| {
724 anyway = anyway
725 || matches!(&w.value, Word::SemanticComment(_))
726 || matches!(&w.value, Word::Modified(m)
727 if matches!(m.modifier.value, Modifier::Ref(_)))
728 });
729 anyway
730 }
731 let in_test = self.scopes().any(|sc| sc.kind == ScopeKind::Test);
732 let can_run = mode != ItemCompMode::TopLevel
733 || match self.mode {
734 RunMode::Normal => !in_test,
735 RunMode::Test => in_test,
736 RunMode::All => true,
737 };
738 if line.is_empty() || !(can_run || must_run || words_should_run_anyway(&line)) {
739 return Ok(());
740 }
741 let span = (line.first().unwrap().span.clone()).merge(line.last().unwrap().span.clone());
742 if max_placeholder(&line).is_some() {
743 self.add_error(
744 span.clone(),
745 "Cannot use placeholder outside of an index macro",
746 );
747 }
748 let binding_count_before = self.asm.bindings.len();
750 let root_len_before = self.asm.root.len();
751 let error_count_before = self.errors.len();
752
753 let mut line_node = self.line(line)?;
754
755 if matches!(self.scope.kind, ScopeKind::File(_)) {
756 if let Err((e, f, mut spans)) = line_node.check_callability(&self.asm) {
758 let e = e.clone();
759 if let Some(f_id) = f.map(|f| f.id.clone()) {
760 let src_span = self.get_span(spans.remove(0));
761 let call_span = self.get_span(spans.pop().unwrap());
762 let error = self.error_with_info(
763 call_span,
764 format!("Cannot call {f_id} because of an inversion error"),
765 [(src_span, e)],
766 );
767 self.errors.push(error);
768 } else {
769 let span = self.get_span(spans.pop().unwrap());
770 self.add_error(span, e);
771 }
772 }
773 if let Ok(sig) = line_node.sig() {
775 self.asm.line_sigs.insert(span.end.line, sig);
776 }
777 }
778
779 let binding_count_after = self.asm.bindings.len();
780 let error_count_after = self.errors.len();
781
782 if mode != ItemCompMode::Function {
783 line_node.optimize_full();
784 }
785 match line_node.sig() {
786 Ok(sig) => {
787 if self.mode != RunMode::Normal
789 && mode != ItemCompMode::Function
790 && !self
791 .scopes()
792 .any(|sc| sc.kind == ScopeKind::File(FileScopeKind::Git))
793 {
794 let test_assert = line_node
795 .last_mut_recursive(&mut self.asm, |node| {
796 if let &mut Node::Prim(Primitive::Assert, span) = node {
797 *node = Node::ImplPrim(ImplPrimitive::TestAssert, span);
798 true
799 } else {
800 false
801 }
802 })
803 .unwrap_or(false);
804 if test_assert {
805 self.asm.test_assert_count += 1;
806 }
807 }
808 if mode != ItemCompMode::Function
815 && self.pre_eval_mode > PreEvalMode::Line
816 && error_count_after == error_count_before
817 && binding_count_before == binding_count_after
818 && root_len_before == self.asm.root.len()
819 && !line_node.is_empty()
820 && self.asm.root.len() >= sig.args()
821 && (self.asm.root.iter().rev().take(sig.args()))
822 .all(|node| matches!(node, Node::Push(_)))
823 {
824 let mut node = Node::from(&self.asm.root[self.asm.root.len() - sig.args()..]);
827 node.extend(line_node.iter().cloned());
828 if let Some((node, errs)) = self.pre_eval(&node) {
829 self.errors.extend(errs);
830 if !matches!(line_node.last().unwrap(), Node::SetOutputComment { .. })
832 && node.iter().all(|node| matches!(node, Node::Push(_)))
833 {
834 let vals: Vec<_> = node
835 .iter()
836 .map(|node| match node {
837 Node::Push(val) => val.clone(),
838 _ => unreachable!(),
839 })
840 .collect();
841 self.code_meta.top_level_values.insert(span, vals);
842 }
843 self.asm.root.truncate(self.asm.root.len() - sig.args());
845 line_node = node;
847 }
848 }
849 }
850 Err(e) => self.add_error(span, e),
851 }
852 self.asm.root.push(line_node);
853 Ok(())
854 }
855 fn compile_bind_function(
856 &mut self,
857 name: Ident,
858 local: LocalIndex,
859 function: Function,
860 span: usize,
861 meta: BindingMeta,
862 ) -> UiuaResult {
863 self.scope.names.insert(name.clone(), local);
864 let span = if span == 0 {
865 Some(CodeSpan::literal(name))
866 } else {
867 self.get_span(span).clone().code()
868 };
869 self.asm
870 .add_binding_at(local, BindingKind::Func(function), span, meta);
871 Ok(())
872 }
873 fn compile_bind_const(
874 &mut self,
875 name: Ident,
876 local: LocalIndex,
877 value: Option<Value>,
878 span: usize,
879 meta: BindingMeta,
880 ) {
881 let span = self.get_span(span).clone().code();
882 self.asm
883 .add_binding_at(local, BindingKind::Const(value), span, meta);
884 self.scope.names.insert(name, local);
885 }
886 pub(crate) fn import_module(&mut self, path_str: &str, span: &CodeSpan) -> UiuaResult<PathBuf> {
888 let (path, file_kind) = if let Some(url) =
890 (path_str.trim().strip_prefix("git:").map(Into::into)).or_else(|| {
891 (path_str.trim().strip_prefix("gh:")).map(|s| format!("github.com/{}", s.trim()))
892 }) {
893 let mut url = url.as_str();
894 if url.contains("branch:") && url.contains("commit:") {
895 return Err(self.error(
896 span.clone(),
897 "Cannot specify both branch and commit in git import",
898 ));
899 }
900 let target = if let Some((a, b)) = url.split_once("branch:") {
901 url = a;
902 GitTarget::Branch(b.trim().into())
903 } else if let Some((a, b)) = url.split_once("commit:") {
904 url = a;
905 GitTarget::Commit(b.trim().into())
906 } else {
907 GitTarget::Default
908 };
909 let mut url = url.trim().trim_end_matches(".git").to_string();
911 if url.ends_with("/uiua") {
912 return Err(self.error(span.clone(), "Cannot import what looks like a Uiua fork"));
913 }
914 if !(url.starts_with("https://") || url.starts_with("http://")) {
915 url = format!("https://{url}");
916 }
917 self.code_meta
918 .import_srcs
919 .insert(span.clone(), ImportSrc::Git(url.clone()));
920 let path = self
921 .backend()
922 .load_git_module(&url, target)
923 .map_err(|e| self.error(span.clone(), e))?;
924 (path, FileScopeKind::Git)
925 } else {
926 let path = self.resolve_import_path(Path::new(path_str));
928 self.code_meta
929 .import_srcs
930 .insert(span.clone(), ImportSrc::File(path.clone()));
931 (path, FileScopeKind::Source)
932 };
933 if !self.imports.contains_key(&path) {
934 thread_local! {
936 static GIT_CACHE: RefCell<HashMap<PathBuf, Compiler>> = RefCell::new(HashMap::new());
937 }
938 let bytes = self
939 .backend()
940 .file_read_all(&path)
941 .or_else(|e| {
942 if path.ends_with(Path::new("example.ua")) {
943 Ok(EXAMPLE_UA.as_bytes().to_vec())
944 } else {
945 Err(e)
946 }
947 })
948 .map_err(|e| self.error(span.clone(), e))?;
949 if let Some(mut comp) = (bytes.len() > 1000)
950 .then(|| GIT_CACHE.with(|cache| cache.borrow().get(&path).cloned()))
951 .flatten()
952 {
953 swap(self, &mut comp);
954 self.macro_env.rt.backend = comp.macro_env.rt.backend;
955 self.asm.inputs.strings = comp.asm.inputs.strings;
956 self.asm.inputs.files.extend(comp.asm.inputs.files);
957 self.scope.experimental = comp.scope.experimental;
958 self.higher_scopes = comp.higher_scopes;
959 self.diagnostics.extend(comp.diagnostics);
960 } else {
961 let input: EcoString = String::from_utf8(bytes)
962 .map_err(|e| self.error(span.clone(), format!("Failed to read file: {e}")))?
963 .into();
964 if self.current_imports.iter().any(|p| p == &path) {
965 return Err(self.error(
966 span.clone(),
967 format!("Cycle detected importing {}", path.to_string_lossy()),
968 ));
969 }
970 let (module, ()) = self.in_scope(ScopeKind::File(file_kind), |comp| {
971 comp.load_str_src(&input, &path).map(drop)
972 })?;
973 self.imports.insert(path.clone(), module);
974 #[cfg(target_arch = "wasm32")]
975 if file_kind == FileScopeKind::Git {
976 GIT_CACHE.with(|cache| {
977 let mut clone = self.clone();
978 clone.macro_env.rt.backend = Arc::new(crate::SafeSys::default());
979 cache.borrow_mut().insert(path.clone(), clone);
980 });
981 }
982 };
983 }
984 let module = self.imports.get(&path).unwrap();
985 if module.experimental {
986 self.experimental_error(span, || {
987 format!(
988 "Module `{path_str}` is experimental. \
989 To use it, add `# Experimental!` to the top of this file."
990 )
991 });
992 }
993 Ok(path)
994 }
995 pub(crate) fn resolve_import_path(&self, path: &Path) -> PathBuf {
997 let mut target = if let Some(parent) = self.current_imports.last().and_then(|p| p.parent())
998 {
999 parent.join(path)
1000 } else {
1001 path.to_path_buf()
1002 };
1003 if !target.exists() && target.extension().is_none() {
1004 target = target.with_extension("ua");
1005 }
1006 let base = Path::new(".");
1007 if let (Ok(canon_target), Ok(canon_base)) = (target.canonicalize(), base.canonicalize()) {
1008 pathdiff::diff_paths(canon_target, canon_base).unwrap_or(target)
1009 } else {
1010 pathdiff::diff_paths(&target, base).unwrap_or(target)
1011 }
1012 }
1013 fn line(&mut self, mut line: Vec<Sp<Word>>) -> UiuaResult<Node> {
1015 let comment_sig = line_sig(&line);
1016 let output_comment = if line
1017 .last()
1018 .is_some_and(|w| matches!(w.value, Word::OutputComment { .. }))
1019 {
1020 let word = line.pop().unwrap();
1021 Some(self.word(word)?)
1022 } else {
1023 None
1024 };
1025 let mut node = self.words(line)?;
1027 node.extend(output_comment);
1028 if let Some(comment_sig) = comment_sig {
1030 self.apply_node_comment(&mut node, &comment_sig.value, "Line", &comment_sig.span);
1031 }
1032 Ok(node)
1033 }
1034 fn apply_node_comment(
1035 &mut self,
1036 node: &mut Node,
1037 comment_sig: &DocCommentSig,
1038 name: &str,
1039 span: &CodeSpan,
1040 ) {
1041 let mut spandex: Option<usize> = None;
1042 if let Ok(sig) = node.sig() {
1044 if !comment_sig.matches_sig(sig) {
1045 let span = *spandex.get_or_insert_with(|| self.add_span(span.clone()));
1046 self.emit_diagnostic(
1047 format!(
1048 "{name} comment describes {}, \
1049 but its code has signature {sig}",
1050 comment_sig.sig_string()
1051 ),
1052 DiagnosticKind::Warning,
1053 self.get_span(span).clone().code().unwrap(),
1054 );
1055 }
1056 }
1057 if comment_sig.label {
1058 if let Some(args) = &comment_sig.args {
1060 let span = *spandex.get_or_insert_with(|| self.add_span(span.clone()));
1061 let labels = Node::bracket(
1062 args.iter()
1063 .map(|a| Node::Label(a.name.clone(), span).sig_node().unwrap()),
1064 span,
1065 );
1066 node.prepend(labels);
1067 }
1068 if let Some(outputs) = &comment_sig.outputs {
1070 let span = *spandex.get_or_insert_with(|| self.add_span(span.clone()));
1071 let labels = Node::bracket(
1072 outputs
1073 .iter()
1074 .map(|o| Node::Label(o.name.clone(), span).sig_node().unwrap()),
1075 span,
1076 );
1077 node.push(labels);
1078 }
1079 }
1080 }
1081 fn args(&mut self, words: Vec<Sp<Word>>) -> UiuaResult<EcoVec<SigNode>> {
1082 (words.into_iter())
1083 .filter(|w| w.value.is_code())
1084 .map(|w| self.word_sig(w))
1085 .collect()
1086 }
1087 fn words_sig(&mut self, words: Vec<Sp<Word>>) -> UiuaResult<SigNode> {
1088 let span = words
1089 .first()
1090 .zip(words.last())
1091 .map(|(f, l)| f.span.clone().merge(l.span.clone()));
1092 let node = self.words(words)?;
1093 let sig = if let Some(span) = span {
1094 self.sig_of(&node, &span)?
1095 } else {
1096 Signature::new(0, 0)
1097 };
1098 Ok(SigNode::new(sig, node))
1099 }
1100 fn words(&mut self, mut words: Vec<Sp<Word>>) -> UiuaResult<Node> {
1101 words.retain(|word| word.value.is_code());
1103 let mut sem = None;
1105 if let Some(word) = words.last() {
1106 if let Word::SemanticComment(com) = &word.value {
1107 let com = com.clone();
1108 sem = Some(words.pop().unwrap().span.sp(com));
1109 }
1110 }
1111 words.reverse();
1113
1114 #[derive(Debug, Clone)]
1117 struct PrevWord(
1118 Option<Primitive>,
1119 Option<Primitive>,
1120 Option<Signature>,
1121 CodeSpan,
1122 );
1123 let mut a: Option<PrevWord> = None;
1124 let mut b: Option<PrevWord> = None;
1125 let mut nodes = Node::empty();
1126 for word in flip_unsplit_lines(split_words(words)).into_iter().flatten() {
1127 let span = word.span.clone();
1128 let (mut modif, mut prim) = (None, None);
1129 match &word.value {
1130 Word::Primitive(p) => prim = Some(*p),
1131 Word::Modified(m) => {
1132 if let Modifier::Primitive(mprim) = &m.modifier.value {
1133 if let Some(first) = m.operands.first() {
1134 if let Word::Primitive(p) = first.value {
1135 modif = Some(*mprim);
1136 prim = Some(p);
1137 }
1138 }
1139 }
1140 }
1141 _ => {}
1142 }
1143
1144 match (a, &b, prim.filter(|_| modif.is_none())) {
1145 (
1147 Some(PrevWord(None, Some(Primitive::Dup), _, a_span)),
1148 Some(PrevWord(_, _, Some(sig), _)),
1149 Some(Primitive::Flip),
1150 ) if *sig == (1, 1) => {
1151 self.emit_diagnostic(
1152 format!(
1153 "Prefer {} over {} {} here",
1154 Primitive::On,
1155 Primitive::Flip,
1156 Primitive::Dup
1157 ),
1158 DiagnosticKind::Style,
1159 a_span.merge(span.clone()),
1160 );
1161 }
1162 (
1164 Some(PrevWord(None, Some(Primitive::Dup), _, a_span)),
1165 Some(PrevWord(None, Some(Primitive::Unique), _, _)),
1166 Some(Primitive::Keep),
1167 ) => {
1168 self.emit_diagnostic(
1169 format!(
1170 "Prefer {} over {}{}{}",
1171 Primitive::Deduplicate.format(),
1172 Primitive::Keep,
1173 Primitive::Unique,
1174 Primitive::Dup
1175 ),
1176 DiagnosticKind::Advice,
1177 a_span.merge(span.clone()),
1178 );
1179 }
1180 (
1182 _,
1183 Some(PrevWord(None, Some(Primitive::Reverse), _, b_span)),
1184 Some(Primitive::First),
1185 ) => {
1186 self.emit_diagnostic(
1187 format!(
1188 "Prefer {} over {}{}",
1189 Primitive::Last.format(),
1190 Primitive::First,
1191 Primitive::Reverse
1192 ),
1193 DiagnosticKind::Advice,
1194 b_span.clone().merge(span.clone()),
1195 );
1196 }
1197 (
1199 Some(PrevWord(None, Some(Primitive::Dup), _, a_span)),
1200 Some(PrevWord(None, Some(Primitive::Rise), _, _)),
1201 Some(Primitive::Select),
1202 ) => {
1203 self.emit_diagnostic(
1204 format!(
1205 "Prefer {} over {}{}{}",
1206 Primitive::Sort.format(),
1207 Primitive::Select,
1208 Primitive::Rise,
1209 Primitive::Dup
1210 ),
1211 DiagnosticKind::Advice,
1212 a_span.merge(span.clone()),
1213 );
1214 }
1215 (
1216 _,
1217 Some(PrevWord(Some(Primitive::By), Some(Primitive::Rise), _, b_span)),
1218 Some(Primitive::Select),
1219 ) => {
1220 self.emit_diagnostic(
1221 format!(
1222 "Prefer {} over {}{}{}",
1223 Primitive::Sort.format(),
1224 Primitive::Select,
1225 Primitive::By,
1226 Primitive::Rise
1227 ),
1228 DiagnosticKind::Advice,
1229 b_span.clone().merge(span.clone()),
1230 );
1231 }
1232 _ => {}
1233 }
1234
1235 let node = self.word(word)?;
1237 let sig = node.sig().ok();
1238 nodes.push(node);
1239 a = b;
1240 b = Some(PrevWord(modif, prim, sig, span));
1241 }
1242 if let Some(sem) = sem {
1243 nodes = self.semantic_comment(sem.value, sem.span, nodes);
1244 }
1245 Ok(nodes)
1246 }
1247 fn word_sig(&mut self, word: Sp<Word>) -> UiuaResult<SigNode> {
1248 let span = word.span.clone();
1249 let node = self.word(word)?;
1250 let sig = self.sig_of(&node, &span)?;
1251 Ok(SigNode::new(sig, node))
1252 }
1253 fn check_depth(&mut self, span: &CodeSpan) -> UiuaResult {
1254 const MAX_RECURSION_DEPTH: usize =
1255 match (cfg!(target_arch = "wasm32"), cfg!(debug_assertions)) {
1256 (false, false) => (512 + 256 + 64) * 1024 * 2,
1257 (false, true) => (512 + 256 + 64) * 1024,
1258 (true, false) => 640 * 1024,
1259 (true, true) => 640 * 1024,
1260 };
1261 let start_addr = *self.start_addrs.first().unwrap();
1262 let curr = 0u8;
1263 let curr_addr = &curr as *const u8 as usize;
1264 let diff = curr_addr.abs_diff(start_addr);
1265 if diff > MAX_RECURSION_DEPTH {
1266 return Err(self.error(span.clone(), "Compilation recursion limit reached"));
1267 }
1268 Ok(())
1269 }
1270 fn word(&mut self, word: Sp<Word>) -> UiuaResult<Node> {
1271 self.check_depth(&word.span)?;
1272 Ok(match word.value {
1273 Word::Number(NumWord::Real(n), _) => Node::new_push(n),
1274 Word::Number(NumWord::Complex(c), _) => Node::new_push(c),
1275 Word::Number(NumWord::Err(s), _) => {
1276 self.add_error(word.span.clone(), format!("Invalid number `{s}`"));
1277 Node::new_push(0.0)
1278 }
1279 Word::Char(c) => {
1280 let val: Value = if c.chars().count() == 1 {
1281 c.chars().next().unwrap().into()
1282 } else {
1283 c.into()
1284 };
1285 Node::new_push(val)
1286 }
1287 Word::String(s) => Node::new_push(s),
1288 Word::MultilineString(lines) => {
1289 let mut s = EcoVec::new();
1290 for (i, line) in lines.into_iter().enumerate() {
1291 if i > 0 {
1292 s.push('\n');
1293 }
1294 s.extend(line.value.chars());
1295 }
1296 Node::new_push(s)
1297 }
1298 Word::Label(label) => Node::Label(label.into(), self.add_span(word.span.clone())),
1299 Word::FormatString(frags) => {
1300 let parts = frags.into_iter().map(Into::into).collect();
1301 let span = self.add_span(word.span.clone());
1302 Node::Format(parts, span)
1303 }
1304 Word::MultilineFormatString(lines) => {
1305 let span = self.add_span(word.span.clone());
1306 let mut curr_part = EcoString::new();
1307 let mut parts = EcoVec::new();
1308 for (l, line) in lines.into_iter().enumerate() {
1309 if l > 0 {
1310 curr_part.push('\n');
1311 }
1312 for (f, frag) in line.value.into_iter().enumerate() {
1313 if f > 0 {
1314 parts.push(take(&mut curr_part));
1315 }
1316 curr_part.push_str(&frag);
1317 }
1318 }
1319 parts.push(curr_part);
1320 Node::Format(parts, span)
1321 }
1322 Word::Ref(r, chained) => r.chain_refs(chained).map(|r| self.reference(r)).collect(),
1323 Word::IncompleteRef(path) => 'blk: {
1324 if let Some((_, locals)) = self.ref_path(&path)? {
1325 self.add_error(
1326 path.last().unwrap().tilde_span.clone(),
1327 "Incomplete module reference",
1328 );
1329 for (local, comp) in locals.iter().zip(path) {
1330 self.validate_local(&comp.module.value, *local, &comp.module.span);
1331 self.code_meta
1332 .global_references
1333 .insert(comp.module.span, local.index);
1334 }
1335 let index = locals.last().unwrap().index;
1336 let names = match &self.asm.bindings[index].kind {
1337 BindingKind::Import(path) => &self.imports[path].names,
1338 BindingKind::Module(module) => &module.names,
1339 _ => break 'blk Node::empty(),
1340 };
1341 let mut completions = Vec::new();
1342 for (name, local) in names.visible_iter() {
1343 if !local.public {
1344 continue;
1345 }
1346 completions.push(Completion {
1347 text: name.into(),
1348 index: local.index,
1349 replace: false,
1350 });
1351 }
1352 if !completions.is_empty() {
1353 self.code_meta
1354 .completions
1355 .insert(word.span.clone(), completions);
1356 }
1357 }
1358 Node::empty()
1359 }
1360 Word::Strand(items) => {
1361 let just_spans: Vec<_> = items.iter().map(|w| w.span.clone()).collect();
1363 let op_nodes = items
1365 .into_iter()
1366 .rev()
1367 .map(|word| self.word_sig(word))
1368 .collect::<UiuaResult<Vec<_>>>()?;
1369 let has_functions = op_nodes.iter().any(|sn| sn.sig.args() > 0);
1371 if has_functions {
1372 return Err(
1373 self.error(word.span.clone(), "Functions are not allowed in strands")
1374 );
1375 }
1376 self.code_meta.strands.insert(word.span.clone(), just_spans);
1377 let inner = Node::from_iter(op_nodes.into_iter().map(|sn| sn.node));
1379
1380 if !inner.is_empty()
1384 && inner.iter().all(
1385 |instr| matches!(instr, Node::Push(Value::Char(arr)) if arr.rank() == 0),
1386 )
1387 {
1388 self.emit_diagnostic(
1389 "Stranded characters should instead be written as a string",
1390 DiagnosticKind::Advice,
1391 word.span.clone(),
1392 );
1393 }
1394
1395 let span = self.add_span(word.span.clone());
1396 if inner.iter().all(|instr| matches!(instr, Node::Push(_))) {
1398 let values: Vec<_> = inner
1399 .iter()
1400 .rev()
1401 .map(|instr| match instr {
1402 Node::Push(v) => v.clone(),
1403 _ => unreachable!(),
1404 })
1405 .collect();
1406 match Value::from_row_values(values, &(&word.span, &self.asm.inputs)) {
1407 Ok(val) => return Ok(Node::new_push(val)),
1408 Err(e) if e.meta.is_fill => {}
1409 Err(e) => return Err(e),
1410 }
1411 }
1412 let sig = self.sig_of(&inner, &word.span)?;
1413 Node::Array {
1414 len: sig.outputs(),
1415 inner: inner.into(),
1416 boxed: false,
1417 allow_ext: false,
1418 prim: None,
1419 span,
1420 }
1421 }
1422 Word::Array(arr) => {
1423 if let Some(down_span) = &arr.down_span {
1424 self.experimental_error(down_span, || {
1425 "Lexical ordering is experimental. To use it, \
1426 add `# Experimental!` to the top of the file."
1427 });
1428 }
1429 if !arr.boxes
1431 && (arr.word_lines().flatten())
1432 .filter(|w| w.value.is_code())
1433 .all(|w| w.value.is_literal() && !matches!(w.value, Word::Strand(_)))
1434 {
1435 let just_spans: Vec<_> = (arr.word_lines().rev().flatten())
1436 .filter(|w| w.value.is_code())
1437 .map(|w| w.span.clone())
1438 .collect();
1439 self.code_meta
1440 .array_inner_spans
1441 .insert(word.span.clone(), just_spans);
1442 }
1443 let line_count = arr.lines.len();
1444 let any_contents = arr.word_lines().flatten().any(|w| w.value.is_code());
1445 let (mut word_lines, item_lines): (Vec<_>, Vec<_>) = arr
1447 .lines
1448 .into_iter()
1449 .partition(|item| matches!(item, Item::Words(_)));
1450 if arr.down_span.is_none() {
1451 word_lines.reverse();
1452 }
1453 let root_start = self.asm.root.len();
1454 self.in_scope(ScopeKind::Function, |comp| {
1455 comp.items(item_lines, ItemCompMode::Function)?;
1456 comp.items(word_lines, ItemCompMode::Function)
1457 })?;
1458 let inner = self.asm.root.split_off(root_start);
1459 let len = match inner.sig() {
1461 Ok(sig) => {
1462 if sig.outputs() == 0 && any_contents {
1463 self.emit_diagnostic(
1464 "Array wraps function with no outputs. This is probably not what you want.",
1465 DiagnosticKind::Advice,
1466 word.span.clone(),
1467 )
1468 }
1469 sig.outputs()
1470 }
1471 Err(e) => {
1472 return Err(self.error(
1473 word.span.clone(),
1474 format!("Cannot infer array signature: {e}"),
1475 ));
1476 }
1477 };
1478 if line_count <= 1
1480 && !arr.boxes
1481 && !inner.is_empty()
1482 && inner.iter().all(
1483 |instr| matches!(instr, Node::Push(Value::Char(arr)) if arr.rank() == 0),
1484 )
1485 {
1486 self.emit_diagnostic(
1487 "An array of characters should instead be written as a string",
1488 DiagnosticKind::Advice,
1489 word.span.clone(),
1490 );
1491 }
1492 let span = self.add_span(word.span.clone());
1493 if inner.iter().all(|instr| matches!(instr, Node::Push(_))) {
1495 let empty = inner.is_empty();
1496 let values = inner.iter().rev().map(|instr| match instr {
1497 Node::Push(v) => v.clone(),
1498 _ => unreachable!(),
1499 });
1500 let res = if arr.boxes {
1501 if empty {
1502 Ok(Array::<Boxed>::default().into())
1503 } else {
1504 Value::from_row_values(
1505 values
1506 .map(|v| Value::Box(Boxed(v).into()))
1507 .collect::<Vec<_>>(),
1508 &(&word.span, &self.asm.inputs),
1509 )
1510 }
1511 } else {
1512 Value::from_row_values(
1513 values.collect::<Vec<_>>(),
1514 &(&word.span, &self.asm.inputs),
1515 )
1516 };
1517 match res {
1518 Ok(val) => {
1519 self.code_meta
1520 .array_shapes
1521 .insert(word.span.clone(), val.shape.clone());
1522 return Ok(Node::new_push(val));
1523 }
1524 Err(e) if e.meta.is_fill => {}
1525 Err(e) => return Err(e),
1526 }
1527 }
1528 Node::Array {
1530 len,
1531 inner: inner.into(),
1532 boxed: arr.boxes,
1533 allow_ext: false,
1534 prim: None,
1535 span,
1536 }
1537 }
1538 Word::Func(func) => self.func(func, word.span)?,
1539 Word::Pack(pack) => {
1540 self.add_error(
1541 word.span.clone(),
1542 "Function packs are not allowed without a modifier",
1543 );
1544 if let Some(first) = pack.into_lexical_order().next() {
1545 self.func(first.value, first.span)?
1546 } else {
1547 Node::empty()
1548 }
1549 }
1550 Word::Primitive(p) => self.primitive(p, word.span),
1551 Word::Modified(m) => self.modified(*m, None)?,
1552 Word::Placeholder(_) => {
1553 Node::empty()
1555 }
1556 Word::SemanticComment(_) => {
1557 Node::empty()
1559 }
1560 Word::OutputComment { i, n } => Node::SetOutputComment { i, n },
1561 Word::Subscripted(sub) => self.subscript(*sub, word.span)?,
1562 Word::Comment(_) | Word::Spaces | Word::BreakLine | Word::FlipLine => Node::empty(),
1563 Word::InlineMacro(_) => {
1564 self.add_error(
1565 word.span.clone(),
1566 "Inline macro was not parsed as a modifier. \
1567 This is a bug in the interpreter",
1568 );
1569 Node::empty()
1570 }
1571 })
1572 }
1573 fn force_sig(
1574 &mut self,
1575 mut node: Node,
1576 new_sig: Signature,
1577 span: &CodeSpan,
1578 ) -> UiuaResult<Node> {
1579 let Ok(sig) = node.sig() else {
1580 return Ok(node);
1581 };
1582 if new_sig == sig {
1583 return Ok(node);
1584 }
1585 let delta = sig.outputs() as isize - sig.args() as isize;
1586 let new_delta = new_sig.outputs() as isize - new_sig.args() as isize;
1587 match delta.cmp(&new_delta) {
1588 Ordering::Equal => {
1589 if sig.args() < new_sig.args() {
1590 let spandex = self.add_span(span.clone());
1591 let mut dip = Node::empty();
1592 for _ in 0..new_sig.args() {
1593 dip = Node::Mod(Primitive::Dip, eco_vec![dip.sig_node().unwrap()], spandex);
1594 }
1595 node.prepend(dip);
1596 }
1597 self.emit_diagnostic(
1598 format!("Signature mismatch: declared {new_sig} but inferred {sig}"),
1599 DiagnosticKind::Warning,
1600 span.clone(),
1601 );
1602 }
1603 Ordering::Less => {
1604 if new_sig.outputs() > 10 {
1605 return Err(self.error(
1606 span.clone(),
1607 format!(
1608 "Signature mismatch: declared {new_sig} but inferred {sig}. \
1609 Signatures with more than 10 outputs cannot be forced."
1610 ),
1611 ));
1612 }
1613 let diff = (new_delta - delta).unsigned_abs();
1614 let spandex = self.add_span(span.clone());
1615 let mut extra = Node::from_iter(
1616 (0..diff).map(|i| Node::new_push(Boxed(Value::from(format!("dbg-{}", i + 1))))),
1617 );
1618 for _ in 0..sig.outputs() {
1619 extra = Node::Mod(Primitive::Dip, eco_vec![extra.sig_node().unwrap()], spandex);
1620 }
1621 node.push(extra);
1622 self.emit_diagnostic(
1623 format!(
1624 "Signature mismatch: declared {new_sig} but inferred {sig}. \
1625 {diff} debug output{} will be generated.",
1626 if diff == 1 { "" } else { "s" }
1627 ),
1628 DiagnosticKind::Warning,
1629 span.clone(),
1630 );
1631 }
1632 Ordering::Greater => {
1633 if new_sig.args() > 10 {
1634 return Err(self.error(
1635 span.clone(),
1636 format!(
1637 "Signature mismatch: declared {new_sig} but inferred {sig}. \
1638 Signatures with more than 10 arguments cannot be forced."
1639 ),
1640 ));
1641 }
1642 let diff = (delta - new_delta).unsigned_abs();
1643 let spandex = self.add_span(span.clone());
1644 let mut pops =
1645 Node::from_iter((0..diff).map(|_| Node::Prim(Primitive::Pop, spandex)));
1646 for _ in 0..new_sig.outputs() {
1647 pops = Node::Mod(Primitive::Dip, eco_vec![pops.sig_node().unwrap()], spandex);
1648 }
1649 node.push(pops);
1650 self.emit_diagnostic(
1651 format!(
1652 "Signature mismatch: declared {new_sig} but inferred {sig}. \
1653 Additional arguments will be popped."
1654 ),
1655 DiagnosticKind::Warning,
1656 span.clone(),
1657 );
1658 }
1659 }
1660 Ok(node)
1661 }
1662 #[must_use]
1663 fn semantic_comment(&mut self, comment: SemanticComment, span: CodeSpan, inner: Node) -> Node {
1664 match comment {
1665 SemanticComment::Experimental => {
1666 self.scope.experimental = true;
1667 inner
1668 }
1669 SemanticComment::NoInline => Node::NoInline(inner.into()),
1670 SemanticComment::TrackCaller => Node::TrackCaller(
1671 match self.sig_of(&inner, &span) {
1672 Ok(sig) => SigNode::new(sig, inner),
1673 Err(e) => {
1674 self.errors.push(e);
1675 SigNode::default()
1676 }
1677 }
1678 .into(),
1679 ),
1680 SemanticComment::External => inner,
1681 SemanticComment::Deprecated(_) => inner,
1682 SemanticComment::Boo => {
1683 self.add_error(span, "The compiler is scared!");
1684 inner
1685 }
1686 }
1687 }
1688 fn ref_local(&self, r: &Ref) -> UiuaResult<Option<(Vec<LocalIndex>, LocalIndex)>> {
1692 if let Some((names, path_locals)) = self.ref_path(&r.path)? {
1693 if let Some(local) = names
1694 .get_prefer_function(&r.name.value, &self.asm)
1695 .or_else(|| {
1696 (r.name.value.strip_suffix('!')).and_then(|name| {
1697 names.get_prefer_module(name, &self.asm).filter(|local| {
1698 matches!(&self.asm.bindings[local.index].kind, BindingKind::Module(_))
1699 })
1700 })
1701 })
1702 {
1703 if local.public {
1704 Ok(Some((path_locals, local)))
1705 } else {
1706 Err(self.error(
1707 r.name.span.clone(),
1708 format!("`{}` is private", r.name.value),
1709 ))
1710 }
1711 } else {
1712 Err(self.error(
1713 r.name.span.clone(),
1714 format!("Item `{}` not found", r.name.value),
1715 ))
1716 }
1717 } else if let Some(local) = self.find_name_function(&r.name.value, &r.name.span) {
1718 Ok(Some((Vec::new(), local)))
1719 } else if r.path.is_empty() && CONSTANTS.iter().any(|def| def.name == r.name.value) {
1720 Ok(None)
1721 } else {
1722 Err(self.error(
1723 r.name.span.clone(),
1724 format!("Unknown identifier `{}`", r.name.value),
1725 ))
1726 }
1727 }
1728 fn find_name_module(&self, name: &str, span: &CodeSpan) -> Option<LocalIndex> {
1729 self.find_name_impl(name, span, true, false)
1730 }
1731 fn find_name_function(&self, name: &str, span: &CodeSpan) -> Option<LocalIndex> {
1732 self.find_name_impl(name, span, false, false)
1733 }
1734 fn find_name_impl(
1735 &self,
1736 name: &str,
1737 span: &CodeSpan,
1738 prefer_module: bool,
1739 stop_at_binding: bool,
1740 ) -> Option<LocalIndex> {
1741 let mut only_modules = [false, true];
1746 if prefer_module {
1747 only_modules.reverse();
1748 }
1749 'outer: for only_modules in only_modules {
1750 let mut hit_stop = false;
1751 for scope in self.scopes() {
1752 if matches!(scope.kind, ScopeKind::File(_))
1753 || stop_at_binding && matches!(scope.kind, ScopeKind::Binding)
1754 {
1755 if hit_stop {
1756 break 'outer;
1757 }
1758 hit_stop = true;
1759 }
1760 let local = if only_modules {
1762 scope.names.get_only_module(name, &self.asm)
1763 } else {
1764 scope.names.get_prefer_function(name, &self.asm)
1765 };
1766 if let Some(local) = local {
1767 return Some(local);
1768 }
1769 if let ScopeKind::Macro(Some(mac_local)) = &scope.kind {
1773 let mac = &self.index_macros[&mac_local.macro_index];
1774 if let Some(index) = mac.locals.get(span).copied() {
1775 return Some(LocalIndex {
1776 index,
1777 public: true,
1778 });
1779 }
1780 }
1781 }
1782 }
1783 let as_non_macro =
1785 self.find_name_impl(name.strip_suffix('!')?, span, true, stop_at_binding)?;
1786 if let BindingKind::Module(_) | BindingKind::Scope(_) | BindingKind::Import(_) =
1787 self.asm.bindings[as_non_macro.index].kind
1788 {
1789 Some(as_non_macro)
1791 } else {
1792 None
1793 }
1794 }
1795 fn ref_path(
1796 &self,
1797 path: &[RefComponent],
1798 ) -> UiuaResult<Option<(&LocalNames, Vec<LocalIndex>)>> {
1799 let Some(first) = path.first() else {
1800 return Ok(None);
1801 };
1802 let mut path_locals = Vec::new();
1803 let module_local = self
1804 .find_name_module(&first.module.value, &first.module.span)
1805 .ok_or_else(|| {
1806 self.error(
1807 first.module.span.clone(),
1808 format!("Unknown import `{}`", first.module.value),
1809 )
1810 })?;
1811 path_locals.push(module_local);
1812 let bkind = &self.asm.bindings[module_local.index].kind;
1813 let mut names = match bkind {
1814 BindingKind::Import(path) => &self.imports[path].names,
1815 BindingKind::Module(module) => &module.names,
1816 BindingKind::Scope(i) => &self.higher_scopes.get(*i).unwrap_or(&self.scope).names,
1817 BindingKind::Func(_) => {
1818 return Err(self.error(
1819 first.module.span.clone(),
1820 format!("`{}` is a function, not a module", first.module.value),
1821 ));
1822 }
1823 BindingKind::Const(_) => {
1824 return Err(self.error(
1825 first.module.span.clone(),
1826 format!("`{}` is a constant, not a module", first.module.value),
1827 ));
1828 }
1829 BindingKind::IndexMacro(_) => {
1830 return Err(self.error(
1831 first.module.span.clone(),
1832 format!("`{}` is an index macro, not a module", first.module.value),
1833 ));
1834 }
1835 BindingKind::CodeMacro(_) => {
1836 return Err(self.error(
1837 first.module.span.clone(),
1838 format!("`{}` is a code macro, not a module", first.module.value),
1839 ));
1840 }
1841 BindingKind::Error => return Ok(None),
1842 };
1843 for comp in path.iter().skip(1) {
1844 let submod_local = names
1845 .get_prefer_module(&comp.module.value, &self.asm)
1846 .ok_or_else(|| {
1847 self.error(
1848 comp.module.span.clone(),
1849 format!("Module `{}` not found", comp.module.value),
1850 )
1851 })?;
1852 path_locals.push(submod_local);
1853 let global = &self.asm.bindings[submod_local.index].kind;
1854 names = match global {
1855 BindingKind::Import(path) => &self.imports[path].names,
1856 BindingKind::Module(module) => &module.names,
1857 BindingKind::Scope(i) => &self.higher_scopes.get(*i).unwrap_or(&self.scope).names,
1858 BindingKind::Func(_) => {
1859 return Err(self.error(
1860 comp.module.span.clone(),
1861 format!("`{}` is a function, not a module", comp.module.value),
1862 ));
1863 }
1864 BindingKind::Const(_) => {
1865 return Err(self.error(
1866 comp.module.span.clone(),
1867 format!("`{}` is a constant, not a module", comp.module.value),
1868 ));
1869 }
1870 BindingKind::IndexMacro(_) => {
1871 return Err(self.error(
1872 comp.module.span.clone(),
1873 format!("`{}` is an index macro, not a module", comp.module.value),
1874 ));
1875 }
1876 BindingKind::CodeMacro(_) => {
1877 return Err(self.error(
1878 comp.module.span.clone(),
1879 format!("`{}` is a code macro, not a module", comp.module.value),
1880 ));
1881 }
1882 BindingKind::Error => return Ok(None),
1883 };
1884 }
1885
1886 Ok(Some((names, path_locals)))
1887 }
1888 fn reference(&mut self, r: Ref) -> Node {
1889 if r.path.is_empty() {
1890 self.ident(r.name.value, r.name.span)
1891 } else {
1892 match self.ref_local(&r) {
1893 Ok(Some((path_locals, local))) => {
1894 self.validate_local(&r.name.value, local, &r.name.span);
1895 for (local, comp) in path_locals.into_iter().zip(&r.path) {
1896 self.validate_local(&comp.module.value, local, &comp.module.span);
1897 (self.code_meta.global_references)
1898 .insert(comp.module.span.clone(), local.index);
1899 }
1900 self.code_meta
1901 .global_references
1902 .insert(r.name.span.clone(), local.index);
1903 self.global_index(local.index, r.name.span)
1904 }
1905 Ok(None) => self.ident(r.name.value, r.name.span),
1906 Err(e) => {
1907 self.errors.push(e);
1908 Node::new_push(Value::default())
1909 }
1910 }
1911 }
1912 }
1913 fn completions(&self, prefix: &str, names: &LocalNames, public_only: bool) -> Vec<Completion> {
1914 let mut completions = Vec::new();
1915 for (name, local) in names.visible_iter() {
1916 if public_only && !local.public {
1917 continue;
1918 }
1919 if name.starts_with(prefix) {
1920 completions.push(Completion {
1921 text: name.into(),
1922 index: local.index,
1923 replace: true,
1924 });
1925 }
1926 let subnames = match &self.asm.bindings[local.index].kind {
1927 BindingKind::Module(m) => &m.names,
1928 BindingKind::Import(path) => &self.imports[path].names,
1929 _ => continue,
1930 };
1931 let subcompletions = self.completions(prefix, subnames, true);
1932 completions.extend(subcompletions.into_iter().map(|c| Completion {
1933 text: format!("{name}~{}", c.text),
1934 ..c
1935 }));
1936 }
1937 completions
1938 }
1939 fn ident(&mut self, ident: Ident, span: CodeSpan) -> Node {
1940 let completions = self.completions(&ident, &self.scope.names, false);
1942 if !completions.is_empty() {
1943 self.code_meta.completions.insert(span.clone(), completions);
1944 }
1945
1946 if let Some(local) = self.find_name_impl(&ident, &span, false, true) {
1947 self.validate_local(&ident, local, &span);
1949 (self.code_meta.global_references).insert(span.clone(), local.index);
1950 self.global_index(local.index, span)
1951 } else if let Some(curr) =
1952 (self.current_bindings.last_mut()).filter(|curr| curr.name == ident)
1953 {
1954 curr.recurses += 1;
1956 let global_index = curr.global_index;
1957 (self.code_meta.global_references).insert(span.clone(), global_index);
1958 if let Some(sig) = curr.signature.filter(|sig| sig.outputs() <= 10) {
1959 Node::CallGlobal(global_index, sig)
1960 } else {
1961 Node::empty()
1962 }
1963 } else if let Some(local) = self.find_name_function(&ident, &span) {
1964 self.validate_local(&ident, local, &span);
1966 (self.code_meta.global_references).insert(span.clone(), local.index);
1967 self.global_index(local.index, span)
1968 } else if let Some(constant) = CONSTANTS.iter().find(|c| c.name == ident) {
1969 if let Some(suggestion) = constant.deprecation {
1971 if self.deprecated_const_errors.insert(constant.name) {
1972 let suggestion = if suggestion.is_empty() {
1973 String::new()
1974 } else {
1975 format!(". {suggestion}")
1976 };
1977 let message = format!(
1978 "{} is deprecated and will be removed in a future version{}",
1979 constant.name, suggestion
1980 );
1981 self.emit_diagnostic(message, DiagnosticKind::Warning, span.clone());
1982 }
1983 }
1984 self.code_meta
1985 .constant_references
1986 .insert(span.clone().sp(ident));
1987 let value = (constant.value)
1988 .resolve(self.scope_file_path(), self.backend().clone())
1989 .unwrap_or_else(|e| {
1990 self.add_error(span, e);
1991 Value::default()
1992 });
1993 Node::Push(value)
1994 } else {
1995 self.add_error(span, format!("Unknown identifier `{ident}`"));
1996 Node::new_push(Value::default())
1997 }
1998 }
1999 fn scope_file_path(&self) -> Option<&Path> {
2000 for scope in self.scopes() {
2001 if let Some(file_path) = &scope.file_path {
2002 return Some(file_path);
2003 }
2004 }
2005 None
2006 }
2007 fn global_index(&mut self, index: usize, span: CodeSpan) -> Node {
2008 let binfo = &mut self.asm.bindings.make_mut()[index];
2009 binfo.used = true;
2010 let bkind = binfo.kind.clone();
2011 match bkind {
2012 BindingKind::Const(Some(val)) => Node::new_push(val),
2013 BindingKind::Const(None) => Node::CallGlobal(index, Signature::new(0, 1)),
2014 BindingKind::Func(f) => Node::Call(f, self.add_span(span.clone())),
2015 BindingKind::Import(path) => {
2016 if let Some(local) = (self.imports.get(&path))
2017 .and_then(|m| m.names.get_prefer_function("Call", &self.asm))
2018 {
2019 self.code_meta.global_references.remove(&span);
2020 (self.code_meta.global_references).insert(span.clone(), local.index);
2021 self.global_index(local.index, span)
2022 } else {
2023 self.add_error(
2024 span,
2025 "Module cannot be called here as \
2026 it has no `Call` function.",
2027 );
2028 Node::empty()
2029 }
2030 }
2031 global @ (BindingKind::Module(_) | BindingKind::Scope(_)) => {
2032 let names = match &global {
2034 BindingKind::Module(m) => &m.names,
2035 BindingKind::Scope(i) => {
2036 &self.higher_scopes.get(*i).unwrap_or(&self.scope).names
2037 }
2038 _ => unreachable!(),
2039 };
2040 if let Some(local) = names.get_last("Call").or_else(|| names.get_last("New")) {
2041 self.code_meta.global_references.remove(&span);
2042 (self.code_meta.global_references).insert(span.clone(), local.index);
2043 self.global_index(local.index, span.clone())
2044 } else {
2045 self.add_error(
2046 span,
2047 "Module cannot be called here as \
2048 it has no `Call` or `New` function.",
2049 );
2050 Node::empty()
2051 }
2052 }
2053 BindingKind::IndexMacro(_) | BindingKind::CodeMacro(_) => {
2054 Node::empty()
2056 }
2057 BindingKind::Error => Node::empty(),
2058 }
2059 }
2060 fn func(&mut self, func: Func, span: CodeSpan) -> UiuaResult<Node> {
2061 let root_start = self.asm.root.len();
2062 self.in_scope(ScopeKind::Function, |comp| {
2063 comp.items(func.lines, ItemCompMode::Function)
2064 })?;
2065 let mut root = self.asm.root.split_off(root_start);
2066
2067 let sig = match root.sig() {
2069 Ok(mut sig) => {
2070 if let Some(declared_sig) = &func.signature {
2071 root = self.force_sig(root, declared_sig.value, &declared_sig.span)?;
2072 sig = declared_sig.value;
2073 }
2074 Some(sig)
2075 }
2076 Err(e) => return Err(self.error(span, format!("Cannot infer function signature: {e}"))),
2077 };
2078 if let Some(sig) = sig {
2079 self.code_meta.function_sigs.insert(
2080 span.clone(),
2081 SigDecl {
2082 sig,
2083 explicit: func.signature.is_some(),
2084 inline: true,
2085 set_inverses: Default::default(),
2086 },
2087 );
2088 }
2089 Ok(root)
2090 }
2091 fn switch(&mut self, branches: Vec<Sp<Word>>, span: CodeSpan) -> UiuaResult<Node> {
2092 let count = branches.len();
2093 let mut br = EcoVec::with_capacity(count);
2095 let mut rigid_indices = Vec::new();
2096 let mut flex_indices = Vec::new();
2097 for (i, branch) in branches.into_iter().enumerate() {
2098 let span = branch.span.clone();
2099 let SigNode { node, sig } = self.word_sig(branch)?;
2100 let is_flex = node
2101 .iter()
2102 .rposition(|node| matches!(node, Node::Prim(Primitive::Assert, _)))
2103 .is_some_and(|end| {
2104 (0..end).rev().any(|start| {
2105 let sub = node.slice(start..end);
2106 match sub.as_slice() {
2107 [Node::Push(val), Node::Prim(Primitive::Dup, _)]
2108 | [Node::Push(val), Node::Push(..)]
2109 if val != &Value::from(1) =>
2110 {
2111 return true;
2112 }
2113 [Node::Format(..), Node::Prim(Primitive::Dup, _)] => return true,
2114 _ => (),
2115 }
2116 sub.is_pure(&self.asm) || !sub.sig().is_ok_and(|sig| sig == (0, 2))
2117 })
2118 });
2119 br.push((SigNode::new(sig, node), span));
2120 if is_flex {
2121 flex_indices.push(i);
2122 } else {
2123 rigid_indices.push(i);
2124 }
2125 }
2126 let mut rigid_funcs = rigid_indices.into_iter().map(|i| &br[i]);
2127 let mut sig = None;
2128 if let Some((arg, _)) = rigid_funcs.next() {
2129 sig = Some(arg.sig);
2130 let sig = sig.as_mut().unwrap();
2131 for (arg, span) in rigid_funcs {
2133 if arg.sig.is_compatible_with(*sig) {
2134 *sig = sig.max_with(arg.sig);
2135 } else if arg.sig.outputs() == sig.outputs() {
2136 sig.update_args(|a| a.max(arg.sig.args()));
2137 sig.update_under_args(|a| a.max(arg.sig.under_args()));
2138 } else {
2139 self.add_error(
2140 span.clone(),
2141 format!(
2142 "Switch branch's signature {} is \
2143 incompatible with previous branches {sig}",
2144 arg.sig
2145 ),
2146 );
2147 }
2148 }
2149 }
2150 let mut flex_funcs = flex_indices.into_iter().map(|i| &br[i]);
2151 let mut sig = sig.unwrap_or_else(|| flex_funcs.next().unwrap().0.sig);
2152 for (arg, _) in flex_funcs {
2153 sig.update_args(|a| a.max(arg.sig.args()));
2154 sig.update_under_args(|a| a.max(arg.sig.under_args()));
2155 }
2156
2157 let span = self.add_span(span.clone());
2158 Ok(Node::Switch {
2159 branches: br.into_iter().map(|(arg, _)| arg).collect(),
2160 sig,
2161 span,
2162 under_cond: false,
2163 })
2164 }
2165 fn handle_primitive_deprecation(&mut self, prim: Primitive, span: &CodeSpan) {
2166 if let Some(suggestion) = prim.deprecation_suggestion() {
2167 if !self.deprecated_prim_errors.insert(prim) {
2168 return;
2169 }
2170 let suggestion = if suggestion.is_empty() {
2171 String::new()
2172 } else {
2173 format!(", {suggestion}")
2174 };
2175 self.emit_diagnostic(
2176 format!(
2177 "{} is deprecated and will be removed in a future version{}",
2178 prim.format(),
2179 suggestion
2180 ),
2181 DiagnosticKind::Warning,
2182 span.clone(),
2183 );
2184 }
2185 }
2186 fn handle_primitive_experimental(&mut self, prim: Primitive, span: &CodeSpan) {
2187 if prim.is_experimental() {
2188 self.experimental_error(span, || {
2189 format!(
2190 "{} is experimental. To use it, add \
2191 `# Experimental!` to the top of the file.",
2192 prim.format()
2193 )
2194 });
2195 }
2196 }
2197 fn validate_primitive(&mut self, prim: Primitive, span: &CodeSpan) {
2198 self.handle_primitive_experimental(prim, span);
2199 self.handle_primitive_deprecation(prim, span);
2200 }
2201 fn primitive(&mut self, prim: Primitive, span: CodeSpan) -> Node {
2202 self.validate_primitive(prim, &span);
2203 let spandex = self.add_span(span.clone());
2204 Node::Prim(prim, spandex)
2205 }
2206 #[allow(clippy::match_single_binding)]
2207 fn subscript(&mut self, sub: Subscripted, span: CodeSpan) -> UiuaResult<Node> {
2208 let scr = sub.script;
2209 Ok(match sub.word.value {
2210 Word::Modified(m) => match m.modifier.value {
2211 Modifier::Ref(_) | Modifier::Macro(..) => {
2212 self.add_error(
2213 m.modifier.span.clone().merge(scr.span.clone()),
2214 "Subscripts are not implemented for macros",
2215 );
2216 self.modified(*m, Some(scr.map(Into::into)))?
2217 }
2218 Modifier::Primitive(prim) => {
2219 if prim.subscript_margs(Some(&scr.value)).is_none() {
2220 self.add_error(
2221 m.modifier.span.clone().merge(scr.span.clone()),
2222 format!("Subscripts are not implemented for {}", prim.format()),
2223 );
2224 }
2225 self.modified(*m, Some(scr.map(Into::into)))?
2226 }
2227 },
2228 Word::Primitive(prim) => self.subscript_prim(prim, sub.word.span, scr)?,
2229 _ => {
2230 self.add_error(span.clone(), "Subscripts are not allowed in this context");
2231 self.word(sub.word)?
2232 }
2233 })
2234 }
2235 fn subscript_prim(
2236 &mut self,
2237 prim: Primitive,
2238 span: CodeSpan,
2239 scr: Sp<Subscript>,
2240 ) -> UiuaResult<Node> {
2241 use Primitive::*;
2242 Ok(match prim {
2243 prim if prim.class() == PrimClass::DyadicPervasive => {
2244 let Some(nos) = self.subscript_n_or_side(&scr, prim.format()) else {
2245 return Ok(self.primitive(prim, span));
2246 };
2247 self.validate_primitive(prim, &span);
2248 match nos {
2249 SubNOrSide::N(n) => {
2250 Node::from_iter([Node::new_push(n), self.primitive(prim, span)])
2251 }
2252 SubNOrSide::Side(side) => {
2253 self.experimental_error_it(&scr.span, || {
2254 format!("Sided {}", prim.format())
2255 });
2256 let sub_span = self.add_span(scr.span);
2257 let mut node = Node::Prim(Primitive::Fix, sub_span);
2258 if side == SubSide::Right {
2259 node = Node::Mod(
2260 Primitive::Dip,
2261 eco_vec![node.sig_node().unwrap()],
2262 sub_span,
2263 );
2264 }
2265 node.push(self.primitive(prim, span));
2266 node
2267 }
2268 }
2269 }
2270 EncodeBytes => {
2271 let Some(side) = self.subscript_side_only(&scr, EncodeBytes.format()) else {
2272 return Ok(self.primitive(EncodeBytes, span));
2273 };
2274 self.validate_primitive(prim, &span);
2275 let span = self.add_span(span);
2276 Node::ImplPrim(ImplPrimitive::SidedEncodeBytes(side), span)
2277 }
2278 Join => {
2279 let Some(nos) = self.subscript_n_or_side(&scr, Join.format()) else {
2280 return Ok(self.primitive(Join, span));
2281 };
2282 match nos {
2283 SubNOrSide::N(n) => match self.positive_subscript(n, Join, &span) {
2284 0 => Node::new_push(Value::default()),
2285 1 => Node::Prim(Identity, self.add_span(span)),
2286 2 => Node::Prim(Join, self.add_span(span)),
2287 n => Node::ImplPrim(ImplPrimitive::MultiJoin(n), self.add_span(span)),
2288 },
2289 SubNOrSide::Side(side) => {
2290 Node::ImplPrim(ImplPrimitive::SidedJoin(side), self.add_span(span))
2291 }
2292 }
2293 }
2294 prim => {
2295 let Some(n) = self.subscript_n_only(&scr, prim.format()) else {
2296 return Ok(self.primitive(prim, span));
2297 };
2298 self.validate_primitive(prim, &span);
2299 match prim {
2300 prim if prim.sig().is_some_and(|sig| sig == (2, 1))
2301 && prim
2302 .subscript_sig(Some(&Subscript::numeric(2)))
2303 .is_some_and(|sig| sig == (1, 1)) =>
2304 {
2305 Node::from_iter([Node::new_push(n), self.primitive(prim, span)])
2306 }
2307 Deshape => {
2308 if n == 1 {
2309 self.emit_diagnostic(
2310 format!("{Deshape}₁ is equivalent to just {}", Deshape.format()),
2311 DiagnosticKind::Advice,
2312 span.clone().merge(scr.span),
2313 );
2314 Node::Prim(Deshape, self.add_span(span))
2315 } else {
2316 Node::ImplPrim(ImplPrimitive::DeshapeSub(n), self.add_span(span))
2317 }
2318 }
2319 Transpose => {
2320 self.subscript_experimental(prim, &span);
2321 if n == 0 {
2322 return Ok(Node::empty());
2323 }
2324 Node::from([
2325 Node::new_push(n - 1),
2326 Node::ImplPrim(ImplPrimitive::AntiOrient, self.add_span(span)),
2327 ])
2328 }
2329 Neg => {
2330 use crate::Complex;
2331 let rotation = match n {
2332 -1..=1 => Complex::ONE,
2334 2 | -2 => -Complex::ONE,
2335 4 => Complex::I,
2336 -4 => -Complex::I,
2337 _ => Complex::from_polar(1.0, std::f64::consts::TAU / n as f64),
2338 };
2339 Node::from_iter([Node::new_push(rotation), self.primitive(Mul, span)])
2340 }
2341 Sqrt => {
2342 if n == 0 {
2343 self.add_error(span.clone(), "Cannot take 0th root");
2344 }
2345 Node::from_iter([Node::new_push(1.0 / n as f64), self.primitive(Pow, span)])
2346 }
2347 Exp => {
2348 let span = self.add_span(span);
2349 Node::from_iter([
2350 Node::new_push(n as f64),
2351 Node::Prim(Flip, span),
2352 Node::Prim(Pow, span),
2353 ])
2354 }
2355 Floor | Ceil => {
2356 self.subscript_experimental(prim, &span);
2357 let mul = 10f64.powi(n);
2358 Node::from_iter([
2359 Node::new_push(mul),
2360 self.primitive(Mul, span.clone()),
2361 self.primitive(prim, span.clone()),
2362 Node::new_push(mul),
2363 self.primitive(Div, span),
2364 ])
2365 }
2366 Round => {
2367 let mul = 10f64.powi(n);
2368 Node::from_iter([
2369 Node::new_push(mul),
2370 self.primitive(Mul, span.clone()),
2371 self.primitive(prim, span.clone()),
2372 Node::new_push(mul),
2373 self.primitive(Div, span),
2374 ])
2375 }
2376 Rand => Node::from_iter([
2377 self.primitive(Rand, span.clone()),
2378 Node::new_push(n),
2379 self.primitive(Mul, span.clone()),
2380 self.primitive(Floor, span),
2381 ]),
2382 Utf8 => match n {
2383 8 => self.primitive(Utf8, span),
2384 16 => Node::ImplPrim(ImplPrimitive::Utf16, self.add_span(span)),
2385 _ => {
2386 self.add_error(span.clone(), "Only UTF-8 and UTF-16 are supported");
2387 self.primitive(Utf8, span)
2388 }
2389 },
2390 Couple => match n {
2391 1 => self.primitive(Fix, span),
2392 2 => self.primitive(Couple, span),
2393 n => Node::Array {
2394 len: self.positive_subscript(n, Couple, &span),
2395 inner: Node::empty().into(),
2396 boxed: false,
2397 allow_ext: true,
2398 prim: Some(Couple),
2399 span: self.add_span(span),
2400 },
2401 },
2402 Box => Node::Array {
2403 len: self.positive_subscript(n, Box, &span),
2404 inner: Node::empty().into(),
2405 boxed: true,
2406 allow_ext: false,
2407 prim: Some(Box),
2408 span: self.add_span(span),
2409 },
2410 Parse => {
2411 if !matches!(n, 1..=36 | 64) {
2412 self.add_error(span.clone(), format!("Cannot parse base {n}"));
2413 }
2414 Node::ImplPrim(ImplPrimitive::ParseSub(n as usize), self.add_span(span))
2415 }
2416 Args => Node::ImplPrim(
2417 ImplPrimitive::StackN {
2418 n: self.positive_subscript(n, Args, &span),
2419 inverse: false,
2420 },
2421 self.add_span(span),
2422 ),
2423 First | Last => {
2424 let n = self.positive_subscript(n, prim, &span);
2425 let span = self.add_span(span);
2426 match n {
2427 0 => Node::Prim(Pop, span),
2428 1 => Node::Prim(prim, span),
2429 n if prim == First => Node::from_iter([
2430 Node::new_push(n),
2431 Node::Prim(Take, span),
2432 Node::Unpack {
2433 count: n,
2434 unbox: false,
2435 allow_ext: false,
2436 prim: Some(First),
2437 span,
2438 },
2439 ]),
2440 n => Node::from_iter([
2441 Node::new_push(-(n as i32)),
2442 Node::Prim(Take, span),
2443 Node::Prim(Reverse, span),
2444 Node::Unpack {
2445 count: n,
2446 unbox: false,
2447 allow_ext: false,
2448 prim: Some(Last),
2449 span,
2450 },
2451 ]),
2452 }
2453 }
2454 Bits => {
2455 let n = self.positive_subscript(n, Bits, &span);
2456 let span = self.add_span(span);
2457 Node::ImplPrim(ImplPrimitive::NBits(n), span)
2458 }
2459 Len => {
2460 let span = self.add_span(span);
2461 Node::from_iter([
2462 Node::Prim(Shape, span),
2463 Node::new_push(n),
2464 Node::Prim(Select, span),
2465 ])
2466 }
2467 Shape => {
2468 let span = self.add_span(span);
2469 Node::from_iter([
2470 Node::Prim(Shape, span),
2471 Node::new_push(n),
2472 Node::Prim(Take, span),
2473 ])
2474 }
2475 Range => {
2476 if n == 0 {
2477 self.emit_diagnostic(
2478 format!("{Range}₀ is equivalent to just {}", Range.format()),
2479 DiagnosticKind::Advice,
2480 span.clone().merge(scr.span),
2481 );
2482 Node::Prim(Range, self.add_span(span))
2483 } else {
2484 let span = self.add_span(span);
2485 Node::from_iter([
2486 Node::Prim(Range, span),
2487 Node::new_push(n),
2488 Node::Prim(Add, span),
2489 ])
2490 }
2491 }
2492 Keep => {
2493 let n = self.positive_subscript(n, Keep, &span);
2494 let span = self.add_span(span);
2495 Node::ImplPrim(ImplPrimitive::MultiKeep(n), span)
2496 }
2497 Occurrences => {
2498 let n = self.positive_subscript(n, Occurrences, &span);
2499 let span = self.add_span(span);
2500 Node::from([
2501 Node::Prim(Occurrences, span),
2502 Node::new_push(n),
2503 Node::Prim(Lt, span),
2504 ])
2505 }
2506 Classify => {
2507 let n = self.positive_subscript(n, Classify, &span);
2508 Node::ImplPrim(ImplPrimitive::ClassifySub(n), self.add_span(span))
2509 }
2510 _ => {
2511 self.add_error(
2512 span.clone(),
2513 format!("Subscripts are not implemented for {}", prim.format()),
2514 );
2515 self.primitive(prim, span)
2516 }
2517 }
2518 }
2519 })
2520 }
2521}
2522
2523#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2524enum SubNOrSide<N = i32> {
2525 N(N),
2526 Side(SubSide),
2527}
2528
2529impl<N: PartialEq> PartialEq<N> for SubNOrSide<N> {
2530 fn eq(&self, other: &N) -> bool {
2531 match self {
2532 SubNOrSide::N(n) => *n == *other,
2533 SubNOrSide::Side(_) => false,
2534 }
2535 }
2536}
2537
2538impl Compiler {
2539 fn validate_subscript(&mut self, sub: Sp<Subscript>) -> Sp<Subscript<i32>> {
2540 let side = sub.value.side;
2541 let num = (sub.value.num.as_ref()).and_then(|num| self.numeric_subscript_n(num, &sub.span));
2542 if num.is_none() && side.is_none() {
2543 self.add_error(sub.span.clone(), "Subscript is incomplete");
2544 }
2545 sub.span.sp(Subscript { num, side })
2546 }
2547 fn numeric_subscript_n(&mut self, num: &NumericSubscript, span: &CodeSpan) -> Option<i32> {
2548 match num {
2549 NumericSubscript::N(n) => Some(*n),
2550 NumericSubscript::NegOnly => {
2551 self.add_error(span.clone(), "Subscript is incomplete");
2552 None
2553 }
2554 NumericSubscript::TooLarge(_) => {
2555 self.add_error(span.clone(), "Subscript is too large");
2556 None
2557 }
2558 }
2559 }
2560 fn subscript_n_or_side(
2561 &mut self,
2562 sub: &Sp<Subscript>,
2563 for_what: impl fmt::Display,
2564 ) -> Option<SubNOrSide> {
2565 match (&sub.value.num, sub.value.side) {
2566 (None, None) => {
2567 self.add_error(sub.span.clone(), "Subscript is incomplete");
2568 None
2569 }
2570 (Some(num), None) => self.numeric_subscript_n(num, &sub.span).map(SubNOrSide::N),
2571 (None, Some(side)) => {
2572 if side.n.is_some() {
2573 self.add_error(
2574 sub.span.clone(),
2575 format!("Sided subscript quantifiers are not allowed for {for_what}"),
2576 );
2577 }
2578 Some(SubNOrSide::Side(side.side))
2579 }
2580 (Some(_), Some(_)) => {
2581 self.add_error(
2582 sub.span.clone(),
2583 format!("Mixed subscripts are not allowed for {for_what}"),
2584 );
2585 None
2586 }
2587 }
2588 }
2589 fn subscript_n_only(
2590 &mut self,
2591 sub: &Sp<Subscript>,
2592 for_what: impl fmt::Display + Copy,
2593 ) -> Option<i32> {
2594 let nos = self.subscript_n_or_side(sub, for_what)?;
2595 match nos {
2596 SubNOrSide::N(n) => Some(n),
2597 SubNOrSide::Side(_) => {
2598 self.add_error(
2599 sub.span.clone(),
2600 format!("Sided subscripts are not allowed for {for_what}"),
2601 );
2602 None
2603 }
2604 }
2605 }
2606 fn subscript_side_only(
2607 &mut self,
2608 sub: &Sp<Subscript>,
2609 for_what: impl fmt::Display + Copy,
2610 ) -> Option<SubSide> {
2611 let nos = self.subscript_n_or_side(sub, for_what)?;
2612 match nos {
2613 SubNOrSide::N(_) => {
2614 self.add_error(
2615 sub.span.clone(),
2616 format!("Numeric subscripts are not allowed for {for_what}"),
2617 );
2618 None
2619 }
2620 SubNOrSide::Side(side) => Some(side),
2621 }
2622 }
2623 fn positive_subscript(&mut self, n: i32, prim: Primitive, span: &CodeSpan) -> usize {
2624 if n < 0 {
2625 self.add_error(
2626 span.clone(),
2627 format!("Subscript for {} must be positive", prim.format()),
2628 );
2629 }
2630 n.unsigned_abs() as usize
2631 }
2632 fn subscript_experimental(&mut self, prim: Primitive, span: &CodeSpan) {
2633 self.experimental_error(span, || {
2634 format!(
2635 "Subcripted {} is experimental. To use it, \
2636 add `# Experimental!` to the top of the file.",
2637 prim.format()
2638 )
2639 });
2640 }
2641 pub fn diagnostics(&self) -> &BTreeSet<Diagnostic> {
2643 &self.diagnostics
2644 }
2645 pub fn diagnostics_mut(&mut self) -> &mut BTreeSet<Diagnostic> {
2647 &mut self.diagnostics
2648 }
2649 pub fn take_diagnostics(&mut self) -> BTreeSet<Diagnostic> {
2653 take(&mut self.diagnostics)
2654 }
2655 pub fn emit_diagnostic(
2657 &mut self,
2658 message: impl Into<String>,
2659 kind: DiagnosticKind,
2660 span: impl Into<Span>,
2661 ) {
2662 let inputs = self.asm.inputs.clone();
2663 self.emit_diagnostic_impl(Diagnostic::new(message.into(), span, kind, inputs));
2664 }
2665 #[allow(clippy::print_stdout)]
2666 fn emit_diagnostic_impl(&mut self, diagnostic: Diagnostic) {
2667 if self.print_diagnostics {
2668 eprintln!("{}", diagnostic.report());
2669 } else {
2670 self.diagnostics.insert(diagnostic);
2671 }
2672 }
2673 fn add_error(&mut self, span: impl Into<Span>, message: impl ToString) {
2674 let e = self.error(span, message);
2675 self.errors.push(e);
2676 }
2677 fn experimental_error<S>(&mut self, span: &CodeSpan, message: impl FnOnce() -> S)
2678 where
2679 S: fmt::Display,
2680 {
2681 if !self.allow_experimental() {
2682 self.scope.experimental_error = true;
2683 self.add_error(span.clone(), message().to_string());
2684 }
2685 }
2686 fn experimental_error_it<S>(&mut self, span: &CodeSpan, thing: impl FnOnce() -> S)
2687 where
2688 S: fmt::Display,
2689 {
2690 self.experimental_error(span, || {
2691 format!(
2692 "{} is experimental. Add `# Experimental!` \
2693 to the top of the file to use it.",
2694 thing()
2695 )
2696 })
2697 }
2698 fn allow_experimental(&self) -> bool {
2699 let take = self
2700 .scopes()
2701 .position(|sc| matches!(sc.kind, ScopeKind::File(_)))
2702 .map(|i| i + 1)
2703 .unwrap_or(usize::MAX);
2704 self.scopes()
2705 .take(take)
2706 .any(|sc| sc.experimental || sc.experimental_error)
2707 }
2708 fn error(&self, span: impl Into<Span>, message: impl ToString) -> UiuaError {
2709 UiuaErrorKind::Run {
2710 message: span.into().sp(message.to_string()),
2711 info: Vec::new(),
2712 inputs: self.asm.inputs.clone().into(),
2713 }
2714 .into()
2715 }
2716 fn error_with_info<S, M>(
2717 &self,
2718 span: impl Into<Span>,
2719 message: impl ToString,
2720 info: impl IntoIterator<Item = (S, M)>,
2721 ) -> UiuaError
2722 where
2723 S: Into<Span>,
2724 M: ToString,
2725 {
2726 UiuaErrorKind::Run {
2727 message: span.into().sp(message.to_string()),
2728 info: info
2729 .into_iter()
2730 .map(|(s, m)| s.into().sp(m.to_string()))
2731 .collect(),
2732 inputs: self.asm.inputs.clone().into(),
2733 }
2734 .into()
2735 }
2736 fn validate_local(&mut self, name: &str, local: LocalIndex, span: &CodeSpan) {
2737 if let Some(suggestion) = &self.asm.bindings[local.index].meta.deprecation {
2739 let mut message = format!("{name} is deprecated");
2740 if !suggestion.is_empty() {
2741 message.push_str(". ");
2742 message.push_str(suggestion);
2743 if message.ends_with(char::is_alphanumeric) {
2744 message.push('.');
2745 }
2746 }
2747 self.emit_diagnostic(message, DiagnosticKind::Warning, span.clone());
2748 }
2749 if local.public {
2751 return;
2752 }
2753 let get = |scope: &Scope| {
2754 (scope.names.get_last(name)).or_else(|| scope.names.get_last(name.strip_suffix('!')?))
2755 };
2756 if !local.public
2757 && get(&self.scope)
2758 .filter(|l| l.public || !matches!(self.scope.kind, ScopeKind::AllInModule))
2759 .or_else(|| self.scopes_to_file().skip(1).find_map(get))
2760 .is_none_or(|l| l.index != local.index)
2761 {
2762 self.add_error(span.clone(), format!("`{name}` is private"));
2763 }
2764 }
2765 pub fn get_span(&self, span: usize) -> Span {
2767 self.asm.spans[span].clone()
2768 }
2769 pub fn add_span(&mut self, span: impl Into<Span>) -> usize {
2771 let span = span.into();
2772 if let Some(i) = self.asm.spans.iter().position(|s| *s == span) {
2773 return i;
2774 }
2775 let idx = self.asm.spans.len();
2776 self.asm.spans.push(span);
2777 idx
2778 }
2779 pub fn create_function(
2781 &mut self,
2782 signature: impl Into<Signature>,
2783 f: impl Fn(&mut Uiua) -> UiuaResult + SendSyncNative + 'static,
2784 ) -> Function {
2785 let sig = signature.into();
2786 let df = self.create_dynamic_function(sig, f);
2787 self.asm
2788 .add_function(FunctionId::Unnamed, sig, Node::Dynamic(df))
2789 }
2790 fn create_dynamic_function(
2791 &mut self,
2792 sig: Signature,
2793 f: impl Fn(&mut Uiua) -> UiuaResult + SendSyncNative + 'static,
2794 ) -> DynamicFunction {
2795 let index = self.asm.dynamic_functions.len();
2796 self.asm.dynamic_functions.push(Arc::new(f));
2797 DynamicFunction { index, sig }
2798 }
2799 pub fn bind_function(&mut self, name: impl Into<EcoString>, function: Function) -> UiuaResult {
2804 self.bind_function_with_meta(name, function, BindingMeta::default())
2805 }
2806 fn bind_function_with_meta(
2807 &mut self,
2808 name: impl Into<EcoString>,
2809 function: Function,
2810 meta: BindingMeta,
2811 ) -> UiuaResult {
2812 let name = name.into();
2813 let local = LocalIndex {
2814 index: self.next_global,
2815 public: true,
2816 };
2817 self.next_global += 1;
2818 self.compile_bind_function(name, local, function, 0, meta)?;
2819 Ok(())
2820 }
2821 pub fn create_bind_function(
2828 &mut self,
2829 name: impl Into<EcoString>,
2830 signature: impl Into<Signature>,
2831 f: impl Fn(&mut Uiua) -> UiuaResult + SendSyncNative + 'static,
2832 ) -> UiuaResult {
2833 let name = name.into();
2834 if let Some(index) = self.externals.get(&name).copied() {
2835 let df = self.create_dynamic_function(signature.into(), f);
2836 self.asm.functions.make_mut()[index] = Node::Dynamic(df);
2837 Ok(())
2838 } else {
2839 let function = self.create_function(signature, f);
2840 let meta = BindingMeta {
2841 external: true,
2842 ..Default::default()
2843 };
2844 self.bind_function_with_meta(name, function, meta)
2845 }
2846 }
2847 fn sig_of(&self, node: &Node, span: &CodeSpan) -> UiuaResult<Signature> {
2848 node.sig().map_err(|e| {
2849 self.error(
2850 span.clone(),
2851 format!("Cannot infer function signature: {e}"),
2852 )
2853 })
2854 }
2855}
2856
2857fn words_look_pervasive(words: &[Sp<Word>]) -> bool {
2858 use Primitive::*;
2859 words.iter().all(|word| match &word.value {
2860 Word::Primitive(p) if p.class().is_pervasive() => true,
2861 Word::Primitive(Dup | Dip | Identity | Fork | Under | Each) => true,
2862 Word::Func(func) if func.word_lines().all(words_look_pervasive) => true,
2863 Word::Number(..) | Word::Char(..) => true,
2864 Word::Modified(m) if m.modifier.value == Modifier::Primitive(Primitive::Each) => true,
2865 _ => false,
2866 })
2867}
2868
2869fn recurse_words(words: &[Sp<Word>], f: &mut dyn FnMut(&Sp<Word>)) {
2870 for word in words {
2871 f(word);
2872 match &word.value {
2873 Word::Strand(items) => recurse_words(items, f),
2874 Word::Array(arr) => arr.word_lines().for_each(|line| {
2875 recurse_words(line, f);
2876 }),
2877 Word::Func(func) => func.word_lines().for_each(|line| {
2878 recurse_words(line, f);
2879 }),
2880 Word::Modified(m) => recurse_words(&m.operands, f),
2881 Word::Pack(pack) => pack.branches.iter().for_each(|branch| {
2882 (branch.value.word_lines()).for_each(|line| recurse_words(line, f))
2883 }),
2884 Word::Subscripted(sub) => recurse_words(slice::from_ref(&sub.word), f),
2885 _ => {}
2886 }
2887 }
2888}
2889
2890fn recurse_words_mut(words: &mut [Sp<Word>], f: &mut dyn FnMut(&mut Sp<Word>)) {
2891 recurse_words_mut_impl(words, &|_| true, f);
2892}
2893
2894fn recurse_words_mut_impl(
2895 words: &mut [Sp<Word>],
2896 recurse_word: &dyn Fn(&Sp<Word>) -> bool,
2897 f: &mut dyn FnMut(&mut Sp<Word>),
2898) {
2899 for word in words {
2900 if !recurse_word(word) {
2901 continue;
2902 }
2903 match &mut word.value {
2904 Word::Strand(items) => recurse_words_mut(items, f),
2905 Word::Array(arr) => arr.word_lines_mut().for_each(|line| {
2906 recurse_words_mut(line, f);
2907 }),
2908 Word::Func(func) => func.word_lines_mut().for_each(|line| {
2909 recurse_words_mut(line, f);
2910 }),
2911 Word::Modified(m) => recurse_words_mut(&mut m.operands, f),
2912 Word::Pack(pack) => pack.branches.iter_mut().for_each(|branch| {
2913 (branch.value.word_lines_mut()).for_each(|line| recurse_words_mut(line, f))
2914 }),
2915 Word::Subscripted(sub) => recurse_words_mut(slice::from_mut(&mut sub.word), f),
2916 _ => {}
2917 }
2918 f(word);
2919 }
2920}
2921
2922fn line_sig(line: &[Sp<Word>]) -> Option<Sp<DocCommentSig>> {
2923 line.split_last()
2924 .and_then(|(last, mut rest)| match &last.value {
2925 Word::Comment(c) => c.parse::<DocCommentSig>().ok().map(|sig| {
2926 while rest.last().is_some_and(|w| matches!(w.value, Word::Spaces)) {
2927 rest = &rest[..rest.len() - 1];
2928 }
2929 let span = (rest.first())
2930 .zip(rest.last())
2931 .map(|(f, l)| f.span.clone().merge(l.span.clone()))
2932 .unwrap_or_else(|| last.span.clone());
2933 span.sp(sig)
2934 }),
2935 _ => None,
2936 })
2937}
2938
2939#[cfg(not(target_arch = "wasm32"))]
2941pub trait SendSyncNative: Send + Sync {}
2942#[cfg(not(target_arch = "wasm32"))]
2943impl<T: Send + Sync> SendSyncNative for T {}
2944
2945#[cfg(target_arch = "wasm32")]
2947pub trait SendSyncNative {}
2948#[cfg(target_arch = "wasm32")]
2949impl<T> SendSyncNative for T {}