Add branching and more coherent method of function calls

We're turing-complete, babey!

* Call stack for functions now differentiates between native and quote
  calls
* Better API for builtin functions allowing for more control
* Example showing off branches

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2022-01-12 21:49:41 -08:00
parent c31be8142c
commit 90f27a4108
5 changed files with 274 additions and 66 deletions

View File

@@ -4,7 +4,7 @@
#![allow(dead_code)]
use crate::syn::ast::SpExpr;
use crate::vm::machine::Machine;
use crate::vm::{error::Result, machine::Machine};
use crate::{scope::Scope, syn::span::Span, vm::inst::Inst};
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap};
@@ -33,7 +33,7 @@ impl Quote {
/// A table of compiled quotes, their expression trees, and their spans.
#[derive(Debug, Clone, Default)]
pub struct QuoteTable {
table: Vec<(Span, Scope, Vec<SpExpr>, Vec<Inst>)>,
table: Vec<(Span, Scope, Vec<SpExpr>, Rc<Vec<Inst>>)>,
}
impl QuoteTable {
@@ -46,14 +46,14 @@ impl QuoteTable {
span: Span,
scope: Scope,
quote: Vec<SpExpr>,
compiled: Vec<Inst>,
compiled: Rc<Vec<Inst>>,
) -> Quote {
let next = Quote(self.table.len());
self.table.push((span, scope, quote, compiled));
next
}
pub fn get(&self, quote: Quote) -> &(Span, Scope, Vec<SpExpr>, Vec<Inst>) {
pub fn get(&self, quote: Quote) -> &(Span, Scope, Vec<SpExpr>, Rc<Vec<Inst>>) {
&self.table[quote.0]
}
}
@@ -86,6 +86,19 @@ impl Value {
BuiltinFn(_) => "builtin function",
}
}
pub fn is_truthy(&self) -> bool {
use Value::*;
match self {
Array(a) => a.len() > 0,
Float(f) => *f != 0.0,
Int(i) => *i != 0,
Str(s) => s.len() > 0,
Quote(_) => true,
ObjPtr(_) => true,
BuiltinFn(_) => true,
}
}
}
impl Display for Value {
@@ -103,8 +116,40 @@ impl Display for Value {
}
}
/// The intent of a builtin function once the function proper returns.
///
/// When a builtin function itself returns, it may want to remain on the call
/// stack, or do some kind of cleanup before it's finished. So, a function may
/// pre-empt itself with the understanding that it will eventually return.
#[derive(Debug, Clone, Copy)]
pub enum BuiltinExit {
/// Pop this builtin off the call stack, and then call the given quote.
Call(Quote),
/// Continue with the assumption that some other function has been pushed to
/// the call stack, updating the reentry for this function call to the
/// specified constant.
Resume(usize),
/// Pops this builtin off the call stack, returning like normal.
Return,
}
#[derive(Clone)]
pub struct BuiltinFn(Rc<dyn FnMut(&mut Machine)>);
pub struct BuiltinFn(Rc<BuiltinFnPtr>);
impl BuiltinFn {
pub fn new(fun: Rc<BuiltinFnPtr>) -> Self {
BuiltinFn(fun)
}
pub fn call(&self, machine: &mut Machine, reentry: usize) -> Result<BuiltinExit> {
(self.0)(machine, reentry)
}
}
//pub type BuiltinFnPtr = dyn Fn(&mut Machine, usize) -> Result<BuiltinExit>;
pub type BuiltinFnPtr = fn(&mut Machine, usize) -> Result<BuiltinExit>;
impl Debug for BuiltinFn {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {