feat: initial context preservation

This allows the REPL to now preserve context, such as previously
declared variables which can be used in future eval steps.

Currently is only supported for variables, support for other types to be
included soon.
This commit is contained in:
Erica Marigold 2023-08-09 21:49:42 +05:30
parent 508c2836e3
commit f9919a40aa
No known key found for this signature in database
GPG key ID: 23CD97ABBBCC5ED2
3 changed files with 80 additions and 11 deletions

26
Cargo.lock generated
View file

@ -198,6 +198,21 @@ version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "bit-set"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -634,6 +649,16 @@ dependencies = [
"str-buf",
]
[[package]]
name = "fancy-regex"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
dependencies = [
"bit-set",
"regex",
]
[[package]]
name = "fastrand"
version = "2.0.0"
@ -1087,6 +1112,7 @@ dependencies = [
"directories",
"dunce",
"env_logger 0.10.0",
"fancy-regex",
"futures-util",
"glam",
"home",

View file

@ -132,6 +132,7 @@ regex = { optional = true, version = "1.7", default-features = false, features =
"std",
"unicode-perl",
] }
fancy-regex = "0.11.0"
rustyline = "12.0.0"
### ROBLOX

View file

@ -1,5 +1,6 @@
use std::{
env,
fmt::Write,
io::ErrorKind,
path::PathBuf,
process::{exit, ExitCode},
@ -7,19 +8,17 @@ use std::{
use anyhow::Error;
use clap::Command;
use lune::{Lune, LuneError};
use fancy_regex::Regex;
use lune::lua::stdio::formatting::{pretty_format_luau_error, pretty_format_value};
use lune::Lune;
use mlua::ExternalError;
use once_cell::sync::Lazy;
use rustyline::{error::ReadlineError, history::FileHistory, DefaultEditor, Editor};
use lune::lua::stdio::formatting::pretty_format_luau_error;
fn env_var_bool(value: String) -> Option<bool> {
match value.to_lowercase().as_str() {
"true" => Some(true),
"1" => Some(true),
"0" => Some(false),
"false" => Some(false),
"true" | "1" => Some(true),
"false" | "0" => Some(false),
&_ => None,
}
}
@ -73,18 +72,36 @@ pub async fn show_interface(cmd: Command) -> Result<ExitCode, Error> {
let no_color = env::var("NO_COLOR").unwrap_or_else(|_| "false".to_string());
if no_color.is_empty() {
false
true
} else {
!env_var_bool(no_color).unwrap_or_else(|| false)
}
});
// Group 2 of this match pattern is the variable contents
// We're using fancy_regex instead of regex for backreferences
// and lookaround. I'm not too good at regex, so if there's a
// way to do this without backreferences and lookarounds,
// please let me know.
const VARIABLE_DECLARATION_PAT: &str = r#"(?!local.*)(?!\s)(=\s*)(.*)"#;
// HACK: Prepend this "context" to the source code provided,
// so that the variable is preserved even the following steps
let mut source_code_context: Option<String> = None;
loop {
let mut source_code = String::new();
match repl.readline("> ") {
Ok(code) => {
source_code = code.clone();
if let Some(ref ctx) = source_code_context {
// If something breaks, blame this
source_code = format!("{} {}", ctx, code);
} else {
source_code.push_str(code.as_str());
}
repl.add_history_entry(code.as_str())?;
// If source code eval was requested, we reset the counter
@ -117,10 +134,35 @@ pub async fn show_interface(cmd: Command) -> Result<ExitCode, Error> {
}
};
let eval_result = lune_instance.run("REPL", source_code).await;
let eval_result = lune_instance.run("REPL", source_code.clone()).await;
match eval_result {
Ok(_) => (),
Ok(_) => {
if Regex::new(VARIABLE_DECLARATION_PAT)?.is_match(&source_code)?
&& !&source_code.contains("\n")
{
let declaration = source_code.split("=").collect::<Vec<&str>>()[1]
.trim()
.replace("\"", "");
let mut formatted_output = String::new();
pretty_format_value(
&mut formatted_output,
&mlua::IntoLua::into_lua(declaration, &mlua::Lua::new())?,
1,
)?;
source_code_context = (|| -> Result<Option<String>, Error> {
let mut ctx = String::new();
write!(&mut ctx, "{}\n", &source_code)?;
Ok(Some(ctx))
})()?;
println!("{}", formatted_output);
}
}
Err(err) => {
eprintln!(
"{}",