diff options
Diffstat (limited to 'lua-bindings/src/lib.rs')
-rw-r--r-- | lua-bindings/src/lib.rs | 482 |
1 files changed, 449 insertions, 33 deletions
diff --git a/lua-bindings/src/lib.rs b/lua-bindings/src/lib.rs index fcf307b..daf4b48 100644 --- a/lua-bindings/src/lib.rs +++ b/lua-bindings/src/lib.rs | |||
@@ -1,40 +1,149 @@ | |||
1 | use std::{sync::atomic::{AtomicUsize, Ordering}, cell::RefCell}; | 1 | use std::{ |
2 | any::Any, | ||
3 | cell::RefCell, | ||
4 | sync::mpsc::{sync_channel, Receiver, SyncSender}, | ||
5 | thread, | ||
6 | }; | ||
2 | 7 | ||
3 | use diaphragm_cairo_renderer::CairoRenderer; | 8 | use diaphragm_cairo_renderer::CairoRenderer; |
4 | use diaphragm_core::{ | 9 | use diaphragm_core::{ |
10 | core_shapes::{CoreDrawable, CoreShape, Rectangle as CoreRectangle}, | ||
5 | solving::VariableHandle, | 11 | solving::VariableHandle, |
6 | text::{FontDescription as CoreFontDescription, FontStyle, FontWeight, Text as CoreText}, | 12 | text::{FontDescription as CoreFontDescription, FontStyle, FontWeight, Text as CoreText}, |
7 | types::Float as CoreFloat, | 13 | types::{Bool as CoreBool, CoreShapeContext, Float as CoreFloat}, |
8 | Runtime, | 14 | Runtime, |
9 | }; | 15 | }; |
10 | use diaphragm_z3_solver::{z3, Z3Context}; | 16 | use diaphragm_z3_solver::{z3, Z3Context}; |
11 | use mlua::prelude::*; | 17 | use mlua::prelude::*; |
12 | 18 | ||
13 | static MAX_ID: AtomicUsize = AtomicUsize::new(0); | ||
14 | |||
15 | thread_local! { | ||
16 | static SENDER: RefCell<Option<Runtime<'static>>> = RefCell::new(None); | ||
17 | } | ||
18 | |||
19 | #[derive(Clone, Copy, Debug)] | 19 | #[derive(Clone, Copy, Debug)] |
20 | struct Float(CoreFloat); | 20 | struct Float(CoreFloat); |
21 | 21 | ||
22 | impl Float { | 22 | impl Float { |
23 | fn new() -> Float { | 23 | fn new() -> Float { |
24 | Float(CoreFloat::Variable(VariableHandle::new( | 24 | Float(runtime_thread_do(Box::new(|r| { |
25 | MAX_ID.fetch_add(1, Ordering::SeqCst), | 25 | r.solver_ctx().new_free_float() |
26 | ))) | 26 | }))) |
27 | } | ||
28 | } | ||
29 | impl LuaUserData for Float { | ||
30 | fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { | ||
31 | macro_rules! float_method { | ||
32 | ($name: expr, $op: ident) => { | ||
33 | methods.add_method($name, |_lua, lhs: &Float, rhs: LuaValue| { | ||
34 | let lhs = *lhs; | ||
35 | let rhs = Float::try_from(rhs).unwrap(); | ||
36 | Ok(Bool(runtime_thread_do(Box::new(move |r| { | ||
37 | r.solver_ctx().$op(lhs.0, rhs.0) | ||
38 | })))) | ||
39 | }); | ||
40 | }; | ||
41 | } | ||
42 | |||
43 | float_method!("eq", float_eq); | ||
44 | float_method!("ne", float_ne); | ||
45 | float_method!("gt", float_gt); | ||
46 | float_method!("ge", float_ge); | ||
47 | float_method!("lt", float_lt); | ||
48 | float_method!("le", float_le); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | impl TryFrom<LuaValue<'_>> for Float { | ||
53 | type Error = LuaError; | ||
54 | |||
55 | fn try_from(value: LuaValue) -> Result<Self, Self::Error> { | ||
56 | match value { | ||
57 | LuaValue::Integer(i) => Ok(Float(CoreFloat::Fixed(i as _))), | ||
58 | LuaValue::Number(f) => Ok(Float(CoreFloat::Fixed(f))), | ||
59 | // Create a new float from the borrow, since it might already be borrowed, with for ex. | ||
60 | // f:eq(f) | ||
61 | LuaValue::UserData(u) => Ok(Float(u.borrow::<Float>().unwrap().0)), | ||
62 | _ => Err(LuaError::FromLuaConversionError { | ||
63 | from: "Value", | ||
64 | to: "Float", | ||
65 | message: Some("Only int, float, or float() values are allowed".to_string()), | ||
66 | }), | ||
67 | } | ||
27 | } | 68 | } |
28 | } | 69 | } |
29 | impl LuaUserData for Float {} | ||
30 | 70 | ||
31 | fn float(_: &Lua, _: ()) -> LuaResult<Float> { | 71 | fn float(_: &Lua, _: ()) -> LuaResult<Float> { |
32 | Ok(Float::new()) | 72 | Ok(Float::new()) |
33 | } | 73 | } |
34 | 74 | ||
75 | #[derive(Clone, Copy, Debug)] | ||
76 | struct Bool(CoreBool); | ||
77 | |||
78 | impl LuaUserData for Bool { | ||
79 | fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { | ||
80 | macro_rules! bool_method { | ||
81 | ($name: expr, $op: ident) => { | ||
82 | methods.add_method($name, |_lua, lhs: &Bool, rhs: LuaValue| { | ||
83 | let lhs = *lhs; | ||
84 | let rhs = Bool::try_from(rhs).unwrap(); | ||
85 | Ok(Bool(runtime_thread_do(Box::new(move |r| { | ||
86 | r.solver_ctx().$op(lhs.0, rhs.0) | ||
87 | })))) | ||
88 | }); | ||
89 | }; | ||
90 | } | ||
91 | |||
92 | bool_method!("eq", bool_eq); | ||
93 | bool_method!("implies", bool_implies); | ||
94 | |||
95 | methods.add_method("and", |_lua, lhs: &Bool, rhs: LuaValue| { | ||
96 | let lhs = *lhs; | ||
97 | let rhs = Bool::try_from(rhs).unwrap(); | ||
98 | Ok(Bool(runtime_thread_do(Box::new(move |r| { | ||
99 | r.solver_ctx().bool_and(&[lhs.0, rhs.0]) | ||
100 | })))) | ||
101 | }); | ||
102 | |||
103 | methods.add_method("or", |_lua, lhs: &Bool, rhs: LuaValue| { | ||
104 | let lhs = *lhs; | ||
105 | let rhs = Bool::try_from(rhs).unwrap(); | ||
106 | Ok(Bool(runtime_thread_do(Box::new(move |r| { | ||
107 | r.solver_ctx().bool_or(&[lhs.0, rhs.0]) | ||
108 | })))) | ||
109 | }); | ||
110 | |||
111 | methods.add_method("no", |_lua, b: &Bool, _: ()| { | ||
112 | let b = *b; | ||
113 | Ok(Bool(runtime_thread_do(Box::new(move |r| { | ||
114 | r.solver_ctx().bool_not(b.0) | ||
115 | })))) | ||
116 | }); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | impl TryFrom<LuaValue<'_>> for Bool { | ||
121 | type Error = LuaError; | ||
122 | |||
123 | fn try_from(value: LuaValue) -> Result<Self, Self::Error> { | ||
124 | match value { | ||
125 | LuaValue::Boolean(b) => Ok(Bool(runtime_thread_do(Box::new(move |r| { | ||
126 | r.solver_ctx().new_fixed_bool(b) | ||
127 | })))), | ||
128 | // Create a new bool from the borrow, since it might already be borrowed, with for ex. | ||
129 | // b:eq(b) | ||
130 | LuaValue::UserData(u) => Ok(Bool(u.borrow::<Bool>().unwrap().0)), | ||
131 | _ => Err(LuaError::FromLuaConversionError { | ||
132 | from: "Value", | ||
133 | to: "Bool", | ||
134 | message: Some("Only bool, or bool() values are allowed".to_string()), | ||
135 | }), | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | |||
35 | #[derive(Clone, Debug)] | 140 | #[derive(Clone, Debug)] |
36 | struct FontDescription(CoreFontDescription); | 141 | struct FontDescription(CoreFontDescription); |
37 | impl LuaUserData for FontDescription {} | 142 | impl LuaUserData for FontDescription { |
143 | fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { | ||
144 | fields.add_field_method_get("size", |_, this| Ok(Float(this.0.size))) | ||
145 | } | ||
146 | } | ||
38 | 147 | ||
39 | const DEFAULT_FONT_FAMILY: &str = "serif"; | 148 | const DEFAULT_FONT_FAMILY: &str = "serif"; |
40 | 149 | ||
@@ -68,9 +177,10 @@ fn font(_: &Lua, params: LuaTable) -> LuaResult<FontDescription> { | |||
68 | Some(_) => return Err(LuaError::RuntimeError("Unknown weight".to_string())), | 177 | Some(_) => return Err(LuaError::RuntimeError("Unknown weight".to_string())), |
69 | }; | 178 | }; |
70 | 179 | ||
71 | let size = params | 180 | let size = match params.get::<_, Option<LuaValue>>("size")? { |
72 | .get::<_, Option<_>>("size")? | 181 | Some(f) => Float::try_from(f)?, |
73 | .unwrap_or_else(Float::new); | 182 | None => Float::new(), |
183 | }; | ||
74 | 184 | ||
75 | Ok(FontDescription(CoreFontDescription { | 185 | Ok(FontDescription(CoreFontDescription { |
76 | family, | 186 | family, |
@@ -80,46 +190,272 @@ fn font(_: &Lua, params: LuaTable) -> LuaResult<FontDescription> { | |||
80 | })) | 190 | })) |
81 | } | 191 | } |
82 | 192 | ||
193 | fn new_shape_context() -> CoreShapeContext { | ||
194 | runtime_thread_do(Box::new(|r| CoreShapeContext::new(r.solver_ctx()))) | ||
195 | } | ||
196 | |||
197 | #[derive(Debug, Clone)] | ||
198 | struct Drawable<T> | ||
199 | where | ||
200 | T: Into<CoreShape>, | ||
201 | { | ||
202 | shape: T, | ||
203 | context: CoreShapeContext, | ||
204 | } | ||
205 | |||
206 | trait HasContext { | ||
207 | fn get_context(&self) -> &CoreShapeContext; | ||
208 | } | ||
209 | |||
210 | fn add_bound_fields<'lua, T, F>(fields: &mut F) | ||
211 | where | ||
212 | T: LuaUserData, | ||
213 | T: HasContext, | ||
214 | F: LuaUserDataFields<'lua, T>, | ||
215 | { | ||
216 | macro_rules! bound_field { | ||
217 | ($name: expr, $method: ident) => { | ||
218 | fields.add_field_method_get($name, |_, this| { | ||
219 | let bounds = this.get_context().bounds().clone(); | ||
220 | Ok(Float(runtime_thread_do(Box::new(move |r| { | ||
221 | bounds.$method(r.solver_ctx()) | ||
222 | })))) | ||
223 | }) | ||
224 | }; | ||
225 | } | ||
226 | |||
227 | bound_field!("top", top); | ||
228 | bound_field!("left", left); | ||
229 | bound_field!("width", width); | ||
230 | bound_field!("height", height); | ||
231 | bound_field!("right", right); | ||
232 | bound_field!("bottom", bottom); | ||
233 | bound_field!("vert_center", vert_center); | ||
234 | bound_field!("horiz_center", horiz_center); | ||
235 | } | ||
236 | |||
237 | impl<T: Into<CoreShape>> From<Drawable<T>> for CoreDrawable { | ||
238 | fn from(value: Drawable<T>) -> Self { | ||
239 | CoreDrawable::new(value.shape.into(), value.context) | ||
240 | } | ||
241 | } | ||
242 | |||
83 | #[derive(Clone, Debug)] | 243 | #[derive(Clone, Debug)] |
84 | struct Text(CoreText); | 244 | struct Text(Drawable<CoreText>); |
85 | impl LuaUserData for Text { | 245 | impl LuaUserData for Text { |
86 | fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { | 246 | fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { |
87 | methods.add_method("draw", |_, this, _params: ()| { | 247 | methods.add_method("draw", |_, this, _params: ()| { |
88 | println!("I'm drawing: {}", this.0.content); | 248 | let drawable = this.0.clone().into(); |
249 | runtime_thread_do(Box::new(|r| { | ||
250 | r.add_drawable(drawable); | ||
251 | })); | ||
89 | Ok(()) | 252 | Ok(()) |
90 | }) | 253 | }) |
91 | } | 254 | } |
255 | |||
256 | fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { | ||
257 | add_bound_fields(fields); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | impl HasContext for Text { | ||
262 | fn get_context(&self) -> &CoreShapeContext { | ||
263 | &self.0.context | ||
264 | } | ||
92 | } | 265 | } |
93 | 266 | ||
94 | fn text(_: &Lua, params: LuaTable) -> LuaResult<Text> { | 267 | fn text(_: &Lua, params: LuaTable) -> LuaResult<Text> { |
95 | let content = params.get("content")?; | 268 | let content: String = params.get("content")?; |
96 | 269 | ||
97 | let font = params | 270 | let font = params |
98 | .get::<_, Option<FontDescription>>("font")? | 271 | .get::<_, Option<FontDescription>>("font")? |
99 | .unwrap_or_default(); | 272 | .unwrap_or_default(); |
100 | 273 | ||
101 | Ok(Text(CoreText { | 274 | let context = new_shape_context(); |
102 | content, | 275 | |
103 | font: font.0, | 276 | let content_2 = content.clone(); |
277 | let font_2 = font.0.clone(); | ||
278 | let context_2 = context.clone(); | ||
279 | |||
280 | runtime_thread_do(Box::new(move |r| { | ||
281 | let (text_width, text_height) = r.renderer().text_extents(&content_2, &font_2); | ||
282 | let solver = r.solver_ctx(); | ||
283 | // let scale = solver.float_div(font.0.size, CoreFloat::Fixed(height)); | ||
284 | |||
285 | let calculated_width = solver.float_mul(&[CoreFloat::Fixed(text_width), font.0.size]); | ||
286 | let bounds_width = context_2.bounds().width(solver); | ||
287 | let width_constraint = solver.float_eq(bounds_width, calculated_width); | ||
288 | solver.constrain(width_constraint); | ||
289 | |||
290 | let calculated_height = solver.float_mul(&[CoreFloat::Fixed(text_height), font.0.size]); | ||
291 | let bounds_height = context_2.bounds().height(solver); | ||
292 | let height_constraint = solver.float_eq(bounds_height, calculated_height); | ||
293 | solver.constrain(height_constraint); | ||
294 | })); | ||
295 | |||
296 | Ok(Text(Drawable { | ||
297 | shape: CoreText { | ||
298 | content, | ||
299 | font: font.0, | ||
300 | }, | ||
301 | context, | ||
302 | })) | ||
303 | } | ||
304 | |||
305 | #[derive(Clone, Debug)] | ||
306 | struct Rectangle(Drawable<CoreRectangle>); | ||
307 | impl LuaUserData for Rectangle { | ||
308 | fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { | ||
309 | methods.add_method("draw", |_, this, _params: ()| { | ||
310 | let drawable = this.0.clone().into(); | ||
311 | runtime_thread_do(Box::new(|r| { | ||
312 | r.add_drawable(drawable); | ||
313 | })); | ||
314 | Ok(()) | ||
315 | }) | ||
316 | } | ||
317 | |||
318 | fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { | ||
319 | add_bound_fields(fields); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | impl HasContext for Rectangle { | ||
324 | fn get_context(&self) -> &CoreShapeContext { | ||
325 | &self.0.context | ||
326 | } | ||
327 | } | ||
328 | |||
329 | fn rectangle(_: &Lua, _params: ()) -> LuaResult<Rectangle> { | ||
330 | Ok(Rectangle(Drawable { | ||
331 | shape: CoreRectangle {}, | ||
332 | context: new_shape_context(), | ||
104 | })) | 333 | })) |
105 | } | 334 | } |
106 | 335 | ||
336 | thread_local! { | ||
337 | static SENDER: RefCell<Option<SyncSender<Message>>> = RefCell::new(None); | ||
338 | static REPLY: RefCell<Option<Receiver<Reply>>> = RefCell::new(None); | ||
339 | static REPLIER: RefCell<Option<SyncSender<Reply>>> = RefCell::new(None); | ||
340 | } | ||
341 | |||
342 | enum Message { | ||
343 | Do(Box<dyn FnOnce(&mut Runtime) + Send>), | ||
344 | } | ||
345 | |||
346 | fn runtime_thread_do<T: Any + Send>(fun: Box<dyn FnOnce(&mut Runtime) -> T + Send>) -> T { | ||
347 | SENDER.with(|sender| { | ||
348 | sender | ||
349 | .borrow_mut() | ||
350 | .as_mut() | ||
351 | .expect("Not currently drawing") | ||
352 | .send(Message::Do(Box::new(|r| { | ||
353 | let ret = fun(r); | ||
354 | REPLIER.with(|replier| { | ||
355 | replier | ||
356 | .borrow_mut() | ||
357 | .as_mut() | ||
358 | .unwrap() | ||
359 | .send(Reply::Any(Box::new(ret))) | ||
360 | .unwrap(); | ||
361 | }); | ||
362 | }))) | ||
363 | .unwrap(); | ||
364 | }); | ||
365 | |||
366 | REPLY.with(|reply| { | ||
367 | let Reply::Any(any) = reply | ||
368 | .borrow_mut() | ||
369 | .as_mut() | ||
370 | .expect("Not currently drawing") | ||
371 | .recv() | ||
372 | .unwrap(); | ||
373 | *any.downcast().unwrap() | ||
374 | }) | ||
375 | } | ||
376 | |||
377 | enum Reply { | ||
378 | Any(Box<dyn Any + Send>), | ||
379 | } | ||
380 | |||
381 | fn constrain(_: &Lua, bool: Bool) -> LuaResult<()> { | ||
382 | runtime_thread_do(Box::new(move |r| r.solver_ctx().constrain(bool.0))); | ||
383 | Ok(()) | ||
384 | } | ||
385 | |||
107 | fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> { | 386 | fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> { |
108 | let content: LuaTable = params.get("content")?; | 387 | // So.... The Z3 stuff isn't Send and contains lifetimes, so we can't store them in global |
109 | let output: LuaTable = params.get("output")?; | 388 | // variables or convert them to Lua. Solution: handle everything in a specific thread, and |
389 | // communicate through a channel. | ||
390 | thread::scope(|s| { | ||
391 | let (message_sender, message_receiver) = sync_channel(1); | ||
392 | let (reply_sender, reply_receiver) = sync_channel(1); | ||
393 | |||
394 | SENDER.with(|f| { | ||
395 | *f.borrow_mut() = Some(message_sender); | ||
396 | }); | ||
397 | |||
398 | REPLY.with(|f| { | ||
399 | *f.borrow_mut() = Some(reply_receiver); | ||
400 | }); | ||
110 | 401 | ||
111 | let z3_cfg = z3::Config::new(); | 402 | s.spawn(move || { |
112 | let z3_ctx = z3::Context::new(&z3_cfg); | 403 | REPLIER.with(|f| { |
113 | let ctx = Z3Context::new(&z3_ctx); | 404 | *f.borrow_mut() = Some(reply_sender.clone()); |
405 | }); | ||
114 | 406 | ||
115 | let cairo_renderer = CairoRenderer::new(); | 407 | let z3_cfg = z3::Config::new(); |
408 | let z3_ctx = z3::Context::new(&z3_cfg); | ||
409 | let ctx = Z3Context::new(&z3_ctx); | ||
116 | 410 | ||
117 | // TODO: we shouldn't need the renderer until the end | 411 | let cairo_renderer = CairoRenderer::new(); |
118 | let mut runtime = Runtime::new(Box::new(ctx), Box::new(cairo_renderer)); | ||
119 | 412 | ||
120 | let _solver = runtime.solver_ctx(); | 413 | // TODO: we shouldn't need the renderer until the end |
414 | let mut runtime = Runtime::new(Box::new(ctx), Box::new(cairo_renderer)); | ||
121 | 415 | ||
122 | dbg!(content, output); | 416 | for message in message_receiver { |
417 | match message { | ||
418 | Message::Do(fun) => { | ||
419 | fun(&mut runtime); | ||
420 | } | ||
421 | } | ||
422 | } | ||
423 | |||
424 | runtime.render(); | ||
425 | }); | ||
426 | |||
427 | let content: LuaValue = params.get("content").unwrap(); | ||
428 | let _output: LuaTable = params.get("output").unwrap(); | ||
429 | |||
430 | match content { | ||
431 | LuaValue::Table(table) => { | ||
432 | let () = table.call_method("draw", ()).unwrap(); | ||
433 | } | ||
434 | // TODO: switch to enum | ||
435 | // LuaValue::UserData(user_data) => { | ||
436 | // if user_data.is::<Text>() { | ||
437 | // let text = user_data | ||
438 | // .borrow::<Text>() | ||
439 | // .expect("Couldn't borrow Text user data"); | ||
440 | // | ||
441 | // SENDER.with(|f| { | ||
442 | // f.borrow_mut() | ||
443 | // .as_ref() | ||
444 | // .expect("Not currently drawing") | ||
445 | // .send(Message::AddDrawable(CoreShape::Text(text.0.clone()))) | ||
446 | // .expect("Could not send shape"); | ||
447 | // }) | ||
448 | // } else { | ||
449 | // panic!("Non-drawable passed to draw"); | ||
450 | // } | ||
451 | // } | ||
452 | _ => panic!("Non-drawable passed to draw"), | ||
453 | } | ||
454 | |||
455 | SENDER.with(|s| { | ||
456 | *s.borrow_mut() = None; | ||
457 | }); | ||
458 | }); | ||
123 | 459 | ||
124 | Ok(()) | 460 | Ok(()) |
125 | } | 461 | } |
@@ -128,11 +464,91 @@ fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> { | |||
128 | fn libdiaphragm(lua: &Lua) -> LuaResult<LuaTable> { | 464 | fn libdiaphragm(lua: &Lua) -> LuaResult<LuaTable> { |
129 | // TODO: the solver as a mutable global solves so much problem (pun not intended) | 465 | // TODO: the solver as a mutable global solves so much problem (pun not intended) |
130 | let exports = lua.create_table()?; | 466 | let exports = lua.create_table()?; |
467 | exports.set("float", lua.create_function(float)?)?; | ||
468 | |||
131 | exports.set("text", lua.create_function(text)?)?; | 469 | exports.set("text", lua.create_function(text)?)?; |
132 | exports.set("font", lua.create_function(font)?)?; | 470 | exports.set("font", lua.create_function(font)?)?; |
133 | exports.set("float", lua.create_function(float)?)?; | ||
134 | 471 | ||
472 | exports.set("rectangle", lua.create_function(rectangle)?)?; | ||
473 | |||
474 | exports.set("constrain", lua.create_function(constrain)?)?; | ||
135 | exports.set("draw", lua.create_function(draw)?)?; | 475 | exports.set("draw", lua.create_function(draw)?)?; |
136 | 476 | ||
477 | // Setting up metatables | ||
478 | // --- | ||
479 | |||
480 | let float_metatable = lua | ||
481 | .create_userdata(Float(CoreFloat::Fixed(0.))) | ||
482 | .unwrap() | ||
483 | .get_metatable() | ||
484 | .unwrap(); | ||
485 | |||
486 | macro_rules! float_metamethod { | ||
487 | ($method: ident, $op: ident) => { | ||
488 | float_metatable | ||
489 | .set( | ||
490 | LuaMetaMethod::$method, | ||
491 | lua.create_function(|_lua, (lhs, rhs): (Float, LuaValue)| { | ||
492 | let rhs = Float::try_from(rhs).unwrap(); | ||
493 | Ok(Float(runtime_thread_do(Box::new(move |r| { | ||
494 | r.solver_ctx().$op(&[lhs.0, rhs.0]) | ||
495 | })))) | ||
496 | }) | ||
497 | .unwrap(), | ||
498 | ) | ||
499 | .unwrap(); | ||
500 | }; | ||
501 | } | ||
502 | |||
503 | float_metamethod!(Add, float_add); | ||
504 | float_metamethod!(Sub, float_sub); | ||
505 | float_metamethod!(Mul, float_mul); | ||
506 | |||
507 | float_metatable | ||
508 | .set( | ||
509 | LuaMetaMethod::Div, | ||
510 | lua.create_function(|_lua, (lhs, rhs): (Float, LuaValue)| { | ||
511 | let rhs = Float::try_from(rhs).unwrap(); | ||
512 | Ok(Float(runtime_thread_do(Box::new(move |r| { | ||
513 | r.solver_ctx().float_div(lhs.0, rhs.0) | ||
514 | })))) | ||
515 | }) | ||
516 | .unwrap(), | ||
517 | ) | ||
518 | .unwrap(); | ||
519 | |||
520 | float_metatable | ||
521 | .set( | ||
522 | LuaMetaMethod::Unm, | ||
523 | lua.create_function(|_lua, f: Float| { | ||
524 | Ok(Float(runtime_thread_do(Box::new(move |r| { | ||
525 | r.solver_ctx().float_neg(f.0) | ||
526 | })))) | ||
527 | }) | ||
528 | .unwrap(), | ||
529 | ) | ||
530 | .unwrap(); | ||
531 | |||
532 | // Not the operators `==`, `!=`, `<`, `<=`, `>`, and `>=` because Lua converts result to bool | ||
533 | |||
534 | let bool_metatable = lua | ||
535 | .create_userdata(Bool(CoreBool::new(VariableHandle::new(0)))) | ||
536 | .unwrap() | ||
537 | .get_metatable() | ||
538 | .unwrap(); | ||
539 | |||
540 | bool_metatable | ||
541 | .set( | ||
542 | LuaMetaMethod::Shl, | ||
543 | lua.create_function(|_lua, (lhs, rhs): (Bool, LuaValue)| { | ||
544 | let rhs = Bool::try_from(rhs).unwrap(); | ||
545 | Ok(Bool(runtime_thread_do(Box::new(move |r| { | ||
546 | r.solver_ctx().bool_implies(lhs.0, rhs.0) | ||
547 | })))) | ||
548 | }) | ||
549 | .unwrap(), | ||
550 | ) | ||
551 | .unwrap(); | ||
552 | |||
137 | Ok(exports) | 553 | Ok(exports) |
138 | } | 554 | } |