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:
47
src/obj/builtin.rs
Normal file
47
src/obj/builtin.rs
Normal 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 _,
|
||||||
|
});
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
pub mod attrs;
|
pub mod attrs;
|
||||||
|
pub mod builtin;
|
||||||
pub mod fun;
|
pub mod fun;
|
||||||
pub mod int;
|
pub mod int;
|
||||||
pub mod intern;
|
pub mod intern;
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ name!(GET_ATTR_MEMBER_NAME, "__get_attr__");
|
|||||||
name!(SET_ATTR_MEMBER_NAME, "__set_attr__");
|
name!(SET_ATTR_MEMBER_NAME, "__set_attr__");
|
||||||
name!(SELF_MEMBER_NAME, "__self__");
|
name!(SELF_MEMBER_NAME, "__self__");
|
||||||
name!(FUNC_MEMBER_NAME, "__func__");
|
name!(FUNC_MEMBER_NAME, "__func__");
|
||||||
|
name!(STR_MEMBER_NAME, "__str__");
|
||||||
|
name!(REPR_MEMBER_NAME, "__repr__");
|
||||||
|
|
||||||
//
|
//
|
||||||
// Predefined VM-aware symbols
|
// Predefined VM-aware symbols
|
||||||
@@ -55,6 +57,8 @@ name!(FUNC_MEMBER_NAME, "__func__");
|
|||||||
//name!(REPR_FUN_NAME, REPR_FUN_SYM, "repr");
|
//name!(REPR_FUN_NAME, REPR_FUN_SYM, "repr");
|
||||||
//name!(GET_LOCAL_FUN_NAME, GET_LOCAL_FUN_SYM, "get_local");
|
//name!(GET_LOCAL_FUN_NAME, GET_LOCAL_FUN_SYM, "get_local");
|
||||||
//name!(SET_LOCAL_FUN_NAME, SET_LOCAL_FUN_SYM, "set_local");
|
//name!(SET_LOCAL_FUN_NAME, SET_LOCAL_FUN_SYM, "set_local");
|
||||||
|
name!(PRINTLN_BUILTIN_NAME, "println");
|
||||||
|
name!(PRINT_BUILTIN_NAME, "print");
|
||||||
|
|
||||||
//
|
//
|
||||||
// Builtin constants
|
// Builtin constants
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ fn flatten<T>(head: Result<Vec<T>>, tail: Result<T>) -> Result<Vec<T>> {
|
|||||||
|
|
||||||
fn parse_string(input: &str) -> String {
|
fn parse_string(input: &str) -> String {
|
||||||
let mut s = String::new();
|
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();
|
let mut chars = input.chars();
|
||||||
while let Some(c) = chars.next() {
|
while let Some(c) = chars.next() {
|
||||||
if c == '\\' {
|
if c == '\\' {
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ impl NativeFrame {
|
|||||||
&self.fun_ptr
|
&self.fun_ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn args(&self) -> &Vec<ObjRef> {
|
||||||
|
&self.args
|
||||||
|
}
|
||||||
|
|
||||||
pub fn callee(&self) -> &ObjRef {
|
pub fn callee(&self) -> &ObjRef {
|
||||||
&self.callee
|
&self.callee
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod error; // TODO : not needed?
|
pub mod error;
|
||||||
pub mod frame;
|
pub mod frame;
|
||||||
pub mod inst;
|
pub mod inst;
|
||||||
pub mod signal;
|
pub mod signal;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
obj::{reserved::*, prelude::*},
|
obj::{builtin::BUILTIN_OBJS, reserved::*, prelude::*},
|
||||||
vm::{consts::ConstPool, frame::*, inst::*, signal::*},
|
vm::{consts::ConstPool, frame::*, inst::*, signal::*},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -91,6 +91,13 @@ impl<'c> Vm<'c> {
|
|||||||
self.condition = condition;
|
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.
|
/// Resumes execution of the current program.
|
||||||
pub fn resume(&mut self) {
|
pub fn resume(&mut self) {
|
||||||
while !self.frames().is_empty() {
|
while !self.frames().is_empty() {
|
||||||
@@ -111,7 +118,8 @@ impl<'c> Vm<'c> {
|
|||||||
let signal = match frame {
|
let signal = match frame {
|
||||||
Frame::Native(fun) => {
|
Frame::Native(fun) => {
|
||||||
let callee = fun.callee().clone();
|
let callee = fun.callee().clone();
|
||||||
(*fun.fun_ptr())(callee, self, vec![])
|
let args = fun.args().clone();
|
||||||
|
(*fun.fun_ptr())(callee, self, args)
|
||||||
}
|
}
|
||||||
Frame::User(_) => {
|
Frame::User(_) => {
|
||||||
self.resume_user_fun()
|
self.resume_user_fun()
|
||||||
@@ -200,6 +208,7 @@ impl<'c> Vm<'c> {
|
|||||||
}
|
}
|
||||||
Inst::LoadGlobal(global) => {
|
Inst::LoadGlobal(global) => {
|
||||||
let value = self.get_global(global)
|
let value = self.get_global(global)
|
||||||
|
.or_else(|| self.get_builtin(global))
|
||||||
.expect("TODO: throw error for missing global");
|
.expect("TODO: throw error for missing global");
|
||||||
self.push(value);
|
self.push(value);
|
||||||
}
|
}
|
||||||
@@ -332,7 +341,7 @@ impl<'c> Vm<'c> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_global(&self, name: Name) -> Option<ObjRef> {
|
fn get_global(&self, name: Name) -> Option<ObjRef> {
|
||||||
self.frames
|
self.frames()
|
||||||
.first()?
|
.first()?
|
||||||
.user_frame()?
|
.user_frame()?
|
||||||
.bindings()
|
.bindings()
|
||||||
@@ -349,4 +358,15 @@ impl<'c> Vm<'c> {
|
|||||||
let bindings = frame.bindings_mut();
|
let bindings = frame.bindings_mut();
|
||||||
bindings.insert(name.index(), value);
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user