uiua/compile/
mod.rs

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/// The Uiua compiler
49#[derive(Clone)]
50pub struct Compiler {
51    pub(crate) asm: Assembly,
52    pub(crate) code_meta: CodeMeta,
53    /// The name of the current bindings
54    current_bindings: Vec<CurrentBinding>,
55    /// The index of the next global binding
56    next_global: usize,
57    /// The current scope
58    pub(crate) scope: Scope,
59    /// Ancestor scopes of the current one
60    higher_scopes: Vec<Scope>,
61    /// Determines which How test scopes are run
62    mode: RunMode,
63    /// The paths of files currently being imported (used to detect import cycles)
64    current_imports: Vec<PathBuf>,
65    /// The bindings of imported files
66    imports: HashMap<PathBuf, Module>,
67    /// Unexpanded index macros
68    index_macros: HashMap<usize, IndexMacro>,
69    /// Unexpanded code macros
70    code_macros: HashMap<usize, CodeMacro>,
71    /// Indices of named external functions
72    externals: HashMap<Ident, usize>,
73    /// The depth of compile-time evaluation
74    comptime_depth: usize,
75    /// Whether the compiler is in a try
76    in_try: bool,
77    /// Accumulated errors
78    errors: Vec<UiuaError>,
79    /// Primitives that have emitted errors because they are deprecated
80    deprecated_prim_errors: HashSet<Primitive>,
81    /// Constants that have emitted errors because they are deprecated
82    deprecated_const_errors: HashSet<&'static str>,
83    /// Accumulated diagnostics
84    diagnostics: BTreeSet<Diagnostic>,
85    /// Print diagnostics as they are encountered
86    pub(crate) print_diagnostics: bool,
87    /// Whether to evaluate comptime code
88    comptime: bool,
89    /// The comptime mode
90    pre_eval_mode: PreEvalMode,
91    /// The interpreter used for comptime code
92    macro_env: Uiua,
93    /// Start addresses
94    start_addrs: Vec<usize>,
95    /// Primitive optional arg getter bindings
96    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)]
141/// Names in a scope
142pub 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/// A Uiua module
203#[derive(Debug, Clone, Default, Serialize, Deserialize)]
204pub struct Module {
205    /// The top level comment
206    pub comment: Option<EcoString>,
207    /// Map module-local names to global indices
208    pub names: LocalNames,
209    /// Whether the mode is a data function
210    data_func: bool,
211    /// Whether the module uses experimental features
212    experimental: bool,
213}
214
215/// An index macro
216#[derive(Clone)]
217struct IndexMacro {
218    words: Vec<Sp<Word>>,
219    /// Map of spans of identifiers used in the macro that were in scope
220    /// when the macro was declared to their local indices. This is used
221    /// for name resolution. It is keyed by span rather than by name so
222    /// that names in both the declaration and invocation's scope can
223    /// be disambiguated.
224    locals: HashMap<CodeSpan, usize>,
225    sig: Option<Signature>,
226    recursive: bool,
227}
228
229/// A code macro
230#[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/// A scope where names are defined
257#[derive(Debug, Clone)]
258pub(crate) struct Scope {
259    kind: ScopeKind,
260    /// The name of the current file, if any
261    file_path: Option<PathBuf>,
262    /// The top level comment
263    comment: Option<EcoString>,
264    /// Map local names to global indices
265    names: LocalNames,
266    /// Whether the scope has a data def defined
267    has_data_def: bool,
268    /// Whether the scope's data def is a data function
269    is_data_func: bool,
270    /// Names of data variants
271    data_variants: IndexSet<EcoString>,
272    /// Whether to allow experimental features
273    pub experimental: bool,
274    /// Whether an error has been emitted for experimental features
275    experimental_error: bool,
276}
277
278#[derive(Debug, Clone, PartialEq, Eq)]
279enum ScopeKind {
280    /// A scope at the top level of a file
281    File(FileScopeKind),
282    /// A scope in a named module
283    Module(Ident),
284    /// A scope that includes all bindings in a module
285    AllInModule,
286    /// A temporary scope, probably for a macro
287    Macro(Option<MacroLocal>),
288    /// A binding scope
289    Binding,
290    /// A function scope between some delimiters
291    Function,
292    /// A test scope between `---`s
293    Test,
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq)]
297enum FileScopeKind {
298    Source,
299    Git,
300}
301
302/// Indices of an index macro's locals
303#[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/// The index of a named local in the bindings, and whether it is public
333#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
334pub struct LocalIndex {
335    /// The index of the binding in assembly's bindings
336    pub index: usize,
337    /// Whether the binding is public
338    pub public: bool,
339}
340
341impl Compiler {
342    /// Create a new compiler
343    pub fn new() -> Self {
344        Self::default()
345    }
346    /// Create a new compiler with a custom backend for `comptime` code
347    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    /// Set the compiler's assembly
354    pub fn with_assembly(self, asm: Assembly) -> Self {
355        Self { asm, ..self }
356    }
357    /// Get a reference to the assembly
358    pub fn assembly(&self) -> &Assembly {
359        &self.asm
360    }
361    /// Get a mutable reference to the assembly
362    pub fn assembly_mut(&mut self) -> &mut Assembly {
363        &mut self.asm
364    }
365    /// Get a reference to the code metadata
366    pub fn code_meta(&self) -> &CodeMeta {
367        &self.code_meta
368    }
369    /// Get a mutable reference to the code metadata
370    pub fn code_meta_mut(&mut self) -> &mut CodeMeta {
371        &mut self.code_meta
372    }
373    /// Take a completed assembly from the compiler
374    pub fn finish(&mut self) -> Assembly {
375        take(&mut self.asm)
376    }
377    /// Set whether to evaluate `comptime`
378    pub fn comptime(&mut self, comptime: bool) -> &mut Self {
379        self.comptime = comptime;
380        self
381    }
382    /// Set the [`PreEvalMode`]
383    pub fn pre_eval_mode(&mut self, mode: PreEvalMode) -> &mut Self {
384        self.pre_eval_mode = mode;
385        self
386    }
387    /// Set whether to print diagnostics as they are encountered
388    ///
389    /// If this is set to false, diagnostics will be accumulated and can be retrieved with [`Compiler::take_diagnostics`]
390    ///
391    /// Defaults to false
392    pub fn print_diagnostics(&mut self, print_diagnostics: bool) -> &mut Self {
393        self.print_diagnostics = print_diagnostics;
394        self
395    }
396    /// Set the run mode
397    pub fn mode(&mut self, mode: RunMode) -> &mut Self {
398        self.mode = mode;
399        self
400    }
401    /// Enable experimental features
402    pub fn experimental(&mut self, experimental: bool) -> &mut Self {
403        self.scope.experimental = experimental;
404        self
405    }
406    /// Get the backend
407    pub fn backend(&self) -> Arc<dyn SysBackend> {
408        self.macro_env.rt.backend.clone()
409    }
410    /// Attempt to downcast the system backend to a concrete reference type
411    pub fn downcast_backend<T: SysBackend>(&self) -> Option<&T> {
412        self.macro_env.downcast_backend()
413    }
414    /// Attempt to downcast the system backend to a concrete mutable type
415    pub fn downcast_backend_mut<T: SysBackend>(&mut self) -> Option<&mut T> {
416        self.macro_env.downcast_backend_mut()
417    }
418    /// Take the system backend
419    pub fn take_backend<T: SysBackend + Default>(&mut self) -> Option<T> {
420        self.macro_env.take_backend()
421    }
422    /// Set the system backend
423    pub fn set_backend<T: SysBackend>(&mut self, backend: T) {
424        self.macro_env.rt.backend = Arc::new(backend);
425    }
426    /// Compile a Uiua file from a file at a path
427    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        // _ = crate::lsp::Spans::from_input(&input);
433        self.asm.inputs.files.insert(path.into(), input.clone());
434        self.load_impl(&input, InputSrc::File(path.into()))
435    }
436    /// Compile a Uiua file from a string
437    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    /// Compile a Uiua file from a string with a path for error reporting
442    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    /// Run in a scoped context. Names defined in this context will be removed when the scope ends.
473    ///
474    /// While names defined in this context will be removed when the scope ends, values *bound* to
475    /// those names will not.
476    ///
477    /// All other runtime state other than the stack, will also be restored.
478    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        // Optimize root
526        // We only optimize if this is not an import
527        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            // Optimize and pre-eval functions
534            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        // dbg!(&self.asm.root);
544
545        // Print diagnostics
546        if self.print_diagnostics {
547            for diagnostic in self.take_diagnostics() {
548                eprintln!("{}", diagnostic.report());
549            }
550        }
551
552        // Update top-level bindings
553        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        // Collect errors
561        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        // Set scope comment
608        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        // Populate prelude
696        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        // Compile the words
749        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            // Validate callability
757            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            // Track stack height
774            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                // Compile test assert
788                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                // Try to evaluate at comptime
809                // This can be done when:
810                // - the line is not in a () function
811                // - the pre-eval mode is greater that `Line`
812                // - there are at least as many push nodes preceding the current line as there are arguments to the line
813                // - the words create no bindings
814                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                    // The nodes for evaluation are the preceding
825                    // push nodes, followed by the current line
826                    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                        // Track top-level values
831                        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                        // Truncate root
844                        self.asm.root.truncate(self.asm.root.len() - sig.args());
845                        // Set line node to the pre-evaluated node
846                        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    /// Import a module
887    pub(crate) fn import_module(&mut self, path_str: &str, span: &CodeSpan) -> UiuaResult<PathBuf> {
888        // Resolve path
889        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            // Git import
910            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            // Normal import
927            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            // We cache Git modules on WASM so that the pad doesn't have to recompile big modules constantly
935            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    /// Resolve a declared import path relative to the path of the file that is being executed
996    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    // Compile a line, checking an end-of-line signature comment
1014    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        // Actually compile the line
1026        let mut node = self.words(line)?;
1027        node.extend(output_comment);
1028        // Validate line signature
1029        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        // Validate comment signature
1043        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            // Add argument labels
1059            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            // Add output labels
1069            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        // Filter out non-code words
1102        words.retain(|word| word.value.is_code());
1103        // Extract semantic comment
1104        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        // Right-to-left
1112        words.reverse();
1113
1114        // We keep track of some data from previous words so we can emit
1115        // diagnostics about suggested changes
1116        #[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                // Flip monadic dup diagnostic
1146                (
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                // Keep unique dup diagnostic
1163                (
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                // First reverse diagnostic
1181                (
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                // Select by rise and select rise dup diagnostics
1198                (
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            // Compile the word
1236            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                // Track span for LSP
1362                let just_spans: Vec<_> = items.iter().map(|w| w.span.clone()).collect();
1363                // Compile individual items
1364                let op_nodes = items
1365                    .into_iter()
1366                    .rev()
1367                    .map(|word| self.word_sig(word))
1368                    .collect::<UiuaResult<Vec<_>>>()?;
1369                // Check item sigs
1370                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                // Flatten instrs
1378                let inner = Node::from_iter(op_nodes.into_iter().map(|sn| sn.node));
1379
1380                // Normal strand
1381
1382                // Diagnostic for strand of characters
1383                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                // Inline constant arrays
1397                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                // Track span for LSP
1430                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                // Compile lines
1446                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                // Calculate length
1460                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                // Diagnostic for array of characters
1479                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                // Inline constant arrays
1494                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                // Normal case
1529                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                // We could error here, but it's easier to handle it higher up
1554                Node::empty()
1555            }
1556            Word::SemanticComment(_) => {
1557                // Semantic comments are handled higher up
1558                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    /// Find the [`LocalName`]s of both the name and all parts of the path of a [`Ref`]
1689    ///
1690    /// Returns [`None`] if the reference is to a constant
1691    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        // println!("name: {name:?} @ {}", span);
1742        // for scope in self.scopes() {
1743        //     println!("  {:?} {:?}", scope.kind, scope.names);
1744        // }
1745        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                // Look in the scope's names
1761                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                // Look in the macro's locals. We look up by span rather than
1770                // name to disambiguate the macro declaration's locals from
1771                // the current ones.
1772                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        // Attempt to look up the identifier as a non-macro
1784        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            // Only allow it if it is a module
1790            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        // Add completions
1941        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            // Name exists in binding scope
1948            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            // Name is a recursive call
1955            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            // Name exists in scope
1965            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            // Name is a built-in constant
1970            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                // Handle called modules
2033                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                // We could error here, but it's easier to handle it higher up
2055                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        // Validate signature
2068        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        // Compile branches
2094        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            // Compile remaining branches
2132            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                            // Ensure that common cases are exact
2333                            -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    /// Get all diagnostics
2642    pub fn diagnostics(&self) -> &BTreeSet<Diagnostic> {
2643        &self.diagnostics
2644    }
2645    /// Get all diagnostics mutably
2646    pub fn diagnostics_mut(&mut self) -> &mut BTreeSet<Diagnostic> {
2647        &mut self.diagnostics
2648    }
2649    /// Take all diagnostics
2650    ///
2651    /// These are only available if `print_diagnostics` is `false`
2652    pub fn take_diagnostics(&mut self) -> BTreeSet<Diagnostic> {
2653        take(&mut self.diagnostics)
2654    }
2655    /// Construct and add a diagnostic with a custom span
2656    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        // Emit deprecation warning
2738        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        // Validate public
2750        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    /// Get a span by its index
2766    pub fn get_span(&self, span: usize) -> Span {
2767        self.asm.spans[span].clone()
2768    }
2769    /// Register a span
2770    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    /// Create a function
2780    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    /// Bind a function in the current scope
2800    ///
2801    /// # Errors
2802    /// Returns an error if the binding name is not valid
2803    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    /// Create and bind a function in the current scope
2822    ///
2823    /// This function is the only way to bind `# External!` functions.
2824    ///
2825    /// # Errors
2826    /// Returns an error if the binding name is not valid
2827    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/// Supertrait for `Send` and `Sync` on native targets, but not on wasm32
2940#[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/// Supertrait for `Send` and `Sync` on native targets, but not on wasm32
2946#[cfg(target_arch = "wasm32")]
2947pub trait SendSyncNative {}
2948#[cfg(target_arch = "wasm32")]
2949impl<T> SendSyncNative for T {}