rulox/
lib.rs

1//! # rulox
2//!
3//! [![crates.io](https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust)](https://crates.io/crates/rulox)
4//! [![github](https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github)](https://github.com/Spartan2909/rulox)
5//! [![docs.rs](https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs)](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}