Skip to content

Commit

Permalink
Move builtin init routine out from runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
sagebind committed May 11, 2019
1 parent b1d00a5 commit d9f36be
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 50 deletions.
54 changes: 40 additions & 14 deletions riptide/src/builtins.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
//! Implementations of built-in global functions that are always available.
use crate::modules;
use crate::prelude::*;
use crate::runtime::Scope;

pub fn init(runtime: &mut Runtime) {
runtime.globals().set("require", Value::ForeignFunction(modules::require));
runtime.globals().set("backtrace", Value::ForeignFunction(backtrace));
runtime.globals().set("call", Value::ForeignFunction(call));
runtime.globals().set("catch", Value::ForeignFunction(catch));
runtime.globals().set("def", Value::ForeignFunction(def));
runtime.globals().set("export", Value::ForeignFunction(export));
runtime.globals().set("include", Value::ForeignFunction(include));
runtime.globals().set("list", Value::ForeignFunction(list));
runtime.globals().set("nil", Value::ForeignFunction(nil));
runtime.globals().set("nth", Value::ForeignFunction(nth));
runtime.globals().set("set", Value::ForeignFunction(set));
runtime.globals().set("table", Value::ForeignFunction(table));
runtime.globals().set("table-set", Value::ForeignFunction(table_set));
runtime.globals().set("throw", Value::ForeignFunction(throw));
runtime.globals().set("typeof", Value::ForeignFunction(type_of));

runtime.globals().set("modules", Value::from(table! {
"loaders" => Value::List(Vec::new()),
"loaded" => Value::from(table!()),
}));
}

/// Binds a value to a new variable.
pub fn def(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn def(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
let name = match args.get(0).and_then(Value::as_string) {
Some(s) => s.clone(),
None => throw!("variable name required"),
Expand All @@ -15,7 +41,7 @@ pub fn def(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
Ok(Value::Nil)
}

pub fn set(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn set(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
let name = match args.get(0).and_then(Value::as_string) {
Some(s) => s.clone(),
None => throw!("variable name required"),
Expand All @@ -28,7 +54,7 @@ pub fn set(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
Ok(Value::Nil)
}

pub fn export(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn export(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
let name = match args.get(0).and_then(Value::as_string) {
Some(s) => s.clone(),
None => throw!("variable name to export required"),
Expand All @@ -43,16 +69,16 @@ pub fn export(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception>
}

/// Returns the name of the primitive type of the given arguments.
pub fn type_of(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn type_of(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
Ok(args.first().map(Value::type_name).map(Value::from).unwrap_or(Value::Nil))
}

/// Constructs a list from the given arguments.
pub fn list(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn list(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
Ok(Value::List(args.to_vec()))
}

pub fn nth(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn nth(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
let list = match args.get(0).and_then(Value::as_list) {
Some(s) => s.to_vec(),
None => throw!("first argument must be a list"),
Expand All @@ -67,7 +93,7 @@ pub fn nth(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
}

/// Constructs a table from the given arguments.
pub fn table(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn table(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
if args.len() & 1 == 1 {
throw!("an even number of arguments is required");
}
Expand All @@ -88,7 +114,7 @@ pub fn table(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
Ok(table.into())
}

pub fn table_set(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn table_set(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
let table = match args.get(0).and_then(Value::as_table) {
Some(s) => s.clone(),
None => throw!("first argument must be a table"),
Expand All @@ -107,19 +133,19 @@ pub fn table_set(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
}

/// Function that always returns Nil.
pub fn nil(_: &mut Runtime, _: &[Value]) -> Result<Value, Exception> {
fn nil(_: &mut Runtime, _: &[Value]) -> Result<Value, Exception> {
Ok(Value::Nil)
}

/// Throw an exception.
pub fn throw(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn throw(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
match args.first() {
Some(value) => Err(Exception::from(value.clone())),
None => Err(Exception::from(Value::Nil)),
}
}

pub fn call(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn call(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
if let Some(function) = args.first() {
let args = match args.get(1) {
Some(Value::List(args)) => &args[..],
Expand All @@ -133,7 +159,7 @@ pub fn call(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
}

/// Invoke a block. If the block throws an exception, catch it and return it.
pub fn catch(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn catch(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
if let Some(function) = args.first() {
match runtime.invoke(function, &[]) {
Ok(_) => Ok(Value::Nil),
Expand All @@ -144,12 +170,12 @@ pub fn catch(runtime: &mut Runtime, args: &[Value]) -> Result<Value, Exception>
}
}

pub fn include(_: &mut Runtime, _: &[Value]) -> Result<Value, Exception> {
fn include(_: &mut Runtime, _: &[Value]) -> Result<Value, Exception> {
throw!("not implemented");
}

/// Returns a backtrace of the call stack as a list of strings.
pub fn backtrace(runtime: &mut Runtime, _: &[Value]) -> Result<Value, Exception> {
fn backtrace(runtime: &mut Runtime, _: &[Value]) -> Result<Value, Exception> {
fn scope_to_value(scope: impl AsRef<Scope>) -> Value {
let scope = scope.as_ref();
Value::from(table! {
Expand Down
6 changes: 6 additions & 0 deletions riptide/src/init.rip
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
#
# This script must never throw an exception, or the runtime will panic.

# Pre-define the global modules table.
# table-set $GLOBALS modules [
# loaders: [] # A list of loader functions.
# loaded: [:] # A table mapping module names to the loaded object.
# ]

def lang (require lang)

table-set $GLOBALS print $lang->print
Expand Down
59 changes: 23 additions & 36 deletions riptide/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ impl Scope {

/// Configure a runtime.
pub struct RuntimeBuilder {
module_loaders: Vec<Value>,
globals: Table,
module_loaders: Vec<ForeignFunction>,
}

impl Default for RuntimeBuilder {
Expand All @@ -96,33 +95,12 @@ impl RuntimeBuilder {
pub fn new() -> Self {
Self {
module_loaders: Vec::new(),
globals: table! {
"require" => Value::ForeignFunction(modules::require),
"backtrace" => Value::ForeignFunction(builtins::backtrace),
"call" => Value::ForeignFunction(builtins::call),
"catch" => Value::ForeignFunction(builtins::catch),
"def" => Value::ForeignFunction(builtins::def),
"export" => Value::ForeignFunction(builtins::export),
"include" => Value::ForeignFunction(builtins::include),
"list" => Value::ForeignFunction(builtins::list),
"nil" => Value::ForeignFunction(builtins::nil),
"nth" => Value::ForeignFunction(builtins::nth),
"set" => Value::ForeignFunction(builtins::set),
"table" => Value::ForeignFunction(builtins::table),
"table-set" => Value::ForeignFunction(builtins::table_set),
"throw" => Value::ForeignFunction(builtins::throw),
"typeof" => Value::ForeignFunction(builtins::type_of),
"modules" => Value::from(table! {
"loaded" => Value::from(table!()),
"loaders" => Value::Nil,
}),
},
}
}

/// Register a module loader.
pub fn module_loader(mut self, loader: ForeignFunction) -> Self {
self.module_loaders.push(loader.into());
self.module_loaders.push(loader);
self
}

Expand All @@ -131,16 +109,27 @@ impl RuntimeBuilder {
}

pub fn build(self) -> Runtime {
self.globals.get("modules").as_table().unwrap().set("loaders", Value::List(self.module_loaders));

let mut runtime = Runtime {
globals: Rc::new(self.globals),
globals: Rc::new(Table::new()),
stack: Vec::new(),
is_exiting: false,
exit_code: 0,
};

runtime.init();
// Set up globals
runtime.globals.set("GLOBALS", runtime.globals.clone());
runtime.globals.set("env", env::vars().collect::<Table>()); // Isn't that easy?

// Initialize builtins
builtins::init(&mut runtime);

// Register predefined module loaders
for loader in self.module_loaders {
runtime.register_module_loader(loader);
}

// Execute initialization
runtime.execute(None, include_str!("init.rip")).expect("error in runtime initialization");

runtime
}
Expand Down Expand Up @@ -170,18 +159,16 @@ impl Default for Runtime {
}

impl Runtime {
/// Initialize the runtime environment.
fn init(&mut self) {
self.globals.set("GLOBALS", self.globals.clone());
self.globals.set("env", env::vars().collect::<Table>()); // Isn't that easy?

self.execute(None, include_str!("init.rip")).expect("error in runtime initialization");
}

pub fn builder() -> RuntimeBuilder {
RuntimeBuilder::new()
}

/// Register a module loader.
pub fn register_module_loader(&self, loader: ForeignFunction) {
let modules = self.globals.get("modules").as_table().unwrap();
modules.set("loaders", modules.get("loaders").append(loader).unwrap());
}

pub fn exit_code(&self) -> i32 {
self.exit_code
}
Expand Down
26 changes: 26 additions & 0 deletions riptide/src/stdlib/process.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::prelude::*;
use crate::process;
use std::process::Command;
use std::thread;
use std::time::Duration;

pub fn load() -> Result<Value, Exception> {
Ok(table! {
"command" => Value::ForeignFunction(command),
"exec" => Value::ForeignFunction(exec),
"sleep" => Value::ForeignFunction(sleep),
"spawn" => Value::ForeignFunction(spawn),
}
.into())
Expand Down Expand Up @@ -60,3 +63,26 @@ fn command(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
fn exec(_: &mut Runtime, _: &[Value]) -> Result<Value, Exception> {
unimplemented!();
}

/// Puts the current process to sleep for a given number of seconds.
fn sleep(_: &mut Runtime, args: &[Value]) -> Result<Value, Exception> {
if let Some(Value::Number(seconds)) = args.first() {
let seconds = *seconds;

let duration = if seconds.is_normal() && seconds > 0f64 {
Duration::new(
seconds.trunc() as u64,
(seconds.fract() * 1_000_000_000f64) as u32,
)
} else {
Duration::from_secs(0)
};

log::debug!("sleeping for {}ms", duration.as_millis());
thread::sleep(duration);

Ok(Value::Nil)
} else {
throw!("sleep duration required")
}
}
12 changes: 12 additions & 0 deletions riptide/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,18 @@ impl Value {
pub fn get(&self, key: impl AsRef<[u8]>) -> Value {
self.as_table().map(|t| t.get(key)).unwrap_or(Value::Nil)
}

/// If this is a list, return a new list with the given value appended.
pub fn append(&self, value: impl Into<Value>) -> Option<Value> {
match self {
Value::List(items) => {
let mut new = items.clone();
new.push(value.into());
Some(Value::List(new))
},
_ => None,
}
}
}

impl PartialEq for Value {
Expand Down

0 comments on commit d9f36be

Please sign in to comment.