ksl/lib.rs
1//! # KSL core library
2//!
3//! [![github]](https://github.com/kands-code/rswk)
4//! [![crates-io]](https://crates.io/crates/ksl)
5//! [![docs-rs]](https://docs.rs/ksl)
6//!
7//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
8//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
9//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
10//!
11//! # Description
12//!
13//! This library provides a comprehensive set of functions
14//! for tokenizing and executing KSL code.
15
16mod builtin;
17pub mod eval;
18pub mod token;
19pub mod value;
20
21/// List of built-in functions.
22pub use builtin::BUILTIN_FUNCTIONS;
23/// Literal value corresponding to the error tag in Atom.
24pub use builtin::ERR_SYMBOL;
25/// Literal value corresponding to the boolean false in Atom.
26pub use builtin::FALSE_SYMBOL;
27/// Module name in environment as key's literal value.
28pub use builtin::MODULE_NAME_ENV;
29/// Imported module path list in environment as key's literal value.
30pub use builtin::MODULE_PATH_ENV;
31/// Literal value corresponding to the ok tag in Atom.
32pub use builtin::OK_SYMBOL;
33/// Literal value corresponding to the boolean true in Atom.
34pub use builtin::TRUE_SYMBOL;
35
36use crate::{builtin::ENV_ARGS_ENV, value::Value};
37
38/// Dict type in KSL
39///
40/// Currently using `FxHashMap` as the underlying implementation.
41pub type Dict = rustc_hash::FxHashMap<std::sync::Arc<str>, std::sync::Arc<Value>>;
42
43/// Scope represents a single level in the environment chain.
44#[derive(Debug)]
45pub struct Scope {
46 /// The variable store for this scope.
47 ///
48 /// Mutable and thread-safe.
49 pub store: parking_lot::RwLock<Dict>,
50 /// The parent scope.
51 ///
52 /// Immutable reference.
53 pub parent: Option<Environment>,
54}
55
56/// Environment type in KSL
57pub type Environment = std::sync::Arc<Scope>;
58
59/// Initial thread stack size.
60pub const INIT_STACK_SIZE: usize = 8 * 1024 * 1024;
61
62/// Initial Environment in KSL
63///
64/// Includes built-in functions and various constants in KSL.
65///
66/// # Example
67///
68/// ```rust
69/// let _ = ksl::init_environment(None);
70/// ```
71pub fn init_environment(env_args: Option<Value>) -> Environment {
72 let mut store = Dict::default();
73 // bifs
74 for &func in BUILTIN_FUNCTIONS.iter() {
75 store.insert(
76 std::sync::Arc::from(func),
77 std::sync::Arc::new(Value::Builtin(func)),
78 );
79 }
80 // module loading paths
81 store.insert(
82 MODULE_PATH_ENV.clone(),
83 std::sync::Arc::new(Value::Object(MODULE_PATH_ENV.clone(), Box::default())),
84 );
85 // environment variables
86 if let Some(val) = env_args
87 && let Value::List(_) = val
88 {
89 store.insert(ENV_ARGS_ENV.clone(), std::sync::Arc::new(val));
90 }
91 // construct environment
92 std::sync::Arc::new(Scope {
93 store: parking_lot::RwLock::new(store),
94 parent: None,
95 })
96}
97
98/// Find a variable in the environment chain.
99pub fn read_from_environment(env: &Environment, key: &str) -> Option<std::sync::Arc<Value>> {
100 let mut current = env;
101 loop {
102 // try currnet scope
103 if let Some(val) = current.store.upgradable_read().get(key) {
104 return Some(val.clone());
105 }
106 // find in parent scope
107 match ¤t.parent {
108 Some(parent) => {
109 current = parent;
110 }
111 None => {
112 // otherwise
113 return None;
114 }
115 }
116 }
117}
118
119/// Find the environment where the variable is defined.
120pub fn find_define_environment(env: &Environment, key: &str) -> Option<Environment> {
121 let mut current = env;
122 loop {
123 // try currnet scope
124 if current.store.upgradable_read().contains_key(key) {
125 return Some(current.clone());
126 }
127 // find in parent scope
128 match current.parent.as_ref() {
129 Some(parent) => {
130 current = parent;
131 }
132 None => {
133 // otherwise
134 return None;
135 }
136 }
137 }
138}
139
140/// Check if two numbers are equal.
141pub fn is_number_eq(n1: f64, n2: f64) -> bool { (n1 == n2) || (n1 - n2).abs() <= n1.abs().max(n2.abs()) * f64::EPSILON }
142
143/// Expand the tilde at the start of a path.
144pub fn expand_tilde(path: std::sync::Arc<str>) -> Result<std::path::PathBuf, std::sync::Arc<str>> {
145 match path.strip_prefix("~/") {
146 Some(rest) => std::env::home_dir()
147 .ok_or_else(|| {
148 std::sync::Arc::from(
149 concat!(
150 "Error[ksl::expand_tilde]: ",
151 "Failed to get user home directory path."
152 )
153 .to_string(),
154 )
155 })
156 .map(|p| p.join(rest)),
157 None => Ok(std::path::PathBuf::from(path.as_ref())),
158 }
159}