From 16063d50f8fa04096ca5d6ca9f16a3c7c2679383 Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Wed, 7 Oct 2020 17:21:01 -0700 Subject: [PATCH] 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 --- src/obj/builtin.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++ src/obj/mod.rs | 1 + src/obj/reserved.rs | 4 ++++ src/syn/parser.y | 2 +- src/vm/frame.rs | 4 ++++ src/vm/mod.rs | 28 +++++++++++++++++++++++---- 6 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 src/obj/builtin.rs diff --git a/src/obj/builtin.rs b/src/obj/builtin.rs new file mode 100644 index 0000000..1aaf830 --- /dev/null +++ b/src/obj/builtin.rs @@ -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 = 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 = 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> = Lazy::new(|| btreemap! { + PRINTLN_BUILTIN_NAME.sym => PRINTLN_BUILTIN_FUN.clone() as _, + PRINT_BUILTIN_NAME.sym => PRINT_BUILTIN_FUN.clone() as _, +}); diff --git a/src/obj/mod.rs b/src/obj/mod.rs index a004013..737d83f 100644 --- a/src/obj/mod.rs +++ b/src/obj/mod.rs @@ -2,6 +2,7 @@ mod macros; pub mod attrs; +pub mod builtin; pub mod fun; pub mod int; pub mod intern; diff --git a/src/obj/reserved.rs b/src/obj/reserved.rs index 1897c39..3c67b70 100644 --- a/src/obj/reserved.rs +++ b/src/obj/reserved.rs @@ -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 diff --git a/src/syn/parser.y b/src/syn/parser.y index 9346d22..810636b 100644 --- a/src/syn/parser.y +++ b/src/syn/parser.y @@ -166,7 +166,7 @@ fn flatten(head: Result>, tail: Result) -> Result> { 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 == '\\' { diff --git a/src/vm/frame.rs b/src/vm/frame.rs index 8950891..8d2b99e 100644 --- a/src/vm/frame.rs +++ b/src/vm/frame.rs @@ -93,6 +93,10 @@ impl NativeFrame { &self.fun_ptr } + pub fn args(&self) -> &Vec { + &self.args + } + pub fn callee(&self) -> &ObjRef { &self.callee } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 2110ef8..82cd5a3 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -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 { + 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 { - 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 { + 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() + } }