From a215133ea64f571343b3ae387232efd4b7d48db0 Mon Sep 17 00:00:00 2001 From: Condorra Date: Fri, 27 Sep 2024 21:57:36 +1000 Subject: [PATCH] Make #alias work. --- src/lua_engine.rs | 60 ++++++++++--- src/lua_engine/frames.rs | 143 +++++++++++++++++++------------ src/lua_engine/muds.rs | 15 +++- src/match_table.rs | 178 ++++++++++++++++++--------------------- 4 files changed, 231 insertions(+), 165 deletions(-) diff --git a/src/lua_engine.rs b/src/lua_engine.rs index c287623..5be168d 100644 --- a/src/lua_engine.rs +++ b/src/lua_engine.rs @@ -3,9 +3,10 @@ use anyhow::Error; use piccolo::{ async_callback::{AsyncSequence, Locals}, meta_ops::{self, MetaResult}, + stash::Fetchable, Callback, Closure, Context, Executor, ExternError, FromValue, Function, IntoValue, Lua, - MetaMethod, Stack, StashedError, StashedExecutor, StashedFunction, StashedTable, StashedValue, - Table, Value, Variadic, + MetaMethod, Stack, StashedError, StashedExecutor, StashedFunction, StashedValue, Table, Value, + Variadic, }; use yew::UseStateSetter; @@ -147,11 +148,21 @@ pub fn install_lua_globals( .map_err(|_| Error::msg("Can't add command"))?; }; } - - register_command!(alias); + macro_rules! register_stateless_command { + ($sym: ident) => { + cmd_table + .set( + ctx, + ctx.intern_static(stringify!($sym).as_bytes()), + $sym(ctx), + ) + .map_err(|_| Error::msg("Can't add command"))?; + }; + } + register_stateless_command!(alias); register_command!(close_mud); register_command!(connect_mud); - register_command!(create_match_table); + register_stateless_command!(create_match_table); register_command!(delete_mud); register_command!(echo); register_command!(echo_frame); @@ -210,6 +221,22 @@ pub fn install_lua_globals( .map_err(|_| Error::msg("Can't add handler"))?; }; } + macro_rules! register_stateless_class_function { + ($class_table: ident, $sym: ident) => { + $class_table + .set( + ctx, + ctx.intern_static(stringify!($sym).as_bytes()), + $sym(ctx), + ) + .map_err(|_| Error::msg("Can't add handler"))?; + }; + ($class_table: ident, $name: literal, $sym: ident) => { + $class_table + .set(ctx, ctx.intern_static($name.as_bytes()), $sym(ctx)) + .map_err(|_| Error::msg("Can't add handler"))?; + }; + } register_class_function!(mud_class_table, mudoutput_line); register_class_function!(mud_class_table, mudoutput_prompt); @@ -254,9 +281,17 @@ pub fn install_lua_globals( let match_table_class_table = Table::new(&ctx); classes_table.set(ctx, "match_table", match_table_class_table)?; match_table_class_table.set(ctx, MetaMethod::Index, match_table_class_table)?; - register_class_function!(match_table_class_table, "add", match_table_add); - register_class_function!(match_table_class_table, "remove", match_table_remove); - register_class_function!(match_table_class_table, "lua_table", match_table_lua_table); + register_stateless_class_function!(match_table_class_table, "add", match_table_add); + register_stateless_class_function!( + match_table_class_table, + "remove", + match_table_remove + ); + register_stateless_class_function!( + match_table_class_table, + "lua_table", + match_table_lua_table + ); register_class_function!( match_table_class_table, "try_run_sub", @@ -312,12 +347,15 @@ pub fn prep_metaop_call<'gc, const N: usize>( } } -pub async fn call_checking_metatable<'gc, 'a>( +pub async fn call_checking_metatable<'gc, T: Fetchable>( seq: &mut AsyncSequence, - obj: StashedTable, + obj: T, func_name: &'static str, arguments: &[StashedValue], -) -> Result<(), StashedError> { +) -> Result<(), StashedError> +where + for<'gcb> ::Fetched<'gcb>: IntoValue<'gcb>, +{ let call = seq.try_enter(|ctx, locals, _execution, mut stack| { let obj = locals.fetch(&obj); stack.consume(ctx)?; diff --git a/src/lua_engine/frames.rs b/src/lua_engine/frames.rs index 0d8c70d..67fbb02 100644 --- a/src/lua_engine/frames.rs +++ b/src/lua_engine/frames.rs @@ -1,23 +1,20 @@ use crate::{ - echo_to_term_frame, id_intern::intern_id, GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell, - TermFrame, + echo_to_term_frame, + id_intern::intern_id, + match_table::{create_match_table, match_table_add}, + GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell, TermFrame, }; use gc_arena::{Gc, Rootable}; use piccolo::{ self, async_sequence, Callback, CallbackReturn, Context, FromValue, Function, IntoValue, - SequenceReturn, Table, UserData, Value, Variadic, + SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value, Variadic, }; -use regex::Regex; use std::{rc::Rc, str}; use yew::UseStateSetter; use super::call_checking_metatable; -pub fn alias<'gc, 'a>( - ctx: Context<'gc>, - _global_memo: &'a GlobalMemoCell, - _global_layout: &'a UseStateSetter, -) -> Callback<'gc> { +pub fn alias<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> { Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { let info: Table = ctx.get_global("info")?; let cur_frame: TermFrame = @@ -43,11 +40,20 @@ pub fn alias<'gc, 'a>( ))?; } - let aliases: Table = cur_frame.get(ctx, "aliases")?; + let aliases: UserData = cur_frame.get(ctx, "aliases")?; - aliases.set(ctx, alias_match, sub_to)?; + stack.push_back(aliases.into_value(ctx)); + stack.push_back(alias_match.into_value(ctx)); + stack.push_back(sub_to.into_value(ctx)); + let seq = async_sequence(&ctx, |locals, mut seq| { + let add_func = locals.stash(&ctx, Function::Callback(match_table_add(ctx))); + async move { + seq.call(&add_func, 0).await?; + Ok(SequenceReturn::Return) + } + }); - Ok(piccolo::CallbackReturn::Return) + Ok(piccolo::CallbackReturn::Sequence(seq)) }) } @@ -229,7 +235,8 @@ pub fn ensure_frame_instance<'gc>(ctx: Context<'gc>, frame: &TermFrame) -> Callb let frame_tab = locals.stash(&ctx, frame_tab); let frame = locals.stash(&ctx, frame); async move { - call_checking_metatable(&mut seq, frame_tab, "new", &[frame]).await?; + call_checking_metatable::(&mut seq, frame_tab, "new", &[frame]) + .await?; Ok(SequenceReturn::Return) } }); @@ -251,10 +258,21 @@ pub(super) fn new_frame<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) - frame_tab.set(ctx, ctx.intern_static(b"frame"), frame)?; - let aliases_tab: Table = Table::new(&ctx); - frame_tab.set(ctx, ctx.intern_static(b"aliases"), aliases_tab)?; + let seq = async_sequence(&ctx, |locals, mut seq| { + let frame_tab = locals.stash(&ctx, frame_tab); + let create_match_tab = locals.stash(&ctx, Function::Callback(create_match_table(ctx))); + async move { + seq.call(&create_match_tab, 0).await?; + seq.try_enter(|ctx, locals, _exec, mut stack| { + let frame_tab = locals.fetch(&frame_tab); + frame_tab.set(ctx, "aliases", stack.consume::(ctx)?)?; + Ok(()) + })?; + Ok(piccolo::SequenceReturn::Return) + } + }); - Ok(piccolo::CallbackReturn::Return) + Ok(piccolo::CallbackReturn::Sequence(seq)) }) } @@ -268,7 +286,7 @@ pub(super) fn send_command_to_frame<'gc>(ctx: Context<'gc>) -> Callback<'gc> { let frame = locals.stash(&ctx, frame); let line = locals.stash(&ctx, line.into_value(ctx)); async move { - call_checking_metatable(&mut seq, frame, "input", &[line]).await?; + call_checking_metatable::(&mut seq, frame, "input", &[line]).await?; Ok(SequenceReturn::Return) } }); @@ -278,47 +296,64 @@ pub(super) fn send_command_to_frame<'gc>(ctx: Context<'gc>) -> Callback<'gc> { pub(super) fn frame_input<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) -> Callback<'gc> { Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { - let frame_tab: Table = Table::from_value( - ctx, - stack + let callback_return = { + let frame_tab: Table = Table::from_value( + ctx, + stack + .pop_front() + .ok_or_else(|| anyhow::Error::msg("classes.frame:input missing object!"))?, + )?; + let line: Value = stack .pop_front() - .ok_or_else(|| anyhow::Error::msg("classes.frame:new missing object!"))?, - )?; - let line: Value = stack - .pop_front() - .ok_or_else(|| anyhow::Error::msg("classes.frame:new missing line!"))?; - stack.consume(ctx)?; + .ok_or_else(|| anyhow::Error::msg("classes.frame:input missing line!"))?; + stack.consume(ctx)?; - // Check for an alias match... - for (alias_match, alias_sub) in frame_tab.get::<&str, Table>(ctx, "aliases")?.iter() { - if let Some(alias_match) = piccolo::String::from_value(ctx, alias_match) - .ok() - .and_then(|am| am.to_str().ok()) - .and_then(|v| Regex::new(v).ok()) - { - if let Some(alias_sub) = piccolo::String::from_value(ctx, alias_sub) - .ok() - .and_then(|am| am.to_str().ok()) - {} - } - } + let aliases: UserData = frame_tab.get(ctx, "aliases")?; + let frame: Value = frame_tab.get(ctx, "frame")?; - let linked_mud = frame_tab.get_value(ctx, ctx.intern_static(b"linked_mud")); - if linked_mud.is_nil() { - return Ok(piccolo::CallbackReturn::Return); - } - let linked_mud: Table = Table::from_value(ctx, linked_mud)?; + let seq = async_sequence(&ctx, |locals, mut seq| { + let aliases = locals.stash(&ctx, aliases); + let line = locals.stash(&ctx, line); + let frame_tab = locals.stash(&ctx, frame_tab); + let frame = locals.stash(&ctx, frame); + async move { + call_checking_metatable::( + &mut seq, + aliases.clone(), + "try_run_sub", + &[line.clone(), frame], + ) + .await?; + let (alias_hit, linked_mud, linked_mud_nil) = + seq.try_enter(|ctx, locals, _ex, mut stack| { + let linked_mud = locals + .fetch(&frame_tab) + .get_value(ctx, ctx.intern_static(b"linked_mud")); + Ok(( + stack.consume::(ctx)?, + locals.stash(&ctx, linked_mud), + linked_mud.is_nil(), + )) + })?; - // linked_mud:mudinput_line(line) - let seq = async_sequence(&ctx, |locals, mut seq| { - let linked_mud = locals.stash(&ctx, linked_mud); - let line = locals.stash(&ctx, line); - async move { - call_checking_metatable(&mut seq, linked_mud, "mudinput_line", &[line]).await?; - Ok(SequenceReturn::Return) - } - }); + if alias_hit || linked_mud_nil { + return Ok(SequenceReturn::Return); + } - Ok(piccolo::CallbackReturn::Sequence(seq)) + call_checking_metatable::( + &mut seq, + linked_mud, + "mudinput_line", + &[line], + ) + .await?; + + Ok(SequenceReturn::Return) + } + }); + + Ok(piccolo::CallbackReturn::Sequence(seq)) + }; + callback_return }) } diff --git a/src/lua_engine/muds.rs b/src/lua_engine/muds.rs index c8b06bd..b147189 100644 --- a/src/lua_engine/muds.rs +++ b/src/lua_engine/muds.rs @@ -204,7 +204,7 @@ pub(super) fn connect_mud<'gc>( let seq = async_sequence(&ctx, |locals, mut seq| { let conntab = locals.stash(&ctx, conntab); async move { - call_checking_metatable(&mut seq, conntab, "new", &[]).await?; + call_checking_metatable::(&mut seq, conntab, "new", &[]).await?; Ok(SequenceReturn::Return) } }); @@ -319,7 +319,7 @@ pub(super) fn mudoutput<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) - .collect(); async move { for (func_name, params) in fns { - call_checking_metatable( + call_checking_metatable::( &mut seq, conntab.clone(), func_name, @@ -401,7 +401,13 @@ pub(super) fn mudoutput_line<'gc>( let line = locals.stash(&ctx, line.into_value(ctx)); async move { for frameroute in frameroutes { - call_checking_metatable(&mut seq, frameroute, "route", &[line.clone()]).await?; + call_checking_metatable::( + &mut seq, + frameroute, + "route", + &[line.clone()], + ) + .await?; } Ok(SequenceReturn::Return) } @@ -494,7 +500,8 @@ pub(super) fn new_mud<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) -> let curr_frame = locals.stash(&ctx, curr_frame); async move { - call_checking_metatable(&mut seq, frameroute, "new", &[curr_frame]).await?; + call_checking_metatable::(&mut seq, frameroute, "new", &[curr_frame]) + .await?; Ok(SequenceReturn::Return) } }); diff --git a/src/match_table.rs b/src/match_table.rs index bbed430..5f28dbe 100644 --- a/src/match_table.rs +++ b/src/match_table.rs @@ -8,12 +8,11 @@ use gc_arena::{Collect, GcRefLock, Rootable}; use itertools::Itertools; use piccolo::{Callback, Context, IntoValue, Table, UserData, Value}; use regex::Regex; -use yew::UseStateSetter; use crate::{ lua_engine::frames::try_unwrap_frame, parsing::{parse_commands, quote_string, ArgumentGuard, ParsedArgument, ParsedCommand}, - GlobalLayoutCell, GlobalMemoCell, + GlobalMemoCell, }; #[derive(Default, Debug, Collect)] @@ -251,6 +250,87 @@ pub enum SubTextPart { Variable(String), } +pub fn create_match_table<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> { + Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { + let _: () = stack.consume(ctx)?; + let user_data = UserData::<'gc>::new:: GcRefLock<'gcb, MatchSubTable>]>( + &ctx, + GcRefLock::new(&ctx, ::default().into()), + ); + let match_table_class: Table = ctx + .get_global::("classes")? + .get(ctx, "match_table")?; + user_data.set_metatable(&ctx, Some(match_table_class)); + + stack.push_back(user_data.into_value(ctx)); + Ok(piccolo::CallbackReturn::Return) + }) +} + +pub fn match_table_add<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> { + Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { + let (match_table, match_text, sub_text): (UserData, piccolo::String, piccolo::String) = + stack.consume(ctx)?; + match_table + .downcast:: GcRefLock<'gcb, MatchSubTable>]>()? + .borrow_mut(&ctx) + .add_record(match_text.to_str()?, sub_text.to_str()?)?; + Ok(piccolo::CallbackReturn::Return) + }) +} + +pub fn match_table_remove<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> { + Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { + let (match_table, match_text): (UserData, piccolo::String) = stack.consume(ctx)?; + match_table + .downcast:: GcRefLock<'gcb, MatchSubTable>]>()? + .borrow_mut(&ctx) + .remove_record(match_text.to_str()?)?; + Ok(piccolo::CallbackReturn::Return) + }) +} + +pub fn match_table_lua_table<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> { + Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { + let match_table: UserData = stack.consume(ctx)?; + stack.push_back( + match_table + .downcast:: GcRefLock<'gcb, MatchSubTable>]>()? + .borrow_mut(&ctx) + .to_value(ctx)?, + ); + Ok(piccolo::CallbackReturn::Return) + }) +} + +pub fn match_table_try_run_sub<'gc, 'a>( + ctx: Context<'gc>, + global_memo: &'a GlobalMemoCell, +) -> Callback<'gc> { + let global_memo = global_memo.clone(); + Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { + let (match_table, sub, frame): (UserData, piccolo::String, Value) = stack.consume(ctx)?; + let frame = try_unwrap_frame(ctx, &frame)?; + + let cmds = match_table + .downcast:: GcRefLock<'gcb, MatchSubTable>]>()? + .borrow() + .try_sub(sub.to_str()?); + + match cmds { + None => stack.push_back(false.into_value(ctx)), + Some(cmds) => { + let mut cq = global_memo.command_queue.borrow_mut(); + for cmd in cmds.into_iter().rev() { + cq.push_front((frame.clone(), cmd)); + } + stack.push_back(Value::Boolean(true)) + } + } + Ok(piccolo::CallbackReturn::Return) + }) +} + #[cfg(test)] mod tests { use super::*; @@ -452,97 +532,3 @@ mod tests { .is_err()) } } - -pub fn create_match_table<'gc, 'a>( - ctx: Context<'gc>, - _global_memo: &'a GlobalMemoCell, - _global_layout: &'a UseStateSetter, -) -> Callback<'gc> { - Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { - let _: () = stack.consume(ctx)?; - let user_data = UserData::<'gc>::new:: GcRefLock<'gcb, MatchSubTable>]>( - &ctx, - GcRefLock::new(&ctx, ::default().into()), - ); - let match_table_class: Table = ctx - .get_global::
("classes")? - .get(ctx, "match_table")?; - user_data.set_metatable(&ctx, Some(match_table_class)); - - stack.push_back(user_data.into_value(ctx)); - Ok(piccolo::CallbackReturn::Return) - }) -} - -pub fn match_table_add<'gc, 'a>( - ctx: Context<'gc>, - _global_memo: &'a GlobalMemoCell, -) -> Callback<'gc> { - Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { - let (match_table, match_text, sub_text): (UserData, piccolo::String, piccolo::String) = - stack.consume(ctx)?; - match_table - .downcast:: GcRefLock<'gcb, MatchSubTable>]>()? - .borrow_mut(&ctx) - .add_record(match_text.to_str()?, sub_text.to_str()?)?; - Ok(piccolo::CallbackReturn::Return) - }) -} - -pub fn match_table_remove<'gc, 'a>( - ctx: Context<'gc>, - _global_memo: &'a GlobalMemoCell, -) -> Callback<'gc> { - Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { - let (match_table, match_text): (UserData, piccolo::String) = stack.consume(ctx)?; - match_table - .downcast:: GcRefLock<'gcb, MatchSubTable>]>()? - .borrow_mut(&ctx) - .remove_record(match_text.to_str()?)?; - Ok(piccolo::CallbackReturn::Return) - }) -} - -pub fn match_table_lua_table<'gc, 'a>( - ctx: Context<'gc>, - _global_memo: &'a GlobalMemoCell, -) -> Callback<'gc> { - Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { - let match_table: UserData = stack.consume(ctx)?; - stack.push_back( - match_table - .downcast:: GcRefLock<'gcb, MatchSubTable>]>()? - .borrow_mut(&ctx) - .to_value(ctx)?, - ); - Ok(piccolo::CallbackReturn::Return) - }) -} - -pub fn match_table_try_run_sub<'gc, 'a>( - ctx: Context<'gc>, - global_memo: &'a GlobalMemoCell, -) -> Callback<'gc> { - let global_memo = global_memo.clone(); - Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { - let (match_table, sub, frame): (UserData, piccolo::String, Value) = stack.consume(ctx)?; - let frame = try_unwrap_frame(ctx, &frame)?; - - let cmds = match_table - .downcast:: GcRefLock<'gcb, MatchSubTable>]>()? - .borrow() - .try_sub(sub.to_str()?); - - match cmds { - None => stack.push_back(false.into_value(ctx)), - Some(cmds) => { - let mut cq = global_memo.command_queue.borrow_mut(); - for cmd in cmds.into_iter().rev() { - cq.push_front((frame.clone(), cmd)); - } - stack.push_back(Value::Boolean(true)) - } - } - Ok(piccolo::CallbackReturn::Return) - }) -}