Skip to main content

endbasic_core/
callable.rs

1// EndBASIC
2// Copyright 2021 Julio Merino
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Affero General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Symbol definitions and symbols table representation.
18
19use crate::ast::ArgSep;
20use crate::ast::ExprType;
21use crate::bytecode::TaggedRegisterRef;
22use crate::bytecode::VarArgTag;
23use crate::mem::HeapOverflowError;
24use crate::mem::{ConstantDatum, DatumPtr, Heap, HeapDatum};
25use crate::reader::LineCol;
26use async_trait::async_trait;
27use std::borrow::Cow;
28use std::fmt;
29use std::io;
30use std::ops::RangeInclusive;
31use std::rc::Rc;
32use std::str::Lines;
33
34/// Error types for callable execution.
35#[derive(Debug, thiserror::Error)]
36pub enum CallError {
37    /// Invalid callable argument.
38    #[error("{0}")]
39    Argument(String),
40
41    /// Runtime evaluation error.
42    #[error("{0}")]
43    Eval(String),
44
45    /// I/O error.
46    #[error("{0}")]
47    IoError(#[from] io::Error),
48
49    /// Callable precondition failure.
50    #[error("{0}")]
51    Precondition(String),
52
53    /// Indicates a syntax error only detectable at runtime.
54    #[error("{0}: {1}")]
55    Syntax(LineCol, String),
56}
57
58impl From<HeapOverflowError> for CallError {
59    fn from(value: HeapOverflowError) -> Self {
60        Self::Eval(value.to_string())
61    }
62}
63
64impl CallError {
65    /// Converts this call error to an upcall error with a mandatory position.
66    ///
67    /// If the error does not carry an origin position on its own, uses
68    /// `default_pos`.
69    pub(crate) fn to_upcall_error(&self, default_pos: LineCol) -> UpcallError {
70        match self {
71            CallError::Argument(message) => UpcallError::Argument(default_pos, message.clone()),
72
73            CallError::Eval(message) => UpcallError::Eval(default_pos, message.clone()),
74
75            CallError::IoError(e) => UpcallError::IoError(default_pos, e.to_string()),
76
77            CallError::Precondition(message) => {
78                UpcallError::Precondition(default_pos, message.clone())
79            }
80
81            CallError::Syntax(pos, message) => UpcallError::Syntax(*pos, message.clone()),
82        }
83    }
84}
85
86/// Result type for callable execution.
87pub type CallResult<T> = Result<T, CallError>;
88
89/// Error type for uncaught upcall failures.
90///
91/// This should be the same as `CallError` but with all error variants annotated with a position.
92#[derive(Debug, thiserror::Error)]
93pub enum UpcallError {
94    /// Invalid callable argument at a given source location.
95    #[error("{0}: {1}")]
96    Argument(LineCol, String),
97
98    /// Runtime evaluation error at a given source location.
99    #[error("{0}: {1}")]
100    Eval(LineCol, String),
101
102    /// I/O error at a given source location.
103    #[error("{0}: {1}")]
104    IoError(LineCol, String),
105
106    /// Callable precondition failure at a given source location.
107    #[error("{0}: {1}")]
108    Precondition(LineCol, String),
109
110    /// Runtime syntax error at a specific source location.
111    #[error("{0}: {1}")]
112    Syntax(LineCol, String),
113}
114
115impl UpcallError {
116    /// Returns the source position and message of this error.
117    pub fn parts(&self) -> (LineCol, String) {
118        match self {
119            UpcallError::Argument(pos, message) => (*pos, message.clone()),
120            UpcallError::Eval(pos, message) => (*pos, message.clone()),
121            UpcallError::IoError(pos, message) => (*pos, message.clone()),
122            UpcallError::Precondition(pos, message) => (*pos, message.clone()),
123            UpcallError::Syntax(pos, message) => (*pos, message.clone()),
124        }
125    }
126}
127
128/// Syntax specification for a required scalar parameter.
129#[derive(Clone, Debug, PartialEq)]
130pub struct RequiredValueSyntax {
131    /// The name of the parameter for help purposes.
132    pub name: Cow<'static, str>,
133
134    /// The type of the expected parameter.
135    pub vtype: ExprType,
136}
137
138/// Syntax specification for a required reference parameter.
139#[derive(Clone, Debug, PartialEq)]
140pub struct RequiredRefSyntax {
141    /// The name of the parameter for help purposes.
142    pub name: Cow<'static, str>,
143
144    /// If true, require an array reference; if false, a variable reference.
145    pub require_array: bool,
146
147    /// If true, allow references to undefined variables because the command will define them when
148    /// missing.  Can only be set to true for commands, not functions, and `require_array` must be
149    /// false.
150    pub define_undefined: bool,
151}
152
153/// Syntax specification for an optional scalar parameter.
154///
155/// Optional parameters are only supported in commands.
156#[derive(Clone, Debug, PartialEq)]
157pub struct OptionalValueSyntax {
158    /// The name of the parameter for help purposes.
159    pub name: Cow<'static, str>,
160
161    /// The type of the expected parameter.
162    pub vtype: ExprType,
163}
164
165/// Specifies the type constraints for a repeated parameter.
166#[derive(Clone, Debug, PartialEq)]
167pub enum RepeatedTypeSyntax {
168    /// Allows any value type, including empty arguments.  The values pushed onto the stack have
169    /// the same semantics as those pushed by `AnyValueSyntax`.
170    AnyValue,
171
172    /// Expects a value of the given type.
173    TypedValue(ExprType),
174
175    /// Expects a reference to a variable (not an array) and allows the variables to not be defined.
176    VariableRef,
177}
178
179/// Syntax specification for a repeated parameter.
180///
181/// The repeated parameter must appear after all singular positional parameters.
182#[derive(Clone, Debug, PartialEq)]
183pub struct RepeatedSyntax {
184    /// The name of the parameter for help purposes.
185    pub name: Cow<'static, str>,
186
187    /// The type of the expected parameters.
188    pub type_syn: RepeatedTypeSyntax,
189
190    /// The separator to expect between the repeated parameters.  For functions, this must be the
191    /// long separator (the comma).
192    pub sep: ArgSepSyntax,
193
194    /// Whether the repeated parameter must at least have one element or not.
195    pub require_one: bool,
196
197    /// Whether to allow any parameter to not be present or not.  Can only be true for commands.
198    pub allow_missing: bool,
199}
200
201impl RepeatedSyntax {
202    /// Formats the repeated argument syntax for help purposes into `output`.
203    ///
204    /// `last_singular_sep` contains the separator of the last singular argument syntax, if any,
205    /// which we need to place inside of the optional group.
206    fn describe(&self, output: &mut String, last_singular_sep: Option<&ArgSepSyntax>) {
207        if !self.require_one {
208            output.push('[');
209        }
210
211        if let Some(sep) = last_singular_sep {
212            sep.describe(output);
213        }
214
215        output.push_str(&self.name);
216        output.push('1');
217        if let RepeatedTypeSyntax::TypedValue(vtype) = self.type_syn {
218            output.push(vtype.annotation());
219        }
220
221        if self.require_one {
222            output.push('[');
223        }
224
225        self.sep.describe(output);
226        output.push_str("..");
227        self.sep.describe(output);
228
229        output.push_str(&self.name);
230        output.push('N');
231        if let RepeatedTypeSyntax::TypedValue(vtype) = self.type_syn {
232            output.push(vtype.annotation());
233        }
234
235        output.push(']');
236    }
237}
238
239/// Syntax specification for a parameter that accepts any scalar type.
240#[derive(Clone, Debug, PartialEq)]
241pub struct AnyValueSyntax {
242    /// The name of the parameter for help purposes.
243    pub name: Cow<'static, str>,
244
245    /// Whether to allow the parameter to not be present or not.  Can only be true for commands.
246    pub allow_missing: bool,
247}
248
249/// Specifies the expected argument separator in a callable's syntax.
250#[derive(Copy, Clone, Debug, PartialEq)]
251pub enum ArgSepSyntax {
252    /// The argument separator must exactly be the one given.
253    Exactly(ArgSep),
254
255    /// The argument separator may be any of the ones given.
256    OneOf(&'static [ArgSep]),
257
258    /// The argument separator is the end of the call.
259    End,
260}
261
262impl ArgSepSyntax {
263    /// Formats the argument separator for help purposes into `output`.
264    fn describe(&self, output: &mut String) {
265        match self {
266            ArgSepSyntax::Exactly(sep) => {
267                let (text, needs_space) = sep.describe();
268
269                if !text.is_empty() && needs_space {
270                    output.push(' ');
271                }
272                output.push_str(text);
273                if !text.is_empty() {
274                    output.push(' ');
275                }
276            }
277
278            ArgSepSyntax::OneOf(seps) => {
279                output.push_str(" <");
280                for (i, sep) in seps.iter().enumerate() {
281                    let (text, _needs_space) = sep.describe();
282                    output.push_str(text);
283                    if i < seps.len() - 1 {
284                        output.push('|');
285                    }
286                }
287                output.push_str("> ");
288            }
289
290            ArgSepSyntax::End => (),
291        };
292    }
293}
294
295/// Syntax specification for a non-repeated argument.
296///
297/// Every item in this enum is composed of a struct that provides the details on the parameter and
298/// a struct that provides the details on how this parameter is separated from the next.
299#[derive(Clone, Debug, PartialEq)]
300pub enum SingularArgSyntax {
301    /// A required scalar value with the syntax details and the separator that follows.
302    RequiredValue(RequiredValueSyntax, ArgSepSyntax),
303
304    /// A required reference with the syntax details and the separator that follows.
305    RequiredRef(RequiredRefSyntax, ArgSepSyntax),
306
307    /// An optional scalar value with the syntax details and the separator that follows.
308    OptionalValue(OptionalValueSyntax, ArgSepSyntax),
309
310    /// A required scalar value of any type with the syntax details and the separator that follows.
311    AnyValue(AnyValueSyntax, ArgSepSyntax),
312}
313
314/// Complete syntax specification for a callable's arguments.
315///
316/// Note that the description of function arguments is more restricted than that of commands.
317/// The arguments compiler panics when these preconditions aren't met with the rationale that
318/// builtin functions must never be ill-defined.
319// TODO(jmmv): It might be nice to try to express these restrictions in the type system, but
320// things are already too verbose as they are...
321#[derive(Clone, Debug, PartialEq)]
322pub(crate) struct CallableSyntax {
323    /// Ordered list of singular arguments that appear before repeated arguments.
324    pub(crate) singular: Cow<'static, [SingularArgSyntax]>,
325
326    /// Details on the repeated argument allowed after singular arguments, if any.
327    pub(crate) repeated: Option<Cow<'static, RepeatedSyntax>>,
328}
329
330impl CallableSyntax {
331    /// Creates a new callable arguments definition from its parts defined statically in the
332    /// code.
333    pub(crate) fn new_static(
334        singular: &'static [SingularArgSyntax],
335        repeated: Option<&'static RepeatedSyntax>,
336    ) -> Self {
337        Self { singular: Cow::Borrowed(singular), repeated: repeated.map(Cow::Borrowed) }
338    }
339
340    /// Creates a new callable arguments definition from its parts defined dynamically at
341    /// runtime.
342    pub(crate) fn new_dynamic(
343        singular: Vec<SingularArgSyntax>,
344        repeated: Option<RepeatedSyntax>,
345    ) -> Self {
346        Self { singular: Cow::Owned(singular), repeated: repeated.map(Cow::Owned) }
347    }
348
349    /// Computes the range of the expected number of parameters for this syntax.
350    pub(crate) fn expected_nargs(&self) -> RangeInclusive<usize> {
351        let mut min = self.singular.len();
352        let mut max = self.singular.len();
353
354        if let Some(syn) = self.repeated.as_ref() {
355            if syn.require_one {
356                min += 1;
357            }
358            max = usize::MAX;
359        }
360
361        min..=max
362    }
363
364    /// Returns true if this syntax represents "no arguments".
365    pub(crate) fn is_empty(&self) -> bool {
366        self.singular.is_empty() && self.repeated.is_none()
367    }
368
369    /// Produces a user-friendly description of this callable syntax.
370    pub(crate) fn describe(&self) -> String {
371        let mut description = String::new();
372        let mut last_singular_sep = None;
373        for (i, s) in self.singular.iter().enumerate() {
374            let sep = match s {
375                SingularArgSyntax::RequiredValue(details, sep) => {
376                    description.push_str(&details.name);
377                    description.push(details.vtype.annotation());
378                    sep
379                }
380
381                SingularArgSyntax::RequiredRef(details, sep) => {
382                    description.push_str(&details.name);
383                    sep
384                }
385
386                SingularArgSyntax::OptionalValue(details, sep) => {
387                    description.push('[');
388                    description.push_str(&details.name);
389                    description.push(details.vtype.annotation());
390                    description.push(']');
391                    sep
392                }
393
394                SingularArgSyntax::AnyValue(details, sep) => {
395                    if details.allow_missing {
396                        description.push('[');
397                    }
398                    description.push_str(&details.name);
399                    if details.allow_missing {
400                        description.push(']');
401                    }
402                    sep
403                }
404            };
405
406            if self.repeated.is_none() || i < self.singular.len() - 1 {
407                sep.describe(&mut description);
408            }
409            if i == self.singular.len() - 1 {
410                last_singular_sep = Some(sep);
411            }
412        }
413
414        if let Some(syn) = &self.repeated {
415            syn.describe(&mut description, last_singular_sep);
416        }
417
418        description
419    }
420}
421
422/// Builder pattern for constructing a callable's metadata.
423pub struct CallableMetadataBuilder {
424    /// Name of the callable, stored in uppercase.
425    name: Cow<'static, str>,
426
427    /// Return type of the callable, or `None` for commands/subroutines.
428    return_type: Option<ExprType>,
429
430    /// Whether this callable requires asynchronous dispatch.
431    is_async: bool,
432
433    /// Category for grouping related callables in help messages.
434    category: Option<&'static str>,
435
436    /// Syntax specifications for the callable's arguments.
437    syntaxes: Vec<CallableSyntax>,
438
439    /// Description of the callable for documentation purposes.
440    description: Option<&'static str>,
441}
442
443impl CallableMetadataBuilder {
444    /// Constructs a new metadata builder with the minimum information necessary.
445    ///
446    /// All code except tests must populate the whole builder with details.  This is enforced at
447    /// construction time, where we only allow some fields to be missing under the test
448    /// configuration.
449    pub fn new(name: &'static str) -> Self {
450        assert!(name == name.to_ascii_uppercase(), "Callable name must be in uppercase");
451
452        Self {
453            name: Cow::Borrowed(name),
454            return_type: None,
455            is_async: false,
456            syntaxes: vec![],
457            category: None,
458            description: None,
459        }
460    }
461
462    /// Constructs a new metadata builder with the minimum information necessary.
463    ///
464    /// This is the same as `new` but using a dynamically-allocated name, which is necessary for
465    /// user-defined symbols.
466    pub fn new_dynamic<S: Into<String>>(name: S) -> Self {
467        Self {
468            name: Cow::Owned(name.into().to_ascii_uppercase()),
469            return_type: None,
470            is_async: false,
471            syntaxes: vec![],
472            category: Some("User defined"),
473            description: Some("User defined symbol."),
474        }
475    }
476
477    /// Sets the return type of the callable.
478    pub fn with_return_type(mut self, return_type: ExprType) -> Self {
479        self.return_type = Some(return_type);
480        self
481    }
482
483    /// Sets whether this callable requires asynchronous dispatch.
484    pub fn with_async(mut self, is_async: bool) -> Self {
485        self.is_async = is_async;
486        self
487    }
488
489    /// Sets the syntax specifications for this callable.
490    pub fn with_syntax(
491        mut self,
492        syntaxes: &'static [(&'static [SingularArgSyntax], Option<&'static RepeatedSyntax>)],
493    ) -> Self {
494        self.syntaxes = syntaxes
495            .iter()
496            .map(|s| CallableSyntax::new_static(s.0, s.1))
497            .collect::<Vec<CallableSyntax>>();
498        self
499    }
500
501    /// Sets the syntax specifications for this callable.
502    pub(crate) fn with_syntaxes<S: Into<Vec<CallableSyntax>>>(mut self, syntaxes: S) -> Self {
503        self.syntaxes = syntaxes.into();
504        self
505    }
506
507    /// Sets the syntax specifications for this callable.
508    pub(crate) fn with_dynamic_syntax(
509        self,
510        syntaxes: Vec<(Vec<SingularArgSyntax>, Option<RepeatedSyntax>)>,
511    ) -> Self {
512        let syntaxes = syntaxes
513            .into_iter()
514            .map(|s| CallableSyntax::new_dynamic(s.0, s.1))
515            .collect::<Vec<CallableSyntax>>();
516        self.with_syntaxes(syntaxes)
517    }
518
519    /// Sets the category for this callable.  All callables with the same category name will be
520    /// grouped together in help messages.
521    pub fn with_category(mut self, category: &'static str) -> Self {
522        self.category = Some(category);
523        self
524    }
525
526    /// Sets the description for this callable.  The `description` is a collection of paragraphs
527    /// separated by a single newline character, where the first paragraph is taken as the summary
528    /// of the description.  The summary must be a short sentence that is descriptive enough to be
529    /// understood without further details.  Empty lines (paragraphs) are not allowed.
530    pub fn with_description(mut self, description: &'static str) -> Self {
531        for l in description.lines() {
532            assert!(!l.is_empty(), "Description cannot contain empty lines");
533        }
534        self.description = Some(description);
535        self
536    }
537
538    /// Generates the final `CallableMetadata` object, ensuring all values are present.
539    pub fn build(self) -> Rc<CallableMetadata> {
540        assert!(!self.syntaxes.is_empty(), "All callables must specify a syntax");
541        Rc::from(CallableMetadata {
542            name: self.name,
543            return_type: self.return_type,
544            is_async: self.is_async,
545            syntaxes: self.syntaxes,
546            category: self.category.expect("All callables must specify a category"),
547            description: self.description.expect("All callables must specify a description"),
548        })
549    }
550
551    /// Generates the final `CallableMetadata` object, ensuring the minimal set of values are
552    /// present.  Only useful for testing.
553    pub fn test_build(mut self) -> Rc<CallableMetadata> {
554        if self.syntaxes.is_empty() {
555            self.syntaxes.push(CallableSyntax::new_static(&[], None));
556        }
557        Rc::from(CallableMetadata {
558            name: self.name,
559            return_type: self.return_type,
560            is_async: self.is_async,
561            syntaxes: self.syntaxes,
562            category: self.category.unwrap_or(""),
563            description: self.description.unwrap_or(""),
564        })
565    }
566}
567
568/// Representation of a callable's metadata.
569///
570/// The callable is expected to hold onto an instance of this object within its struct to make
571/// queries fast.
572#[derive(Clone, Debug, PartialEq)]
573pub struct CallableMetadata {
574    /// Name of the callable, stored in uppercase.
575    name: Cow<'static, str>,
576
577    /// Return type of the callable, or `None` for commands/subroutines.
578    return_type: Option<ExprType>,
579
580    /// Whether this callable requires asynchronous dispatch.
581    is_async: bool,
582
583    /// Syntax specifications for the callable's arguments.
584    syntaxes: Vec<CallableSyntax>,
585
586    /// Category for grouping related callables in help messages.
587    category: &'static str,
588
589    /// Description of the callable for documentation purposes.
590    description: &'static str,
591}
592
593impl CallableMetadata {
594    /// Gets the callable's name, all in uppercase.
595    pub fn name(&self) -> &str {
596        &self.name
597    }
598
599    /// Gets the callable's return type.
600    pub fn return_type(&self) -> Option<ExprType> {
601        self.return_type
602    }
603
604    /// Gets whether this callable requires asynchronous dispatch.
605    pub fn is_async(&self) -> bool {
606        self.is_async
607    }
608
609    /// Gets the callable's syntax specification.
610    pub fn syntax(&self) -> String {
611        fn format_one(cs: &CallableSyntax) -> String {
612            let mut syntax = cs.describe();
613            if syntax.is_empty() {
614                syntax.push_str("no arguments");
615            }
616            syntax
617        }
618
619        match self.syntaxes.as_slice() {
620            [] => panic!("Callables without syntaxes are not allowed at construction time"),
621            [one] => format_one(one),
622            many => many
623                .iter()
624                .map(|syn| format!("<{}>", syn.describe()))
625                .collect::<Vec<String>>()
626                .join(" | "),
627        }
628    }
629
630    /// Returns true if `sep` is valid for a function call (only `Long` and `End` are allowed because
631    /// the parser only produces comma separators for function arguments).
632    fn is_function_sep(sep: &ArgSepSyntax) -> bool {
633        match sep {
634            ArgSepSyntax::Exactly(ArgSep::Long) | ArgSepSyntax::End => true,
635            ArgSepSyntax::OneOf(seps) => seps.iter().all(|s| *s == ArgSep::Long),
636            _ => false,
637        }
638    }
639
640    /// Checks that the syntax of a callable that returns a value only uses separators that can appear
641    /// in a function call (i.e. the comma separator).  The parser only produces `ArgSep::Long` for
642    /// function arguments, so any other separator in the metadata would be dead/untestable.
643    fn debug_assert_function_seps(&self, syntax: &CallableSyntax) {
644        if self.return_type().is_none() {
645            return;
646        }
647        for syn in syntax.singular.iter() {
648            let sep = match syn {
649                SingularArgSyntax::RequiredValue(_, sep) => sep,
650                SingularArgSyntax::RequiredRef(_, sep) => sep,
651                SingularArgSyntax::OptionalValue(_, sep) => sep,
652                SingularArgSyntax::AnyValue(_, sep) => sep,
653            };
654            debug_assert!(
655                Self::is_function_sep(sep),
656                "Function {} has a non-comma separator in its singular args syntax",
657                self.name()
658            );
659        }
660        if let Some(repeated) = syntax.repeated.as_ref() {
661            debug_assert!(
662                Self::is_function_sep(&repeated.sep),
663                "Function {} has a non-comma separator in its repeated args syntax",
664                self.name()
665            );
666        }
667    }
668
669    /// Finds the syntax definition that matches the given argument count.
670    ///
671    /// Returns an error if no syntax matches, and panics if multiple syntaxes match (which would
672    /// indicate an ambiguous callable definition).
673    pub(crate) fn find_syntax(&self, nargs: usize) -> Option<&CallableSyntax> {
674        let mut matches = self.syntaxes.iter().filter(|s| s.expected_nargs().contains(&nargs));
675        let syntax = matches.next();
676        match syntax {
677            Some(syntax) => {
678                debug_assert!(matches.next().is_none(), "Ambiguous syntax definitions");
679                if cfg!(debug_assertions) {
680                    self.debug_assert_function_seps(syntax);
681                }
682                Some(syntax)
683            }
684            None => None,
685        }
686    }
687
688    /// Gets the callable's category as a collection of lines.  The first line is the title of the
689    /// category, and any extra lines are additional information for it.
690    #[allow(unused)]
691    pub fn category(&self) -> &'static str {
692        self.category
693    }
694
695    /// Gets the callable's textual description as a collection of lines.  The first line is the
696    /// summary of the callable's purpose.
697    #[allow(unused)]
698    pub fn description(&self) -> Lines<'static> {
699        self.description.lines()
700    }
701
702    /// Returns true if this is a callable that takes no arguments.
703    #[allow(unused)]
704    pub fn is_argless(&self) -> bool {
705        self.syntaxes.is_empty() || (self.syntaxes.len() == 1 && self.syntaxes[0].is_empty())
706    }
707
708    /// Returns true if this callable is a function (not a command).
709    #[allow(unused)]
710    pub(crate) fn is_function(&self) -> bool {
711        self.return_type.is_some()
712    }
713
714    /// Returns true if this callable is user-defined.
715    pub(crate) fn is_user_defined(&self) -> bool {
716        self.category == "User defined"
717    }
718}
719
720/// Reads a boolean from the register at `index`, asserting that `vtype` is `Boolean`.
721fn deref_boolean(regs: &[u64], index: usize, vtype: ExprType) -> bool {
722    assert_eq!(ExprType::Boolean, vtype);
723    regs[index] != 0
724}
725
726/// Reads a double from the register at `index`, asserting that `vtype` is `Double`.
727fn deref_double(regs: &[u64], index: usize, vtype: ExprType) -> f64 {
728    assert_eq!(ExprType::Double, vtype);
729    f64::from_bits(regs[index])
730}
731
732/// Reads an integer from the register at `index`, asserting that `vtype` is `Integer`.
733fn deref_integer(regs: &[u64], index: usize, vtype: ExprType) -> i32 {
734    assert_eq!(ExprType::Integer, vtype);
735    regs[index] as i32
736}
737
738/// Reads a string from the register at `index`, asserting that `vtype` is `Text`.
739fn deref_string<'a>(
740    regs: &[u64],
741    index: usize,
742    vtype: ExprType,
743    constants: &'a [ConstantDatum],
744    heap: &'a Heap,
745) -> &'a str {
746    assert_eq!(ExprType::Text, vtype);
747    let ptr = DatumPtr::from(regs[index]);
748    ptr.resolve_string(constants, heap)
749}
750
751/// Dereferences this register reference as an array and returns its dimensions.
752fn array_dimensions<'a>(regs: &'a [u64], index: usize, heap: &'a Heap) -> &'a [usize] {
753    let ptr = DatumPtr::from(regs[index]);
754    let heap_idx = ptr.heap_index();
755    let HeapDatum::Array(a) = heap.get(heap_idx) else {
756        panic!("Scalar variable does not point to an array on the heap");
757    };
758    &a.dimensions
759}
760
761/// An immutable reference to a variable (register) in the register file, carrying
762/// its type for runtime validation of dereference operations.
763pub struct RegisterRef<'a, 'vm> {
764    /// The scope through which to access the register.
765    scope: &'a Scope<'vm>,
766
767    /// The absolute index of the register.
768    index: usize,
769
770    /// The type of the value pointed to.
771    pub vtype: ExprType,
772}
773
774impl<'a, 'vm> RegisterRef<'a, 'vm> {
775    /// Dereferences this register reference as a boolean.
776    pub fn deref_boolean(&self) -> bool {
777        deref_boolean(self.scope.regs, self.index, self.vtype)
778    }
779
780    /// Dereferences this register reference as a double.
781    pub fn deref_double(&self) -> f64 {
782        deref_double(self.scope.regs, self.index, self.vtype)
783    }
784
785    /// Dereferences this register reference as an integer.
786    pub fn deref_integer(&self) -> i32 {
787        deref_integer(self.scope.regs, self.index, self.vtype)
788    }
789
790    /// Dereferences this register reference as a string.
791    pub fn deref_string(&self) -> &str {
792        deref_string(self.scope.regs, self.index, self.vtype, self.scope.constants, self.scope.heap)
793    }
794
795    /// Dereferences this register reference as an array and returns its dimensions.
796    pub fn array_dimensions(&self) -> &[usize] {
797        array_dimensions(self.scope.regs, self.index, self.scope.heap)
798    }
799}
800
801impl<'a, 'vm> fmt::Display for RegisterRef<'a, 'vm> {
802    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
803        write!(f, "&[R{}]{}", self.index, self.vtype)
804    }
805}
806
807/// A mutable reference to a variable (register) in the register file, carrying
808/// its type for runtime validation of dereference and set operations.
809pub struct RegisterRefMut<'a, 'vm> {
810    /// The scope through which to access the register.
811    scope: &'a mut Scope<'vm>,
812
813    /// The absolute index of the register.
814    index: usize,
815
816    /// The type of the value pointed to.
817    pub vtype: ExprType,
818}
819
820impl<'a, 'vm> RegisterRefMut<'a, 'vm> {
821    /// Dereferences this register reference as a boolean.
822    pub fn deref_boolean(&self) -> bool {
823        deref_boolean(self.scope.regs, self.index, self.vtype)
824    }
825
826    /// Dereferences this register reference as a double.
827    pub fn deref_double(&self) -> f64 {
828        deref_double(self.scope.regs, self.index, self.vtype)
829    }
830
831    /// Dereferences this register reference as an integer.
832    pub fn deref_integer(&self) -> i32 {
833        deref_integer(self.scope.regs, self.index, self.vtype)
834    }
835
836    /// Dereferences this register reference as a string.
837    pub fn deref_string(&self) -> &str {
838        deref_string(self.scope.regs, self.index, self.vtype, self.scope.constants, self.scope.heap)
839    }
840
841    /// Dereferences this register reference as an array and returns its dimensions.
842    pub fn array_dimensions(&self) -> &[usize] {
843        array_dimensions(self.scope.regs, self.index, self.scope.heap)
844    }
845
846    /// Sets a boolean via this register reference.
847    pub fn set_boolean(&mut self, b: bool) {
848        assert_eq!(ExprType::Boolean, self.vtype);
849        self.scope.regs[self.index] = if b { 1 } else { 0 };
850    }
851
852    /// Sets a double via this register reference.
853    pub fn set_double(&mut self, d: f64) {
854        assert_eq!(ExprType::Double, self.vtype);
855        self.scope.regs[self.index] = d.to_bits();
856    }
857
858    /// Sets an integer via this register reference.
859    pub fn set_integer(&mut self, i: i32) {
860        assert_eq!(ExprType::Integer, self.vtype);
861        self.scope.regs[self.index] = i as u64;
862    }
863
864    /// Sets a string via this register reference.
865    pub fn set_string<S: Into<String>>(&mut self, s: S) -> CallResult<()> {
866        assert_eq!(ExprType::Text, self.vtype);
867        self.scope.regs[self.index] = self.scope.heap.push(HeapDatum::Text(s.into()))?;
868        Ok(())
869    }
870}
871
872impl<'a, 'vm> fmt::Display for RegisterRefMut<'a, 'vm> {
873    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
874        write!(f, "&[R{}]{}", self.index, self.vtype)
875    }
876}
877
878/// Arguments provided to a callable during its execution.
879pub struct Scope<'a> {
880    /// Slice of register values containing the callable's arguments.
881    pub(crate) regs: &'a mut [u64],
882
883    /// Reference to the constants pool for resolving constant pointers.
884    pub(crate) constants: &'a [ConstantDatum],
885
886    /// Reference to the heap for resolving heap pointers.
887    pub(crate) heap: &'a mut Heap,
888
889    /// Start of the current frame (where the arguments to the upcall start).
890    pub(crate) fp: usize,
891
892    /// Number of register slots to skip before the first argument.
893    ///
894    /// For commands, this is 0 because the first register slot holds the first argument.  For
895    /// functions, this is 1 because the first register slot holds the return value and arguments
896    /// start at slot 1.  The `get_*` methods add this offset to their register accesses so that
897    /// both commands and functions can use index 0 to refer to the first argument.
898    pub(crate) arg_offset: usize,
899
900    /// Source locations of the call arguments, one per argument in encounter order.
901    ///
902    /// Indexed by logical argument number: `arg_linecols[0]` is the source position of the first
903    /// argument.  Does not include an entry for the function return value slot.  May be shorter
904    /// than the actual argument count if debug information is unavailable.
905    pub(crate) arg_linecols: &'a [LineCol],
906
907    /// Last error raised in the VM, if any.
908    pub(crate) last_error: &'a Option<(LineCol, String)>,
909
910    /// `DATA` values captured from the compiled source.
911    pub(crate) data: &'a [Option<ConstantDatum>],
912}
913
914impl<'a> Scope<'a> {
915    /// Returns `DATA` values captured from the compiled source in encounter order.
916    pub fn data(&self) -> &[Option<ConstantDatum>] {
917        self.data
918    }
919
920    /// Returns the total number of argument register slots.
921    pub fn nargs(&self) -> usize {
922        self.arg_linecols.len()
923    }
924
925    /// Returns the source position of the argument at `arg`.
926    ///
927    /// `arg` is the logical argument index, matching the `N` in `scope.get_*(N)`.
928    pub fn get_pos(&self, arg: u8) -> LineCol {
929        self.arg_linecols[usize::from(arg)]
930    }
931
932    /// Gets the type tag of the argument at `arg`.
933    pub fn get_type(&self, arg: u8) -> VarArgTag {
934        VarArgTag::parse_u64(self.regs[self.fp + self.arg_offset + (arg as usize)]).unwrap()
935    }
936
937    /// Gets the boolean value of the argument at `arg`.
938    pub fn get_boolean(&self, arg: u8) -> bool {
939        self.regs[self.fp + self.arg_offset + (arg as usize)] != 0
940    }
941
942    /// Gets the double value of the argument at `arg`.
943    pub fn get_double(&self, arg: u8) -> f64 {
944        f64::from_bits(self.regs[self.fp + self.arg_offset + (arg as usize)])
945    }
946
947    /// Gets the integer value of the argument at `arg`.
948    pub fn get_integer(&self, arg: u8) -> i32 {
949        self.regs[self.fp + self.arg_offset + (arg as usize)] as i32
950    }
951
952    /// Gets an immutable register reference from the argument at `arg`.
953    pub fn get_ref(&self, arg: u8) -> RegisterRef<'_, 'a> {
954        let tagged_ptr = self.regs[self.fp + self.arg_offset + (arg as usize)];
955        let (index, vtype) = TaggedRegisterRef::from_u64(tagged_ptr).parse();
956        RegisterRef { scope: self, index, vtype }
957    }
958
959    /// Gets a mutable register reference from the argument at `arg`.
960    pub fn get_mut_ref(&mut self, arg: u8) -> RegisterRefMut<'_, 'a> {
961        let tagged_ptr = self.regs[self.fp + self.arg_offset + (arg as usize)];
962        let (index, vtype) = TaggedRegisterRef::from_u64(tagged_ptr).parse();
963        RegisterRefMut { scope: self, index, vtype }
964    }
965
966    /// Gets the string value of the argument at `arg`.
967    pub fn get_string(&self, arg: u8) -> &str {
968        let index = self.regs[self.fp + self.arg_offset + (arg as usize)];
969        let ptr = DatumPtr::from(index);
970        ptr.resolve_string(self.constants, self.heap)
971    }
972
973    /// Returns the last error stored in the VM, if any.
974    pub fn last_error(&self) -> Option<(LineCol, &str)> {
975        self.last_error.as_ref().map(|(pos, message)| (*pos, message.as_str()))
976    }
977
978    /// Sets the return value of the function to `b`.
979    ///
980    /// Always returns success.  The returned value is only to support the idiomatic invocation
981    /// `return scope.return_boolean(...)`.
982    pub fn return_boolean(self, b: bool) -> CallResult<()> {
983        self.regs[self.fp] = if b { 1 } else { 0 };
984        Ok(())
985    }
986
987    /// Sets the return value of the function to `d`.
988    ///
989    /// Always returns success.  The returned value is only to support the idiomatic invocation
990    /// `return scope.return_double(...)`.
991    pub fn return_double(self, d: f64) -> CallResult<()> {
992        self.regs[self.fp] = d.to_bits();
993        Ok(())
994    }
995
996    /// Sets the return value of the function to `i`.
997    ///
998    /// Always returns success.  The returned value is only to support the idiomatic invocation
999    /// `return scope.return_integer(...)`.
1000    pub fn return_integer(self, i: i32) -> CallResult<()> {
1001        self.regs[self.fp] = i as u64;
1002        Ok(())
1003    }
1004
1005    /// Sets the return value of the function to `s`.
1006    ///
1007    /// Always returns success.  The returned value is only to support the idiomatic invocation
1008    /// `return scope.return_string(...)`.
1009    pub fn return_string<S: Into<String>>(self, s: S) -> CallResult<()> {
1010        self.regs[self.fp] = self.heap.push(HeapDatum::Text(s.into()))?;
1011        Ok(())
1012    }
1013}
1014
1015/// A trait to define a callable that is executed by a `Machine`.
1016///
1017/// The callable themselves are immutable but they can reference mutable state.  Given that
1018/// EndBASIC is not threaded, it is sufficient for those references to be behind a `RefCell`
1019/// and/or an `Rc`.
1020///
1021/// Idiomatically, these objects need to provide a `new()` method that returns an `Rc<Callable>`, as
1022/// that's the type used throughout the execution engine.
1023#[async_trait(?Send)]
1024pub trait Callable {
1025    /// Returns the metadata for this function.
1026    ///
1027    /// The return value takes the form of a reference to force the callable to store the metadata
1028    /// as a struct field so that calls to this function are guaranteed to be cheap.
1029    fn metadata(&self) -> Rc<CallableMetadata>;
1030
1031    /// Executes the callable if it is synchronous.
1032    fn exec(&self, _scope: Scope<'_>) -> CallResult<()> {
1033        unimplemented!("Must be implemented for !is_async callables")
1034    }
1035
1036    /// Executes the callable if it is asynchronous.
1037    async fn async_exec(&self, _scope: Scope<'_>) -> CallResult<()> {
1038        unimplemented!("Must be implemented for is_async callables")
1039    }
1040}