Initial commit
* Parser, compiler, objects, and VM base implementations * Stuff will print out, functions called, etc * There are probably plenty of bugs but this is a good starting point to start committing changes into git Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
137
src/obj/builtin_fun.rs
Normal file
137
src/obj/builtin_fun.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::vm::frame::Frame;
|
||||
use crate::{obj::prelude::*, vm::Vm};
|
||||
|
||||
pub type BuiltinFunPtr = fn(BuiltinFunVm, Vec<ObjPtr>, BuiltinFunState) -> BuiltinFunResult;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! builtin_fun {
|
||||
($name:expr, $ptr:expr) => {
|
||||
$crate::obj::builtin_fun::BuiltinFun::new($name.into(), $ptr)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! builtin_fun_ptr {
|
||||
($($tt:tt)+) => { $crate::obj::ObjPtr::new(builtin_fun!($($tt)+)) };
|
||||
}
|
||||
|
||||
pub use builtin_fun;
|
||||
pub use builtin_fun_ptr;
|
||||
|
||||
/// A result from a builtin function.
|
||||
#[derive(Debug)]
|
||||
pub enum BuiltinFunResult {
|
||||
/// Return the given optional object.
|
||||
Return(Option<ObjPtr>),
|
||||
/// Yield function execution to the given stack frame.
|
||||
Yield(Frame),
|
||||
}
|
||||
|
||||
/// A builtin function may be resumed after it's done calling a user-defined
|
||||
/// function. This enum determines what the function execution context is.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BuiltinFunState {
|
||||
/// This function is being called for the first time.
|
||||
Begin,
|
||||
/// This function is being resumed after yielding control to some other function.
|
||||
Resume(usize),
|
||||
}
|
||||
|
||||
impl BuiltinFunState {
|
||||
pub fn next(&self) -> Self {
|
||||
match self {
|
||||
BuiltinFunState::Begin => BuiltinFunState::Resume(0),
|
||||
BuiltinFunState::Resume(n) => BuiltinFunState::Resume(n + 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a wrapper structure around the VM that provides an API to a
|
||||
/// `BuiltinFun` function call.
|
||||
///
|
||||
/// It has some extra checks that prevent the VM being used in a weird/unexpected way.
|
||||
pub struct BuiltinFunVm<'vm> {
|
||||
vm: &'vm mut Vm,
|
||||
last_sp: usize,
|
||||
}
|
||||
|
||||
impl<'vm> BuiltinFunVm<'vm> {
|
||||
pub fn new(vm: &'vm mut Vm, last_sp: usize) -> Self {
|
||||
Self { vm, last_sp }
|
||||
}
|
||||
|
||||
pub fn stack(&self) -> &Vec<ObjPtr> {
|
||||
self.vm.stack()
|
||||
}
|
||||
|
||||
pub fn make_frame(&self, fun: ObjPtr, args: Vec<ObjPtr>) -> Option<Frame> {
|
||||
self.vm.make_frame(fun, args)
|
||||
}
|
||||
|
||||
pub fn push_stack(&mut self, value: ObjPtr) {
|
||||
self.vm.push_stack(value);
|
||||
}
|
||||
|
||||
pub fn pop_stack(&mut self) -> Option<ObjPtr> {
|
||||
if self.stack().len() <= self.last_sp {
|
||||
panic!("Tried to underflow stack in BuiltinFun call ");
|
||||
}
|
||||
self.pop_stack_unchecked()
|
||||
}
|
||||
|
||||
pub fn pop_stack_unchecked(&mut self) -> Option<ObjPtr> {
|
||||
self.vm.pop_stack()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BuiltinFun {
|
||||
name: String,
|
||||
fun: BuiltinFunPtr,
|
||||
attrs: AttrsPtr,
|
||||
}
|
||||
|
||||
impl fmt::Debug for BuiltinFun {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("BuiltinFun")
|
||||
.field("name", &self.name)
|
||||
.field(
|
||||
"fun",
|
||||
&format!(
|
||||
"<builtin function at {:#016x}>",
|
||||
(&self.fun as *const _) as usize
|
||||
),
|
||||
)
|
||||
.field("attrs", &self.attrs)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl BuiltinFun {
|
||||
pub fn new(name: String, fun: BuiltinFunPtr) -> Self {
|
||||
Self {
|
||||
name,
|
||||
fun,
|
||||
attrs: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fun(&self) -> BuiltinFunPtr {
|
||||
self.fun
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for BuiltinFun {
|
||||
fn attrs(&self) -> AttrsPtr {
|
||||
AttrsPtr::clone(&self.attrs)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
55
src/obj/builtins.rs
Normal file
55
src/obj/builtins.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
//! Builtin functions and objects.
|
||||
use crate::obj::prelude::*;
|
||||
|
||||
pub const BUILTIN_FUNS: &[(&str, BuiltinFunPtr)] = &[
|
||||
//
|
||||
("println", println),
|
||||
("print", print),
|
||||
];
|
||||
|
||||
fn println(vm: BuiltinFunVm, args: Vec<ObjPtr>, state: BuiltinFunState) -> BuiltinFunResult {
|
||||
do_print(vm, args, state, "\n")
|
||||
}
|
||||
|
||||
fn print(vm: BuiltinFunVm, args: Vec<ObjPtr>, state: BuiltinFunState) -> BuiltinFunResult {
|
||||
do_print(vm, args, state, "")
|
||||
}
|
||||
|
||||
/// Shared logic of `print` and `println`.
|
||||
/// It can append a string to the end of whatever is being printed.
|
||||
fn do_print(
|
||||
mut vm: BuiltinFunVm,
|
||||
args: Vec<ObjPtr>,
|
||||
state: BuiltinFunState,
|
||||
end: &str,
|
||||
) -> BuiltinFunResult {
|
||||
const POST_CALL_STR: BuiltinFunState = BuiltinFunState::Resume(0);
|
||||
|
||||
if state == BuiltinFunState::Begin {
|
||||
if args.len() == 0 {
|
||||
println!();
|
||||
return BuiltinFunResult::Return(None);
|
||||
} else if args.len() != 1 {
|
||||
todo!("Throw exception: arity error");
|
||||
}
|
||||
|
||||
let arg = ObjPtr::clone(&args[0]);
|
||||
let str_fun = arg.get("__str__").expect("No __str__ function?");
|
||||
let frame = vm
|
||||
.make_frame(str_fun, vec![arg])
|
||||
.expect("__str__ attr was not a function?");
|
||||
BuiltinFunResult::Yield(frame)
|
||||
} else if state == POST_CALL_STR {
|
||||
// Get return value, cast it as an `Any`, and then downcast to a string.
|
||||
let result = vm.pop_stack().expect("No return value from __str__?");
|
||||
let result_any = result.as_any();
|
||||
if let Some(value) = result_any.downcast_ref::<Str>() {
|
||||
print!("{}{}", value.value(), end);
|
||||
} else {
|
||||
todo!("Throw exception: __str__ function didn't return a Str?");
|
||||
}
|
||||
BuiltinFunResult::Return(None)
|
||||
} else {
|
||||
panic!("unexpected state: {state:?}");
|
||||
}
|
||||
}
|
||||
53
src/obj/int.rs
Normal file
53
src/obj/int.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use crate::obj::prelude::*;
|
||||
use std::{any::Any, sync::LazyLock};
|
||||
|
||||
pub type IntValue = i64;
|
||||
|
||||
static INT_ATTRS: LazyLock<AttrsPtr> = LazyLock::new(|| {
|
||||
attrs_ptr! {
|
||||
//
|
||||
"__str__" => builtin_fun_ptr!("__str__", |_vm, args, _state| {
|
||||
if args.len() != 1 {
|
||||
todo!("Throw exception: arity error");
|
||||
}
|
||||
let arg_any = &args[0].as_any();
|
||||
let obj = arg_any.downcast_ref::<Int>().expect("Int.__str__ did not receive Int?");
|
||||
|
||||
BuiltinFunResult::Return(Some(ObjPtr::new(Str::new(obj.value().to_string()))))
|
||||
}),
|
||||
}
|
||||
});
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Int {
|
||||
attrs: AttrsPtr,
|
||||
value: IntValue,
|
||||
}
|
||||
|
||||
impl Int {
|
||||
pub fn new(value: IntValue) -> Self {
|
||||
let attrs = AttrsPtr::clone(&INT_ATTRS);
|
||||
// TODO intern
|
||||
Int { attrs, value }
|
||||
}
|
||||
|
||||
pub fn value(&self) -> IntValue {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for Int {
|
||||
fn attrs(&self) -> AttrsPtr {
|
||||
AttrsPtr::clone(&self.attrs)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn Any + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Int {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{}", self.value())
|
||||
}
|
||||
}
|
||||
73
src/obj/mod.rs
Normal file
73
src/obj/mod.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
pub mod builtin_fun;
|
||||
pub mod builtins;
|
||||
pub mod int;
|
||||
pub mod none;
|
||||
pub mod str;
|
||||
pub mod user_fun;
|
||||
|
||||
pub mod prelude {
|
||||
// Module types
|
||||
pub use super::{builtin_fun::*, int::*, none::*, str::*, user_fun::*};
|
||||
// Local types
|
||||
pub use super::{Attrs, AttrsPtr, Obj, ObjPtr};
|
||||
// Macros
|
||||
pub use super::{attrs, attrs_ptr};
|
||||
// Builtins
|
||||
pub use super::builtins::*;
|
||||
}
|
||||
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
// Macro definitions
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! attrs {
|
||||
($($key:expr => $value:expr),* $(,)?) => {{
|
||||
let mut __attrs = $crate::obj::Attrs::new();
|
||||
$(
|
||||
__attrs.insert($key.into(), $value);
|
||||
)*
|
||||
__attrs
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! attrs_ptr {
|
||||
($($tt:tt)*) => {{
|
||||
$crate::obj::AttrsPtr::new(
|
||||
std::sync::RwLock::new(
|
||||
attrs! {
|
||||
$($tt)*
|
||||
}
|
||||
)
|
||||
)
|
||||
}};
|
||||
}
|
||||
|
||||
pub use attrs;
|
||||
pub use attrs_ptr;
|
||||
|
||||
pub type ObjPtr<T = dyn Obj + 'static> = Arc<T>;
|
||||
pub type Attrs = HashMap<String, ObjPtr>;
|
||||
pub type AttrsPtr = Arc<RwLock<Attrs>>;
|
||||
|
||||
pub trait Obj: Debug + Sync + Send {
|
||||
fn attrs(&self) -> AttrsPtr;
|
||||
|
||||
fn as_any(&self) -> &(dyn Any + 'static);
|
||||
|
||||
fn get(&self, key: &str) -> Option<ObjPtr> {
|
||||
let attrs_ptr = self.attrs();
|
||||
let attrs = attrs_ptr.read().unwrap();
|
||||
attrs.get(key).cloned()
|
||||
}
|
||||
|
||||
fn set(&mut self, key: String, value: ObjPtr) -> Option<ObjPtr> {
|
||||
let attrs_ptr = self.attrs();
|
||||
let mut attrs = attrs_ptr.write().unwrap();
|
||||
attrs.insert(key, value)
|
||||
}
|
||||
}
|
||||
39
src/obj/none.rs
Normal file
39
src/obj/none.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use crate::obj::prelude::*;
|
||||
use std::{any::Any, sync::LazyLock};
|
||||
|
||||
pub static NONE_INST: LazyLock<ObjPtr> = LazyLock::new(|| ObjPtr::new(None::new()));
|
||||
static NONE_STR: LazyLock<ObjPtr> = LazyLock::new(|| ObjPtr::new(Str::new("None")));
|
||||
|
||||
static NONE_ATTRS: LazyLock<AttrsPtr> = LazyLock::new(|| {
|
||||
attrs_ptr! {
|
||||
//
|
||||
"__str__" => builtin_fun_ptr!("__str__", |_vm, args, _state| {
|
||||
if args.len() != 1 {
|
||||
todo!("Throw exception: arity error");
|
||||
}
|
||||
BuiltinFunResult::Return(Some(ObjPtr::clone(&NONE_STR)))
|
||||
}),
|
||||
}
|
||||
});
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct None {
|
||||
attrs: AttrsPtr,
|
||||
}
|
||||
|
||||
impl None {
|
||||
fn new() -> Self {
|
||||
let attrs = AttrsPtr::clone(&NONE_ATTRS);
|
||||
Self { attrs }
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for None {
|
||||
fn attrs(&self) -> AttrsPtr {
|
||||
AttrsPtr::clone(&self.attrs)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn Any + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
53
src/obj/str.rs
Normal file
53
src/obj/str.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use crate::obj::prelude::*;
|
||||
use std::{any::Any, sync::LazyLock};
|
||||
|
||||
pub type StrValue = String;
|
||||
|
||||
static STR_ATTRS: LazyLock<AttrsPtr> = LazyLock::new(|| {
|
||||
attrs_ptr! {
|
||||
//
|
||||
"__str__" => builtin_fun_ptr!("__str__", |_vm, mut args, _state| {
|
||||
if args.len() != 1 {
|
||||
todo!("Throw exception: arity error");
|
||||
}
|
||||
BuiltinFunResult::Return(Some(args.pop().unwrap()))
|
||||
}),
|
||||
}
|
||||
});
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Str {
|
||||
attrs: AttrsPtr,
|
||||
value: StrValue,
|
||||
}
|
||||
|
||||
impl Str {
|
||||
pub fn new(value: impl Into<StrValue>) -> Self {
|
||||
let attrs = AttrsPtr::clone(&STR_ATTRS);
|
||||
// TODO intern
|
||||
Str {
|
||||
attrs,
|
||||
value: value.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &StrValue {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for Str {
|
||||
fn attrs(&self) -> AttrsPtr {
|
||||
AttrsPtr::clone(&self.attrs)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn Any + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Str {
|
||||
fn to_string(&self) -> String {
|
||||
self.value().clone()
|
||||
}
|
||||
}
|
||||
50
src/obj/user_fun.rs
Normal file
50
src/obj/user_fun.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use crate::obj::prelude::*;
|
||||
use crate::vm::{inst::Inst, name::Name};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserFun {
|
||||
name: Option<Name>,
|
||||
params: Vec<Name>,
|
||||
locals: Vec<Name>,
|
||||
body: Vec<Inst>,
|
||||
attrs: AttrsPtr,
|
||||
}
|
||||
|
||||
impl UserFun {
|
||||
pub fn new(name: Option<Name>, params: Vec<Name>, locals: Vec<Name>, body: Vec<Inst>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
params,
|
||||
locals,
|
||||
body,
|
||||
attrs: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the name of this function, `None` if it is anonymous.
|
||||
pub fn name(&self) -> Option<Name> {
|
||||
self.name
|
||||
}
|
||||
|
||||
pub fn params(&self) -> &Vec<Name> {
|
||||
&self.params
|
||||
}
|
||||
|
||||
pub fn locals(&self) -> &Vec<Name> {
|
||||
&self.locals
|
||||
}
|
||||
|
||||
pub fn body(&self) -> &Vec<Inst> {
|
||||
&self.body
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for UserFun {
|
||||
fn attrs(&self) -> AttrsPtr {
|
||||
AttrsPtr::clone(&self.attrs)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user