rulox/lib.rs
1//! # rulox
2//!
3//! [](https://crates.io/crates/rulox)
4//! [](https://github.com/Spartan2909/rulox)
5//! [](https://docs.rs/rulox/latest) <br>
6//!
7//! `rulox` is a lightweight scripting language embedded in Rust.
8//! It is based on the Lox language from [Crafting Interpreters](https://craftinginterpreters.com/).
9//!
10//! # Examples
11//! ```
12//! use rulox::prelude::*;
13//! use rulox::LoxError;
14//!
15//! fn main() -> Result<(), LoxError> {
16//! lox! {
17//! var a = 5;
18//!
19//! print a + 2;
20//! }
21//!
22//! let b: f64 = a.get()?.try_into().unwrap();
23//!
24//! println!("{}", b);
25//!
26//! Ok(())
27//! }
28//! ```
29//!
30//! ```
31//! # use rulox::prelude::*;
32//! # use rulox::LoxError;
33//! # fn main() -> Result<(), LoxError> {
34//! lox! {
35//! for (var i = 5; i > 0; i = i - 1) print i;
36//! }
37//! # Ok(())
38//! # }
39//! ```
40//!
41//! ```
42//! # use rulox::prelude::*;
43//! # use rulox::LoxError;
44//! # fn main() -> Result<(), LoxError> {
45//! lox! {
46//! fun hello(name) {
47//! print "Hello " + name + "! :)";
48//! }
49//!
50//! fun add_one(num) {
51//! return num + 1;
52//! }
53//! }
54//!
55//! hello.get()?.call([LoxValue::from("Alice")].into());
56//!
57//! assert_eq!(add_one.get()?.call([LoxValue::from(3)].into())?, 4);
58//! # Ok(())
59//! # }
60//! ```
61//!
62//! ```
63//! # use rulox::prelude::*;
64//! # use rulox::LoxError;
65//! # fn main() -> Result<(), LoxError> {
66//! lox! {
67//! var people = ["Bob", "Alice", "John"];
68//!
69//! for (person in people) {
70//! print "Hello " + person + "!";
71//! }
72//! }
73//! # Ok(())
74//! # }
75//! ```
76//!
77//! ```
78//! # use rulox::prelude::*;
79//! # use rulox::LoxError;
80//! # fn main() -> Result<(), LoxError> {
81//! lox! {
82//! class Person {
83//! init(name) {
84//! this.name = name;
85//! }
86//!
87//! say_hello() {
88//! print "Hello, my name is " + this.name + "!";
89//! }
90//! }
91//!
92//! var jane = Person("Jane");
93//! jane.say_hello();
94//! }
95//! # Ok(())
96//! # }
97//! ```
98//!
99//! ```
100//! # use rulox::prelude::*;
101//! # use rulox::LoxError;
102//! # fn main() -> Result<(), LoxError> {
103//! lox! {
104//! class Person {
105//! init(name) {
106//! this.name = name;
107//! }
108//!
109//! say_hello() {
110//! print "Hello, my name is " + this.name + "!";
111//! }
112//! }
113//!
114//! class Telepath > Person {
115//! init(name, power) {
116//! super(name);
117//! this.power = power;
118//! }
119//!
120//! lift(weight) {
121//! if (this.power < weight) {
122//! print "It's too heavy!";
123//! } else if (this.power == weight) {
124//! print "I can't keep this up for long!";
125//! } else {
126//! print "This is child's play.";
127//! }
128//! }
129//! }
130//!
131//! var bob = Person("Bob");
132//! bob.say_hello();
133//!
134//! print "";
135//!
136//! var alice = Telepath("Alice", 4);
137//! alice.say_hello();
138//! alice.lift(1.5);
139//! alice.lift(4);
140//! alice.lift(10);
141//! }
142//! # Ok(())
143//! # }
144//! ```
145//!
146//! ```
147//! # use rulox::prelude::*;
148//! # use rulox::LoxError;
149//! # fn main() -> Result<(), LoxError> {
150//! lox! {
151//! var except_ran = false;
152//! var else_ran = false;
153//! var finally_ran = false;
154//! try {
155//! print "try";
156//! throw 1;
157//! } except {
158//! print "except";
159//! except_ran = true;
160//! } else {
161//! print "else";
162//! else_ran = true;
163//! } finally {
164//! print "finally";
165//! finally_ran = true;
166//! }
167//! }
168//! # Ok(())
169//! # }
170//! ```
171
172#![warn(missing_docs)]
173
174/// Parses Lox code and converts it to Rust.
175/// # Examples
176/// ```
177/// use rulox::prelude::*;
178/// use rulox::LoxVariable;
179/// # use rulox::LoxError;
180///
181/// # fn main() -> Result<(), LoxError> {
182/// lox! {
183/// var hello = "hello ";
184/// }
185///
186/// let world = LoxVariable::new("world");
187///
188/// lox! {
189/// print hello + world;
190/// }
191/// # Ok(())
192/// # }
193/// ```
194pub use rulox_macro::lox;
195
196/// Generates a rulox binding for a Rust function.
197///
198/// ## Examples
199///
200/// ```
201/// use rulox::prelude::*;
202/// use rulox::lox_bindgen;
203/// # use rulox::LoxError;
204///
205/// fn hello(name: String) -> String {
206/// "Hello ".to_string() + &name
207/// }
208///
209/// # fn main() -> Result<(), LoxError> {
210/// lox_bindgen!(fn hello(name) as lox_hello);
211///
212/// lox! {
213/// print lox_hello("Alice");
214/// }
215/// # Ok(())
216/// # }
217/// ```
218#[macro_export]
219macro_rules! lox_bindgen {
220 ( fn $rust_name:ident $( :: $segment:ident )* ( $( $arg:ident ),* ) ) => {
221 $crate::lox_bindgen!( fn $rust_name ( $( $arg ),* ) as $rust_name )
222 };
223 ( fn $rust_name:ident $( :: $segment:ident )* ( $( $arg:ident ),* ) as $lox_name:ident ) => {
224 let $lox_name = __rulox_helpers::LoxVariable::new(LoxValue::function(__rulox_helpers::LoxFn::new(
225 move |__args: $crate::LoxArgs| -> __rulox_helpers::LoxResult {
226 let ( $( $arg, )* ) = __args.extract()?;
227 $crate::ToLoxResult::to_lox_result($rust_name $( :: $segment )* ( $( $arg ),* ))
228 },
229 vec![$( stringify!($arg) ),*]
230 )));
231 };
232 ( async fn $rust_name:ident $( :: $segment:ident )* ( $( $arg:ident ),* ) ) => {
233 $crate::lox_bindgen!( async fn $rust_name ( $( $arg ),* ) as $rust_name )
234 };
235 ( async fn $rust_name:ident $( :: $segment:ident )* ( $( $arg:ident ),* ) as $lox_name:ident ) => {
236 let $lox_name = __rulox_helpers::LoxVariable::new(LoxValue::coroutine(
237 move |__args: $crate::LoxArgs| -> Box<dyn $crate::prelude::__rulox_helpers::Future<Output = $crate::LoxResult> + Send + Sync + 'static> {
238 Box::new(async move {
239 let ( $( $arg, )* ) = __args.extract()?;
240 $crate::ToLoxResult::to_lox_result($rust_name $( :: $segment )* ( $( $arg ),* ).await)
241 })
242 },
243 vec![$( stringify!($arg) ),*]
244 ));
245 };
246 {
247 $(
248 fn $sync_rust_name:ident $( :: $sync_segment:ident )* ( $( $sync_arg:ident ),* ) $( as $sync_lox_name:ident )? ;
249 )*
250 $(
251 async fn $async_rust_name:ident $( :: $async_segment:ident )* ( $( $async_arg:ident ),* ) $( as $async_lox_name:ident )? ;
252 )*
253 } => {
254 $(
255 $crate::lox_bindgen!( fn $sync_rust_name $( :: $sync_segment )* ( $( $sync_arg ),* ) $( as $sync_lox_name )? );
256 )*
257 $(
258 $crate::lox_bindgen!( async fn $async_rust_name $( :: $async_segment )* ( $( $async_arg ),* ) $( as $async_lox_name )? );
259 )*
260 };
261}
262
263/// Generates a Rust binding for a rulox function.
264///
265/// ## Examples
266///
267/// ```
268/// use rulox::prelude::*;
269/// use rulox::rust_bindgen;
270/// # use rulox::LoxError;
271///
272/// # fn main() -> Result<(), LoxError> {
273/// lox! {
274/// fun hello(name) {
275/// return "Hello " + name + "!";
276/// }
277/// }
278///
279/// rust_bindgen!(fn hello(name: &str) -> String as rust_hello);
280///
281/// assert_eq!(rust_hello("Bob"), Ok("Hello Bob!".to_string()));
282/// # Ok(())
283/// # }
284/// ```
285#[macro_export]
286macro_rules! rust_bindgen {
287 ( fn $lox_name:ident ( $( $arg_name:ident : $arg_ty:ty ),* ) -> $ret_ty:ty as $rust_name:ident ) => {
288 let $rust_name = {
289 let $lox_name = $lox_name.close_over();
290 move | $( $arg_name : $arg_ty ),* | -> $crate::prelude::__rulox_helpers::Result<$ret_ty, $crate::LoxError> {
291 $lox_name.get()?.call([ $( $arg_name.into() ),* ].into())?.try_into()
292 }
293 };
294 };
295}
296
297/// Generates an implementation of [`TryFrom<LoxValue>`].
298///
299/// This implementation casts the input to an external object, attempts to
300/// downcast it to this type, and then clones it.
301///
302/// If cloning is not desirable, [`Shared<T>`][rulox_types::Shared] where
303/// [`T: LoxObject`][rulox_types::LoxObject] implements [`TryFrom<LoxValue>`],
304/// and can be used instead.
305///
306/// This macro requires that the input type implement both
307/// [`LoxObject`] and [`Clone`].
308pub use rulox_macro::TryFromLoxValue;
309
310#[cfg(feature = "async")]
311pub use rulox_types::async_types::Coroutine;
312#[cfg(feature = "async")]
313pub use rulox_types::async_types::LoxFuture;
314pub use rulox_types::Downcast;
315pub use rulox_types::DynLoxObject;
316pub use rulox_types::LoxArgs;
317pub use rulox_types::LoxClass;
318pub use rulox_types::LoxError;
319pub use rulox_types::LoxFn;
320pub use rulox_types::LoxObject;
321pub use rulox_types::LoxResult;
322pub use rulox_types::LoxValue;
323pub use rulox_types::LoxVariable;
324pub use rulox_types::MapKey;
325pub use rulox_types::Shared;
326
327#[cfg(feature = "serialise")]
328pub use rulox_types::hashmap_to_json_map;
329
330#[doc(hidden)]
331pub use rulox_types::ToLoxResult;
332
333/// Items that the [`lox`] macro expects to find in scope.
334pub mod prelude {
335 pub use crate::lox;
336 pub use crate::LoxValue;
337
338 #[doc(hidden)] // Not public API.
339 pub mod __rulox_helpers {
340 pub use crate::LoxArgs;
341 pub use crate::LoxClass;
342 pub use crate::LoxError;
343 pub use crate::LoxFn;
344 pub use crate::LoxObject;
345 pub use crate::LoxResult;
346 pub use crate::LoxValue;
347 pub use crate::LoxVariable;
348 pub use core::result::Result;
349 pub use rulox_types::extract;
350 pub use rulox_types::read;
351 pub use rulox_types::write;
352 pub use rulox_types::LoxRc;
353 pub use rulox_types::Shared;
354 pub use std::collections::HashMap;
355
356 #[cfg(feature = "async")]
357 pub use core::future::Future;
358 #[cfg(feature = "async")]
359 pub use rulox_types::async_types::Coroutine;
360 }
361}