Make #alias work.

This commit is contained in:
Condorra 2024-09-27 21:57:36 +10:00
parent ff840c04c2
commit a215133ea6
4 changed files with 231 additions and 165 deletions

View File

@ -3,9 +3,10 @@ use anyhow::Error;
use piccolo::{ use piccolo::{
async_callback::{AsyncSequence, Locals}, async_callback::{AsyncSequence, Locals},
meta_ops::{self, MetaResult}, meta_ops::{self, MetaResult},
stash::Fetchable,
Callback, Closure, Context, Executor, ExternError, FromValue, Function, IntoValue, Lua, Callback, Closure, Context, Executor, ExternError, FromValue, Function, IntoValue, Lua,
MetaMethod, Stack, StashedError, StashedExecutor, StashedFunction, StashedTable, StashedValue, MetaMethod, Stack, StashedError, StashedExecutor, StashedFunction, StashedValue, Table, Value,
Table, Value, Variadic, Variadic,
}; };
use yew::UseStateSetter; use yew::UseStateSetter;
@ -147,11 +148,21 @@ pub fn install_lua_globals(
.map_err(|_| Error::msg("Can't add command"))?; .map_err(|_| Error::msg("Can't add command"))?;
}; };
} }
macro_rules! register_stateless_command {
register_command!(alias); ($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!(close_mud);
register_command!(connect_mud); register_command!(connect_mud);
register_command!(create_match_table); register_stateless_command!(create_match_table);
register_command!(delete_mud); register_command!(delete_mud);
register_command!(echo); register_command!(echo);
register_command!(echo_frame); register_command!(echo_frame);
@ -210,6 +221,22 @@ pub fn install_lua_globals(
.map_err(|_| Error::msg("Can't add handler"))?; .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_line);
register_class_function!(mud_class_table, mudoutput_prompt); 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); let match_table_class_table = Table::new(&ctx);
classes_table.set(ctx, "match_table", match_table_class_table)?; classes_table.set(ctx, "match_table", match_table_class_table)?;
match_table_class_table.set(ctx, MetaMethod::Index, 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_stateless_class_function!(match_table_class_table, "add", match_table_add);
register_class_function!(match_table_class_table, "remove", match_table_remove); register_stateless_class_function!(
register_class_function!(match_table_class_table, "lua_table", match_table_lua_table); 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!( register_class_function!(
match_table_class_table, match_table_class_table,
"try_run_sub", "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, seq: &mut AsyncSequence,
obj: StashedTable, obj: T,
func_name: &'static str, func_name: &'static str,
arguments: &[StashedValue], arguments: &[StashedValue],
) -> Result<(), StashedError> { ) -> Result<(), StashedError>
where
for<'gcb> <T as Fetchable>::Fetched<'gcb>: IntoValue<'gcb>,
{
let call = seq.try_enter(|ctx, locals, _execution, mut stack| { let call = seq.try_enter(|ctx, locals, _execution, mut stack| {
let obj = locals.fetch(&obj); let obj = locals.fetch(&obj);
stack.consume(ctx)?; stack.consume(ctx)?;

View File

@ -1,23 +1,20 @@
use crate::{ use crate::{
echo_to_term_frame, id_intern::intern_id, GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell, echo_to_term_frame,
TermFrame, id_intern::intern_id,
match_table::{create_match_table, match_table_add},
GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell, TermFrame,
}; };
use gc_arena::{Gc, Rootable}; use gc_arena::{Gc, Rootable};
use piccolo::{ use piccolo::{
self, async_sequence, Callback, CallbackReturn, Context, FromValue, Function, IntoValue, 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 std::{rc::Rc, str};
use yew::UseStateSetter; use yew::UseStateSetter;
use super::call_checking_metatable; use super::call_checking_metatable;
pub fn alias<'gc, 'a>( pub fn alias<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> {
ctx: Context<'gc>,
_global_memo: &'a GlobalMemoCell,
_global_layout: &'a UseStateSetter<GlobalLayoutCell>,
) -> Callback<'gc> {
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
let info: Table = ctx.get_global("info")?; let info: Table = ctx.get_global("info")?;
let cur_frame: TermFrame = 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_tab = locals.stash(&ctx, frame_tab);
let frame = locals.stash(&ctx, frame); let frame = locals.stash(&ctx, frame);
async move { async move {
call_checking_metatable(&mut seq, frame_tab, "new", &[frame]).await?; call_checking_metatable::<StashedTable>(&mut seq, frame_tab, "new", &[frame])
.await?;
Ok(SequenceReturn::Return) 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)?; frame_tab.set(ctx, ctx.intern_static(b"frame"), frame)?;
let aliases_tab: Table = Table::new(&ctx); let seq = async_sequence(&ctx, |locals, mut seq| {
frame_tab.set(ctx, ctx.intern_static(b"aliases"), aliases_tab)?; 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::<Value>(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 frame = locals.stash(&ctx, frame);
let line = locals.stash(&ctx, line.into_value(ctx)); let line = locals.stash(&ctx, line.into_value(ctx));
async move { async move {
call_checking_metatable(&mut seq, frame, "input", &[line]).await?; call_checking_metatable::<StashedTable>(&mut seq, frame, "input", &[line]).await?;
Ok(SequenceReturn::Return) 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> { pub(super) fn frame_input<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) -> Callback<'gc> {
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
let frame_tab: Table = Table::from_value( let callback_return = {
ctx, let frame_tab: Table = Table::from_value(
stack ctx,
stack
.pop_front()
.ok_or_else(|| anyhow::Error::msg("classes.frame:input missing object!"))?,
)?;
let line: Value = stack
.pop_front() .pop_front()
.ok_or_else(|| anyhow::Error::msg("classes.frame:new missing object!"))?, .ok_or_else(|| anyhow::Error::msg("classes.frame:input missing line!"))?;
)?; stack.consume(ctx)?;
let line: Value = stack
.pop_front()
.ok_or_else(|| anyhow::Error::msg("classes.frame:new missing line!"))?;
stack.consume(ctx)?;
// Check for an alias match... let aliases: UserData = frame_tab.get(ctx, "aliases")?;
for (alias_match, alias_sub) in frame_tab.get::<&str, Table>(ctx, "aliases")?.iter() { let frame: Value = frame_tab.get(ctx, "frame")?;
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 linked_mud = frame_tab.get_value(ctx, ctx.intern_static(b"linked_mud")); let seq = async_sequence(&ctx, |locals, mut seq| {
if linked_mud.is_nil() { let aliases = locals.stash(&ctx, aliases);
return Ok(piccolo::CallbackReturn::Return); let line = locals.stash(&ctx, line);
} let frame_tab = locals.stash(&ctx, frame_tab);
let linked_mud: Table = Table::from_value(ctx, linked_mud)?; let frame = locals.stash(&ctx, frame);
async move {
call_checking_metatable::<StashedUserData>(
&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::<bool>(ctx)?,
locals.stash(&ctx, linked_mud),
linked_mud.is_nil(),
))
})?;
// linked_mud:mudinput_line(line) if alias_hit || linked_mud_nil {
let seq = async_sequence(&ctx, |locals, mut seq| { return Ok(SequenceReturn::Return);
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)
}
});
Ok(piccolo::CallbackReturn::Sequence(seq)) call_checking_metatable::<StashedValue>(
&mut seq,
linked_mud,
"mudinput_line",
&[line],
)
.await?;
Ok(SequenceReturn::Return)
}
});
Ok(piccolo::CallbackReturn::Sequence(seq))
};
callback_return
}) })
} }

View File

@ -204,7 +204,7 @@ pub(super) fn connect_mud<'gc>(
let seq = async_sequence(&ctx, |locals, mut seq| { let seq = async_sequence(&ctx, |locals, mut seq| {
let conntab = locals.stash(&ctx, conntab); let conntab = locals.stash(&ctx, conntab);
async move { async move {
call_checking_metatable(&mut seq, conntab, "new", &[]).await?; call_checking_metatable::<StashedTable>(&mut seq, conntab, "new", &[]).await?;
Ok(SequenceReturn::Return) Ok(SequenceReturn::Return)
} }
}); });
@ -319,7 +319,7 @@ pub(super) fn mudoutput<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) -
.collect(); .collect();
async move { async move {
for (func_name, params) in fns { for (func_name, params) in fns {
call_checking_metatable( call_checking_metatable::<StashedTable>(
&mut seq, &mut seq,
conntab.clone(), conntab.clone(),
func_name, func_name,
@ -401,7 +401,13 @@ pub(super) fn mudoutput_line<'gc>(
let line = locals.stash(&ctx, line.into_value(ctx)); let line = locals.stash(&ctx, line.into_value(ctx));
async move { async move {
for frameroute in frameroutes { for frameroute in frameroutes {
call_checking_metatable(&mut seq, frameroute, "route", &[line.clone()]).await?; call_checking_metatable::<StashedTable>(
&mut seq,
frameroute,
"route",
&[line.clone()],
)
.await?;
} }
Ok(SequenceReturn::Return) 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); let curr_frame = locals.stash(&ctx, curr_frame);
async move { async move {
call_checking_metatable(&mut seq, frameroute, "new", &[curr_frame]).await?; call_checking_metatable::<StashedTable>(&mut seq, frameroute, "new", &[curr_frame])
.await?;
Ok(SequenceReturn::Return) Ok(SequenceReturn::Return)
} }
}); });

View File

@ -8,12 +8,11 @@ use gc_arena::{Collect, GcRefLock, Rootable};
use itertools::Itertools; use itertools::Itertools;
use piccolo::{Callback, Context, IntoValue, Table, UserData, Value}; use piccolo::{Callback, Context, IntoValue, Table, UserData, Value};
use regex::Regex; use regex::Regex;
use yew::UseStateSetter;
use crate::{ use crate::{
lua_engine::frames::try_unwrap_frame, lua_engine::frames::try_unwrap_frame,
parsing::{parse_commands, quote_string, ArgumentGuard, ParsedArgument, ParsedCommand}, parsing::{parse_commands, quote_string, ArgumentGuard, ParsedArgument, ParsedCommand},
GlobalLayoutCell, GlobalMemoCell, GlobalMemoCell,
}; };
#[derive(Default, Debug, Collect)] #[derive(Default, Debug, Collect)]
@ -251,6 +250,87 @@ pub enum SubTextPart {
Variable(String), 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::<Rootable!['gcb => GcRefLock<'gcb, MatchSubTable>]>(
&ctx,
GcRefLock::new(&ctx, <MatchSubTable as Default>::default().into()),
);
let match_table_class: Table = ctx
.get_global::<Table>("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::<Rootable!['gcb => 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::<Rootable!['gcb => 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::<Rootable!['gcb => 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::<Rootable!['gcb => 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -452,97 +532,3 @@ mod tests {
.is_err()) .is_err())
} }
} }
pub fn create_match_table<'gc, 'a>(
ctx: Context<'gc>,
_global_memo: &'a GlobalMemoCell,
_global_layout: &'a UseStateSetter<GlobalLayoutCell>,
) -> Callback<'gc> {
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
let _: () = stack.consume(ctx)?;
let user_data = UserData::<'gc>::new::<Rootable!['gcb => GcRefLock<'gcb, MatchSubTable>]>(
&ctx,
GcRefLock::new(&ctx, <MatchSubTable as Default>::default().into()),
);
let match_table_class: Table = ctx
.get_global::<Table>("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::<Rootable!['gcb => 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::<Rootable!['gcb => 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::<Rootable!['gcb => 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::<Rootable!['gcb => 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)
})
}