avid 0.6.1

A plug-and-play scripting language
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
use std::{
    collections::{HashMap, HashSet},
    fmt::Debug,
};

use crate::{
    func::{Builtin, Callable, BUILTIN_TYPES},
    parser::Parser,
    Avid, Result, Error, Ast, ast::SendCallable,
};

/// Easy way to build an [Avid] instance.
///
/// Instantiate it with [new](Builder::new), change its settings with the various
/// methods, and then "compile" the source into an [Avid] instance by calling [build](Builder::build).
///
///
/// # Examples
/// ```
/// use avid::Builder;
///
/// let src = "5 print";
///
/// // Prints `5`
/// Builder::new(src)
///     .build()
///     .unwrap()
///     .run(None)
///     .unwrap();
/// ```
pub struct Builder<'a, 'f, T: BuildTarget<'a, 'f>> {
    src: &'a str,
    src_name: Option<String>,
    promised: HashSet<String>,
    provided: HashMap<String, T::Function>,
}

impl<'a, 'f, T: BuildTarget<'a, 'f>> Debug for Builder<'a, 'f, T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Builder")
            .field("src", &self.src)
            .field("promised", &self.promised)
            .field(
                "provided",
                &self
                    .provided
                    .iter()
                    .map(|(key, _)| (key, "<AvidFunc>"))
                    .collect::<HashMap<_, _>>(),
            )
            .finish()
    }
}

impl<'a, 'f: 'a> Builder<'a, 'f, Avid<'a, 'f>> {
    /// Creates a new instance of a `Builder` with the default configuration.
    ///
    /// For now, that means that it has the small Avid standard library, which can be found
    /// [in the documentation](https://gitlab.com/daisyflare/avid/-/blob/main/doc/StandardLib.md). To change this configuration,
    /// use the [Builder::remove()] and [Builder::register_fn()] methods.
    ///
    /// # Examples
    /// ```
    /// use avid::Builder;
    ///
    /// let src = "- + print";
    ///
    /// let res = Builder::new(src).build();
    ///
    /// // All of those functions are available.
    /// assert!(res.is_ok());
    /// ```
    pub fn new(src: &'a str) -> Self {
        static_assert_eq!(
            BUILTIN_TYPES.len(),
            16,
            "Change list of standard library in docs/StandardLib.md"
        );
        Self {
            src,
            src_name: None,
            promised: HashSet::new(),
            provided: HashMap::new(),
        }
        .standard_lib()
    }


}

impl<'a> Builder<'a, 'a, Ast<'a>> {
    /// Start building an [Ast] with the default configuration.
    ///
    /// If you want to just build the [Ast] with the default configuration, use [Ast::new()].
    ///
    /// # Examples
    /// ```
    /// use avid::{Ast, Builder};
    ///
    /// let src = "hi";
    ///
    /// let ast: Ast = Builder::new_ast(src)
    ///     .promise("hi")
    ///     .build()
    ///     .unwrap();
    ///
    /// // now do stuff with ast
    /// ```
    pub fn new_ast(src: &'a str) -> Builder<'a, 'a, Ast<'a>> {
        static_assert_eq!(
            BUILTIN_TYPES.len(),
            16,
            "Change list of standard library in docs/StandardLib.md"
        );
        Self {
            src,
            src_name: None,
            promised: HashSet::new(),
            provided: HashMap::new(),
        }
        .standard_lib()
    }
}

impl<'a, 'f, T: BuildTarget<'a, 'f>> Builder<'a, 'f, T> {
    /// Consumes the builder and creates the [Avid] instance
    ///
    /// When this method is called, the source code is parsed and checked for syntax errors, then (if
    /// there were no errors) an [Avid] instance is returned.
    ///
    /// # Examples
    /// ```should_panic
    /// use avid::Builder;
    ///
    /// let builder = Builder::new("/* some source code */");
    ///
    /// match builder.build() {
    ///     Err(e) => eprintln!("There was a syntax error: {e:?}"),
    ///     Ok(avid) => todo!("Do something with the interpreter!")
    /// }
    /// ```
    pub fn build(self) -> Result<T> {
        T::try_from(self)
    }

    /// Set the name of the source file for error reporting.
    ///
    /// If this method is not entered, errors will default to using "<provided>" as the name of
    /// the source file.
    ///
    /// # Examples
    /// Without changing the name of the source file:
    /// ```
    /// use avid::Builder;
    ///
    /// let src = "unknown-variable";
    ///
    /// let should_be_error = Builder::new(src)
    ///     .build()
    ///     .unwrap_err();
    ///
    /// assert!(should_be_error.to_string().starts_with("<provided>:1:1: Error"))
    /// ```
    /// After changing the name of the source file:
    /// ```
    /// use avid::Builder;
    ///
    /// let src = "unknown-variable";
    ///
    /// let should_be_error = Builder::new(src)
    ///     .src_name("file")
    ///     .build()
    ///     .unwrap_err();
    ///
    /// assert!(should_be_error.to_string().starts_with("file:1:1: Error"))
    /// ```
    pub fn src_name<N: ToString>(mut self, name: N) -> Self {
        self.src_name = Some(name.to_string());
        self
    }


    /// Promise that a function will be available at runtime.
    ///
    /// This helps get around lifetime requirements or cases when a variable is unknown until call time,
    /// such as in hooks or callbacks.
    ///
    /// # Examples
    /// ```
    /// use avid::*;
    ///
    /// let src = "get-msg print";
    ///
    /// let avid = Builder::new(src)
    ///     .promise("get-msg")
    ///     .build().unwrap();
    ///
    /// // Msg is not known until after the instance is compiled
    /// let msg = "Hello!";
    ///
    /// let get_msg = |s: &mut Stack| {
    ///     s.push(Object::String(msg.to_string()));
    ///     Ok(())
    /// };
    ///
    /// let mut promises = PromiseBuilder::new()
    ///     .add_promise("get-msg", get_msg)
    ///     .build();
    ///
    /// // Prints "Hello!"
    /// avid.run(Some(&mut promises)).unwrap();
    /// ```
    /// This can be used to provide runtime-only information.
    ///
    /// ```
    /// use avid::*;
    ///
    /// let src = "inc-x";
    ///
    /// let mut avid = Builder::new(src)
    ///     .promise("inc-x")
    ///     .build().unwrap();
    ///
    /// let mut x = 0;
    ///
    /// {
    ///     let mut promises = PromiseBuilder::new()
    ///         .add_promise("inc-x", |_| {
    ///             x += 1;
    ///             Ok(())
    ///         })
    ///         .build();
    ///
    ///     // Increments x
    ///     avid.run_mut(Some(&mut promises)).unwrap();
    /// }
    ///
    /// // Both of these can be reused in this way as soon as `promises` goes out of scope
    /// dbg!(&x);
    /// dbg!(&avid);
    /// ```
    /// This also can be used to not consume variables until the Avid instance goes out of scope.
    pub fn promise<N: ToString>(mut self, name: N) -> Self {
        self.promised.insert(name.to_string());
        self
    }

    /// Registers a new function in the interpreter-to-be under the specified name.
    ///
    /// The function gets registered under `name` and must be called as such in the program.
    ///
    /// # NOTE
    /// If you get strange errors about lifetimes and/or closure typing while using this method,
    /// make sure that you have annotated the argument's type (`|arg: &mut Stack|`).
    ///
    /// Avid is still in it's beta stage, so there are still some kinks to be worked out in the
    /// library. This is one of them. Hopefully it'll be fixed soon!
    ///
    /// # Examples
    /// ```
    /// use avid::{Builder, Stack, Object};
    ///
    /// let src = "msg eprint";
    ///
    /// let avid = Builder::new(src)
    ///     // Add a function that pushes a string onto the stack.
    ///     .register_fn("msg", |stack: &mut Stack| {
    ///         stack.push(Object::String("Hi!".to_string()));
    ///         Ok(())
    ///     })
    ///     // Add a function that pops one thing from the stack and prints it
    ///     // to stderr.
    ///     .register_fn("eprint", |stack: &mut Stack| {
    ///         let [to_print] = stack.pop()?;
    ///         eprint!("{}", to_print.to_string());
    ///         Ok(())
    ///     }).build().unwrap();
    ///
    /// // Will print "Hi!" to stderr
    /// avid.run(None).unwrap();
    /// ```
    // TODO(#18): Make register_fn take values as opposed to the stack
    // then the current register_fn can be renamed to register_stack_fn.

    // TODO(#19): Weird errors when register_fn is not type-annotated.
    pub fn register_fn<N: ToString, F: Into<T::Function> + 'f>(
        mut self,
        name: N,
        func: F,
    ) -> Self {
        let name = name.to_string();

        // let func = Box::new(func) as Box<dyn AvidFunc>;
        // let func = Callable::Unknown {
        //     name: Some(name.clone()),
        //     func,
        // };
        let func = func.into();

        self.provided.insert(name, func);
        self
    }

    /// Removes a function or object from the scope of the interpreter-to-be.
    ///
    /// When this function is called, any function registered under `name` gets unregistered
    /// from the scope of the interpreter-to-be and, if a user tries to call it, an error
    /// is thrown.
    ///
    /// # Examples
    /// ```
    /// use avid::{Builder, Stack};
    ///
    /// let src = "func";
    ///
    /// let res = Builder::new(src)
    ///     .register_fn("func", |_: &mut Stack| {
    ///         println!("func was called");
    ///         Ok(())
    ///     })
    ///     .remove("func")
    ///     .build();
    ///
    /// // Syntax error: Unknown variable "func"
    /// assert!(res.is_err());
    /// ```
    pub fn remove<N: ToString>(mut self, name: N) -> Self {
        let name = name.to_string();
        self.provided.remove(&name);
        self
    }

    fn register_builtin<N: ToString>(mut self, name: N, builtin: Builtin) -> Self {
        let name = name.to_string();

        self.provided.insert(name, T::Function::from(builtin));

        self
    }

    fn standard_lib(self) -> Self {
        static_assert_eq!(
            BUILTIN_TYPES.len(),
            16,
            "Choose whether to add to standard library"
        );

        self.register_builtin("+", Builtin::Sum)
            .register_builtin("-", Builtin::Subtract)
            .register_builtin("print", Builtin::Print)
            .register_builtin("dup", Builtin::Dup)
            .register_builtin("swap", Builtin::Swap)
            .register_builtin("drop", Builtin::Drop)
            .register_builtin("list", Builtin::NewList)
            .register_builtin("append", Builtin::AppendList)
            .register_builtin("not", Builtin::Not)
            .register_builtin("==", Builtin::Eq)
            .register_builtin("!=", Builtin::NotEq)
            .register_builtin("get", Builtin::GetFromList)
            .register_builtin("rot", Builtin::Rot)
    }

    /// Enable or disable the stack debugger.
    ///
    /// Often while writing code in stack-oriented languages, it can be difficult to keep
    /// track of what is on the stack at any particular point in the code. Avid has a
    /// "stack debugging" function, `???`, which prints the types of the stack and then
    /// exits.
    ///
    /// # Warning
    /// The stack debugger makes the thread panic! This is okay in some kinds of
    /// programs, such as a basic interpreter or single-user application, but seemingly
    /// random crashes are not good in a multi-user program! Allow with caution!
    ///
    /// # Examples
    /// ```should_panic
    /// use avid::Builder;
    ///
    /// let src = "1 false \"Hi!\" ???";
    ///
    /// let avid = Builder::new(src)
    ///     .allow_stack_debug(true)
    ///     .build().unwrap();
    ///
    /// // Panics with "[Num Bool String]"
    /// avid.run(None).unwrap();
    /// ```
    pub fn allow_stack_debug(self, allowed: bool) -> Self {
        if allowed {
            self.register_builtin("???", Builtin::StackDebug)
        } else {
            self.remove("???")
        }
    }

    /// Removes all standard library IO functions from the interpreter.
    ///
    /// Often, an author of a program wants to sandbox potentially malicious code
    /// written by users. This function helps with that by removing any ability
    /// to use IO functions in the Avid interpreter.
    ///
    /// # Examples
    /// ```
    /// use avid::Builder;
    ///
    /// let src = "\"IO D:\" print";
    ///
    /// let res = Builder::new(src)
    ///     .no_io()
    ///     .build();
    ///
    /// // Syntax error: Unknown value "print"
    /// assert!(res.is_err());
    /// ```
    ///
    /// When this function is called, the following functions are removed from
    /// the standard library and will not be recognised if a user tries to use
    /// them: `print`.
    pub fn no_io(self) -> Self {
        static_assert_eq!(
            BUILTIN_TYPES.len(),
            16,
            "Change list of IO functions in comment of `Builder::no_io`"
        );
        self.remove("print")
    }
}

pub trait BuildTarget<'a, 'f>: TryFrom<Builder<'a, 'f, Self>, Error = Error> {
    type Function: From<Builtin>;
}

impl<'a, 'f: 'a> TryFrom<Builder<'a, 'f, Self>> for Avid<'a, 'f> {
    type Error = Error;

    fn try_from(value: Builder<'a, 'f, Self>) -> Result<Self, Self::Error> {
        let parser = Parser::<Self>::new(value.src, value.src_name, value.promised, value.provided);
        parser.parse()
    }
}

impl<'a, 'f: 'a> BuildTarget<'a, 'f> for Avid<'a, 'f> {
    #[doc(hidden)]
    type Function = Callable<'a, 'f>;
}

impl<'a> TryFrom<Builder<'a, 'a, Ast<'a>>> for Ast<'a> {
    type Error = Error;

    fn try_from(value: Builder<'a, 'a, Ast<'a>>) -> Result<Self, Self::Error> {
        let parser = Parser::<Ast>::new(value.src, value.src_name, value.promised, value.provided);
        parser.parse()
    }
}

impl<'a> BuildTarget<'a, 'a> for Ast<'a> {
    #[doc(hidden)]
    type Function = SendCallable<'a>;
}