add some documentation

This commit is contained in:
Daniella / Tove 2023-02-25 15:02:31 +01:00
parent c9f9ba9979
commit 25a30a5be6
Signed by: TudbuT
GPG key ID: 7D63D5634B7C417F
10 changed files with 212 additions and 93 deletions

2
Cargo.lock generated
View file

@ -16,7 +16,7 @@ checksum = "b03f7fbd470aa8b3ad163c85cce8bccfc11cc9c44ef12da0a4eddd98bd307352"
[[package]]
name = "spl"
version = "0.0.2"
version = "0.0.3"
dependencies = [
"once_cell",
"readformat",

View file

@ -1,6 +1,6 @@
[package]
name = "spl"
version = "0.0.2"
version = "0.0.3"
edition = "2021"
description = "Stack Pogramming Language: A simple, concise scripting language."
license = "MIT"

View file

@ -197,7 +197,7 @@ pub fn dyn_read(stack: &mut Stack) -> OError {
kind: ErrorKind::LexError(format!("{x:?}")),
stack: stack.trace(),
})?),
run_at_base: false,
run_as_base: false,
origin: stack.get_frame(),
fname: None,
name: "(dyn-read)".to_owned(),
@ -224,7 +224,7 @@ pub fn dyn_readf(stack: &mut Stack) -> OError {
kind: ErrorKind::LexError(format!("{x:?}")),
stack: stack.trace(),
})?),
run_at_base: true,
run_as_base: true,
origin: stack.get_frame(),
fname: Some(n),
name: "root".to_owned(),
@ -267,7 +267,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
AFunc::new(Func {
ret_count: f.2,
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(wrap(f.1)))),
run_at_base: false,
run_as_base: false,
origin: o.clone(),
fname: None,
name: f.0.to_owned(),

View file

@ -56,7 +56,7 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option<u32>, Words, u
words.push(Word::Const(Value::Func(AFunc::new(Func {
ret_count: block.0.ok_or(LexerError::FunctionBlockExpected)?,
to_call: FuncImpl::SPL(block.1),
run_at_base: false,
run_as_base: false,
origin: Arc::new(Frame::dummy()),
fname: None,
name: "dyn".to_owned(),

View file

@ -1,97 +1,135 @@
//! # Using SPL as a crate
//!
//! SPL has a complete API for use in applications and libraries.
//! To start a file, use `start_file` with the path, which is relatively straightforward, just like
//! `start_file_in_runtime`, which is the same as `start_file` but doesn't create and set a
//! runtime.
//!
//! To start code more customizably, you will have to create a stack and a runtime yourself, then
//! call `add_std` to include the standard library.
//!
//! Example:
//! ```
//! use spl::*;
//! fn main() -> OError {
//! Runtime::new().set();
//! let mut stack = Stack::new();
//! add_std(&mut stack)?;
//! Words::new(vec![
//! Word::Const(Value::Str("Hello, World!".to_owned())),
//! Word::Call("println".to_owned(), /*pop result:*/ false, /*reference:*/ 0)
//! ]).exec(&mut stack);
//! Ok(())
//! }
//! ```
#![allow(clippy::type_complexity)]
#![allow(clippy::len_without_is_empty)]
pub mod stdlib;
pub mod dyn_fns;
pub mod lexer;
pub mod mutex;
pub mod runtime;
pub mod std_fns;
pub mod stdlib;
pub mod stream;
pub use lexer::*;
pub use runtime::*;
use std::fs;
use lexer::lex;
use runtime::*;
/// Creates a runtime, lexes and executes some SPL code from a file, returning the stack that was
/// used for the operations, which should be empty in most cases.
pub fn start_file(path: &str) -> Result<Stack, Error> {
Runtime::new().set();
(start_file_in_runtime(path), Runtime::reset()).0
}
/// TO START A STANDALONE PIECE OF CODE, USE start_file!!
/// Lexes and starts some SPL code from a file, returning the stack.
pub fn start_file_in_runtime(path: &str) -> Result<Stack, Error> {
let mut stack = Stack::new_in(FrameInfo {
file: "std.spl".to_owned(),
function: "root".to_owned(),
});
let mut stack = Stack::new();
// import stdlib
let f = find_in_splpath("std.spl");
let words =
lex(if let Ok(f) = f { fs::read_to_string(f).unwrap() } else { f.unwrap_err() }).map_err(|x| Error {
kind: ErrorKind::LexError(format!("{x:?}")),
stack: Vec::new(),
})?;
words.exec(&mut stack)?;
add_std(&mut stack)?;
// run file
Words {
words: vec![
Word::Const(Value::Str(path.to_owned())),
Word::Call("call-main-on-file".to_owned(), false, 0),
],
}
Words::new(vec![
Word::Const(Value::Str(path.to_owned())),
Word::Call("call-main-on-file".to_owned(), false, 0),
])
.exec(&mut stack)?;
Ok(stack)
}
#[macro_export]
macro_rules! require_on_stack {
($name:tt, $type:tt, $stack:expr, $fn:literal) => {
let Value::$type($name)
= $stack.pop().lock_ro().native.clone() else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
/// Inćlude the standard library in a runtime-stack-pair, where the runtime has been .set().
pub fn add_std(stack: &mut Stack) -> OError {
let f = find_in_splpath("std.spl");
let words = lex(if let Ok(f) = f {
fs::read_to_string(f).unwrap()
} else {
f.unwrap_err()
})
.map_err(|x| Error {
kind: ErrorKind::LexError(format!("{x:?}")),
stack: Vec::new(),
})?;
words.exec(stack)
}
macro_rules! nofmt {
{$($code:tt)*} => {
$($code)*
};
}
// rustfmt adds infinite indentation to this, incrementing every time it is run.
nofmt! {
#[macro_export]
macro_rules! require_on_stack {
($name:tt, $type:tt, $stack:expr, $fn:literal) => {
let Value::$type($name) = $stack.pop().lock_ro().native.clone() else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
};
};
};
}
#[macro_export]
macro_rules! require_int_on_stack {
($name:tt, $stack:expr, $fn:literal) => {
let Value::Int($name)
= $stack.pop().lock_ro().native.clone().try_mega_to_int() else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
}
#[macro_export]
macro_rules! require_int_on_stack {
($name:tt, $stack:expr, $fn:literal) => {
let Value::Int($name) = $stack.pop().lock_ro().native.clone().try_mega_to_int() else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
};
};
};
}
#[macro_export]
macro_rules! require_array {
($name:tt, $array:expr, $stack:expr, $fn:literal) => {
let Value::Array(ref $name)
= $array.lock_ro().native else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
}
#[macro_export]
macro_rules! require_array {
($name:tt, $array:expr, $stack:expr, $fn:literal) => {
let Value::Array(ref $name) = $array.lock_ro().native else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
};
};
};
}
#[macro_export]
macro_rules! require_mut_array {
($name:tt, $array:expr, $stack:expr, $fn:literal) => {
let Value::Array(ref mut $name)
= $array.lock().native else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
}
#[macro_export]
macro_rules! require_mut_array {
($name:tt, $array:expr, $stack:expr, $fn:literal) => {
let Value::Array(ref mut $name) = $array.lock().native else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
};
};
};
}
#[macro_export]
macro_rules! require_array_on_stack {
($name:tt, $stack:expr, $fn:literal) => {
let binding = $stack.pop();
require_array!($name, binding, $stack, $fn)
};
}
#[macro_export]
macro_rules! require_mut_array_on_stack {
($name:tt, $stack:expr, $fn:literal) => {
let binding = $stack.pop();
require_mut_array!($name, binding, $stack, $fn)
};
}
#[macro_export]
macro_rules! require_array_on_stack {
($name:tt, $stack:expr, $fn:literal) => {
let binding = $stack.pop();
require_array!($name, binding, $stack, $fn)
};
}
#[macro_export]
macro_rules! require_mut_array_on_stack {
($name:tt, $stack:expr, $fn:literal) => {
let binding = $stack.pop();
require_mut_array!($name, binding, $stack, $fn)
};
}
}

View file

@ -1,4 +1,4 @@
use spl::{start_file};
use spl::start_file;
use std::env::args;

View file

@ -1,8 +1,8 @@
use crate::{
dyn_fns,
mutex::*,
std_fns,
stream::{self, *}, stdlib,
std_fns, stdlib,
stream::{self, *},
};
use core::panic;
@ -26,6 +26,7 @@ thread_local! {
static RUNTIME: RefCell<Option<Arc<Mut<Runtime>>>> = RefCell::new(None);
}
/// Obtains a reference to the runtime.
pub fn runtime<T>(f: impl FnOnce(RwLockReadGuard<Runtime>) -> T) -> T {
RUNTIME.with(|rt| {
f(rt.borrow_mut()
@ -35,6 +36,7 @@ pub fn runtime<T>(f: impl FnOnce(RwLockReadGuard<Runtime>) -> T) -> T {
})
}
/// Obtains a mutable reference to the runtime.
pub fn runtime_mut<T>(f: impl FnOnce(RwLockWriteGuard<Runtime>) -> T) -> T {
RUNTIME.with(|rt| {
f(rt.borrow_mut()
@ -44,6 +46,12 @@ pub fn runtime_mut<T>(f: impl FnOnce(RwLockWriteGuard<Runtime>) -> T) -> T {
})
}
/// An SPL runtime.
///
/// This holds:
/// - types
/// - type refs
/// - streams
#[derive(Clone)]
pub struct Runtime {
next_type_id: u32,
@ -139,6 +147,8 @@ impl Runtime {
}
}
/// Anything that can be .set() and result in the runtime being set.
/// Implemented for Arc<Mut<Runtime>> and Runtime.
pub trait SetRuntime {
fn set(self);
}
@ -155,12 +165,21 @@ impl SetRuntime for Arc<Mut<Runtime>> {
}
}
/// A frame's location in SPL code.
#[derive(Clone, Debug)]
pub struct FrameInfo {
pub file: String,
pub function: String,
}
/// An SPL stack frame.
///
/// This holds:
/// - its parent
/// - variables
/// - functions
/// - its origin ([FrameInfo])
/// - whether all functions in it should be made global.
#[derive(Clone, Debug)]
pub struct Frame {
parent: Option<Arc<Frame>>,
@ -211,7 +230,7 @@ impl Frame {
variables: Mut::new(HashMap::new()),
functions: Mut::new(HashMap::new()),
origin: FrameInfo {
file: "RUNTIME".to_owned(),
file: "std.spl".to_owned(),
function: "root".to_owned(),
},
redirect_to_base: false,
@ -331,6 +350,12 @@ impl Frame {
}
}
/// An SPL stack.
///
/// This holds:
/// - a stack of frames
/// - the main stack of objects
/// - a return accumultor: how many blocks to return directly from
#[derive(Clone, Debug)]
pub struct Stack {
frames: Vec<Arc<Frame>>,
@ -408,7 +433,7 @@ impl Stack {
func.origin.clone(),
cname.clone(),
func.name.clone(),
func.run_at_base,
func.run_as_base,
)
} else {
Frame::new(func.origin.clone(), func.name.clone())
@ -453,7 +478,7 @@ impl Stack {
stack.push(tmpframe.get_var(tmpname.clone(), stack)?);
Ok(())
}))),
run_at_base: false,
run_as_base: false,
fname: Some("RUNTIME".to_owned()),
name: name.clone(),
}),
@ -469,7 +494,7 @@ impl Stack {
let v = stack.pop();
tmpframe.set_var(tmpname.clone(), v, stack)
}))),
run_at_base: false,
run_as_base: false,
fname: Some("RUNTIME".to_owned()),
name: "=".to_owned() + &name,
}),
@ -568,6 +593,9 @@ impl Stack {
}
}
/// An SPL keyword. Used to deviate from normal linear code structure.
///
/// This is different from a [Word], which are any SPL code.
#[derive(Clone, Debug)]
pub enum Keyword {
/// <none>
@ -618,6 +646,9 @@ pub enum Keyword {
With(Vec<String>),
}
/// Any SPL value that is not a construct.
///
/// Holds its rust representation.
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Null,
@ -660,19 +691,32 @@ impl PartialOrd for Value {
}
}
/// The smallest fragment of SPL code.
#[derive(Clone, Debug)]
pub enum Word {
/// A keyword, used to deviate from normal code structure.
Key(Keyword),
/// A constant to push to the stack when encountered.
Const(Value),
/// A function call.
Call(String, bool, u32),
/// A method call.
ObjCall(String, bool, u32),
}
/// A collection of executable words.
#[derive(Clone, Debug)]
pub struct Words {
pub words: Vec<Word>,
}
impl Words {
pub fn new(words: Vec<Word>) -> Self {
Words { words }
}
}
/// Any kind of SPL-executable code.
#[derive(Clone)]
pub enum FuncImpl {
Native(fn(&mut Stack) -> OError),
@ -690,14 +734,24 @@ impl FuncImpl {
}
}
/// Any kind of SPL-executable code with metadata surrounding it.
///
/// This holds:
/// - the amount of values returned when called
/// - the actual executable code ([FuncImpl])
/// - the frame that defined it
/// - the name of the file it was defined in, if this is different form the definition frame
/// - the name of the function.
/// - wether it should be run as the root layer (= wether functions it defines should be made
/// global)
#[derive(Clone)]
pub struct Func {
pub ret_count: u32,
pub to_call: FuncImpl,
pub run_at_base: bool,
pub origin: Arc<Frame>,
pub fname: Option<String>,
pub name: String,
pub run_as_base: bool,
}
impl PartialEq for Func {
@ -713,6 +767,14 @@ impl Debug for Func {
}
}
/// Any SPL type.
///
/// This holds:
/// - the name
/// - the numeric ID
/// - its parent types
/// - its methods
/// - its fields
#[derive(Clone, Debug)]
pub struct Type {
name: String,
@ -785,8 +847,8 @@ impl Type {
);
Ok(())
}))),
run_at_base: false,
origin: origin.clone(),
run_as_base: false,
fname: Some("RUNTIME".to_owned()),
name: name.clone(),
}),
@ -802,8 +864,8 @@ impl Type {
o.lock().property_map.insert(tmpname.clone(), v);
Ok(())
}))),
run_at_base: false,
origin,
run_as_base: false,
fname: Some("RUNTIME".to_owned()),
name: "=".to_owned() + &name,
}),
@ -813,6 +875,12 @@ impl Type {
}
}
/// Any kind of SPL object, no matter if it is a construct or not.
///
/// This holds:
/// - the type of the object
/// - the fields mandated by the type
/// - the native value ([Value]), null for constructs unless set manually.
#[derive(Clone, Debug)]
pub struct Object {
pub kind: AMType,
@ -905,6 +973,7 @@ impl From<Value> for Object {
}
}
/// Trait for converting things to SPL Objects.
pub trait SPL {
fn spl(self) -> AMObject;
}
@ -918,6 +987,7 @@ where
}
}
/// Finds a file in the SPL_PATH, or returns the internal [stdlib] version of it.
pub fn find_in_splpath(path: &str) -> Result<String, String> {
if Path::new(path).exists() {
return Ok(path.to_owned());
@ -936,6 +1006,8 @@ pub fn find_in_splpath(path: &str) -> Result<String, String> {
}
impl Words {
/// Executes the words. This does *not* create a new frame on the stack. Use [Stack::call] to
/// call and create a new frame.
pub fn exec(&self, stack: &mut Stack) -> OError {
for word in self.words.clone() {
match word {
@ -947,8 +1019,8 @@ impl Words {
Arc::new(Func {
ret_count: rem,
to_call: FuncImpl::SPL(words),
run_at_base: false,
origin: stack.get_frame(),
run_as_base: false,
fname: None,
name,
}),
@ -970,8 +1042,8 @@ impl Words {
Arc::new(Func {
ret_count: v.0,
to_call: FuncImpl::SPL(v.1),
run_at_base: false,
origin: origin.clone(),
run_as_base: false,
fname: None,
name: name.clone() + ":" + &k,
}),
@ -1048,8 +1120,8 @@ impl Words {
stack.push(ftmp.clone().spl());
Ok(())
}))),
run_at_base: false,
origin: stack.get_frame(),
run_as_base: false,
fname: None,
name: s + &x,
}));
@ -1094,8 +1166,8 @@ impl Words {
stack.push(ftmp.clone().spl());
Ok(())
}))),
run_at_base: false,
origin: stack.get_frame(),
run_as_base: false,
fname: None,
name: s + &x,
}));
@ -1121,6 +1193,7 @@ impl Words {
}
}
/// Any error SPL can handle and throw.
#[derive(Debug, PartialEq, Eq)]
pub enum ErrorKind {
Parse(String, String),
@ -1137,6 +1210,7 @@ pub enum ErrorKind {
CustomObject(AMObject),
}
/// Wrapper for ErrorKind with the stack trace.
#[derive(PartialEq, Eq)]
pub struct Error {
pub kind: ErrorKind,

View file

@ -562,7 +562,12 @@ pub fn import(stack: &mut Stack) -> OError {
let Value::Str(mut s) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("import".to_owned()))
};
let fallback = match s.as_str().rsplit_once(|x| x == '/' || x == '#').map(|(.., x)| x).unwrap_or(s.as_str()) {
let fallback = match s
.as_str()
.rsplit_once(|x| x == '/' || x == '#')
.map(|(.., x)| x)
.unwrap_or(s.as_str())
{
"std.spl" => Some(stdlib::STD),
"iter.spl" => Some(stdlib::ITER),
"stream.spl" => Some(stdlib::STREAM),
@ -591,8 +596,8 @@ pub fn import(stack: &mut Stack) -> OError {
stack.push(Value::Str(fallback.to_owned()).spl());
Ok(())
} else {
Err(x)
}
Err(x)
}
})?;
dyn_fns::wrap(dyn_fns::dyn_readf)(stack)?;
call(stack)?;
@ -763,7 +768,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
AFunc::new(Func {
ret_count: f.2,
to_call: FuncImpl::Native(f.1),
run_at_base: false,
run_as_base: false,
origin: o.clone(),
fname: None,
name: f.0.to_owned(),

View file

@ -1,5 +1,3 @@
pub const STD: &str = include_str!("../std.spl");
pub const ITER: &str = include_str!("../iter.spl");
pub const STREAM: &str = include_str!("../stream.spl");

View file

@ -16,6 +16,7 @@ static STREAM_TYPES: Lazy<Arc<Mut<HashMap<String, StreamType>>>> =
Lazy::new(|| Arc::new(Mut::new(HashMap::new())));
static IS_INITIALIZED: Lazy<Arc<Mut<bool>>> = Lazy::new(|| Arc::new(Mut::new(false)));
/// Registers a custom stream type.
pub fn register_stream_type(
name: &str,
supplier: impl Fn(&mut Stack) -> Result<Stream, Error> + Sync + Send + 'static,
@ -25,10 +26,12 @@ pub fn register_stream_type(
.insert(name.to_owned(), StreamType::from(supplier));
}
/// Gets a stream type by name.
pub fn get_stream_type(name: String) -> Option<StreamType> {
STREAM_TYPES.lock_ro().get(&name).cloned()
}
/// An SPL stream type.
#[derive(Clone)]
pub struct StreamType {
func: Arc<Box<dyn Fn(&mut Stack) -> Result<Stream, Error> + Sync + Send + 'static>>,
@ -40,6 +43,7 @@ impl StreamType {
}
}
/// An SPL stream, holding a reader and a writer, and a function to close it.
pub struct Stream {
reader: Box<dyn Read + 'static>,
writer: Box<dyn Write + 'static>,
@ -303,7 +307,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
AFunc::new(Func {
ret_count: f.2,
to_call: FuncImpl::Native(f.1),
run_at_base: false,
run_as_base: false,
origin: o.clone(),
fname: None,
name: f.0.to_owned(),