Add a couple of builtin functions, and the Vm::call() method

* Builtin functions print and println have been added
* If a global lookup fails, the VM will attempt to look up a builtin
* Vm::call(fun, args) allows interrupting the current execution state
  and starting a new function instead. It will return the value left on
  top of the stack.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-10-07 17:21:01 -07:00
parent 76d0e6723f
commit 16063d50f8
6 changed files with 81 additions and 5 deletions

47
src/obj/builtin.rs Normal file
View File

@@ -0,0 +1,47 @@
use crate::{obj::{prelude::*, reserved::*}, vm::signal::*};
use maplit::btreemap;
use once_cell::sync::Lazy;
use std::collections::BTreeMap;
pub static PRINTLN_BUILTIN_FUN: Lazy<NativeFunRef> = Lazy::new(|| NativeFun::new_obj(|_, vm, args| {
// TODO : use __get_attr__ when it gets added
let to_string = {
let obj_ref = &args[0];
read_obj!(let obj = obj_ref);
obj.get_attr(STR_MEMBER_NAME.sym)
.or_else(|| obj.get_attr(REPR_MEMBER_NAME.sym))
.expect("no __str__ or __repr__ member")
};
let return_value = vm.call(to_string, vec![]);
let str_ref: &StrRef = std::any::Any::downcast_ref(&return_value).expect("to_string to return str value");
{
read_obj!(let str_obj = str_ref);
println!("{}", str_obj.value());
}
Signal::Return
}));
pub static PRINT_BUILTIN_FUN: Lazy<NativeFunRef> = Lazy::new(|| NativeFun::new_obj(|_, vm, args| {
// TODO : use __get_attr__ when it gets added
let to_string = {
let obj_ref = &args[0];
read_obj!(let obj = obj_ref);
obj.get_attr(STR_MEMBER_NAME.sym)
.or_else(|| obj.get_attr(REPR_MEMBER_NAME.sym))
.expect("no __str__ or __repr__ member")
};
let return_value = vm.call(to_string, vec![]);
let str_ref: &StrRef = std::any::Any::downcast_ref(&return_value).expect("to_string to return str value");
{
read_obj!(let str_obj = str_ref);
print!("{}", str_obj.value());
}
Signal::Return
}));
pub static BUILTIN_OBJS: Lazy<BTreeMap<Sym, ObjRef>> = Lazy::new(|| btreemap! {
PRINTLN_BUILTIN_NAME.sym => PRINTLN_BUILTIN_FUN.clone() as _,
PRINT_BUILTIN_NAME.sym => PRINT_BUILTIN_FUN.clone() as _,
});

View File

@@ -2,6 +2,7 @@
mod macros;
pub mod attrs;
pub mod builtin;
pub mod fun;
pub mod int;
pub mod intern;

View File

@@ -43,6 +43,8 @@ name!(GET_ATTR_MEMBER_NAME, "__get_attr__");
name!(SET_ATTR_MEMBER_NAME, "__set_attr__");
name!(SELF_MEMBER_NAME, "__self__");
name!(FUNC_MEMBER_NAME, "__func__");
name!(STR_MEMBER_NAME, "__str__");
name!(REPR_MEMBER_NAME, "__repr__");
//
// Predefined VM-aware symbols
@@ -55,6 +57,8 @@ name!(FUNC_MEMBER_NAME, "__func__");
//name!(REPR_FUN_NAME, REPR_FUN_SYM, "repr");
//name!(GET_LOCAL_FUN_NAME, GET_LOCAL_FUN_SYM, "get_local");
//name!(SET_LOCAL_FUN_NAME, SET_LOCAL_FUN_SYM, "set_local");
name!(PRINTLN_BUILTIN_NAME, "println");
name!(PRINT_BUILTIN_NAME, "print");
//
// Builtin constants

View File

@@ -166,7 +166,7 @@ fn flatten<T>(head: Result<Vec<T>>, tail: Result<T>) -> Result<Vec<T>> {
fn parse_string(input: &str) -> String {
let mut s = String::new();
let input = &input[1..input.bytes().len() - 1];
let input = &input[..input.bytes().len() - 1];
let mut chars = input.chars();
while let Some(c) = chars.next() {
if c == '\\' {

View File

@@ -93,6 +93,10 @@ impl NativeFrame {
&self.fun_ptr
}
pub fn args(&self) -> &Vec<ObjRef> {
&self.args
}
pub fn callee(&self) -> &ObjRef {
&self.callee
}

View File

@@ -1,11 +1,11 @@
pub mod consts;
pub mod error; // TODO : not needed?
pub mod error;
pub mod frame;
pub mod inst;
pub mod signal;
use crate::{
obj::{reserved::*, prelude::*},
obj::{builtin::BUILTIN_OBJS, reserved::*, prelude::*},
vm::{consts::ConstPool, frame::*, inst::*, signal::*},
};
@@ -91,6 +91,13 @@ impl<'c> Vm<'c> {
self.condition = condition;
}
/// Calls a function.
pub fn call(&mut self, fun: ObjRef, args: Vec<ObjRef>) -> ObjRef {
self.handle_signal(Signal::Call(fun, args));
self.resume_until_return();
self.pop().expect("return value")
}
/// Resumes execution of the current program.
pub fn resume(&mut self) {
while !self.frames().is_empty() {
@@ -111,7 +118,8 @@ impl<'c> Vm<'c> {
let signal = match frame {
Frame::Native(fun) => {
let callee = fun.callee().clone();
(*fun.fun_ptr())(callee, self, vec![])
let args = fun.args().clone();
(*fun.fun_ptr())(callee, self, args)
}
Frame::User(_) => {
self.resume_user_fun()
@@ -200,6 +208,7 @@ impl<'c> Vm<'c> {
}
Inst::LoadGlobal(global) => {
let value = self.get_global(global)
.or_else(|| self.get_builtin(global))
.expect("TODO: throw error for missing global");
self.push(value);
}
@@ -332,7 +341,7 @@ impl<'c> Vm<'c> {
}
fn get_global(&self, name: Name) -> Option<ObjRef> {
self.frames
self.frames()
.first()?
.user_frame()?
.bindings()
@@ -349,4 +358,15 @@ impl<'c> Vm<'c> {
let bindings = frame.bindings_mut();
bindings.insert(name.index(), value);
}
fn get_builtin(&self, name: Name) -> Option<ObjRef> {
read_obj!(let fun = self.frames()
.first()?
.user_frame()?
.callee()
);
let fun: &UserFun = std::any::Any::downcast_ref(fun.as_any()).unwrap();
let sym = fun.locals()[name.index()];
BUILTIN_OBJS.get(&sym).cloned()
}
}