Remove unused VirtualId import
The `VirtualId` enum was imported but not used in the `macros.rs` file. This commit removes the unused import to clean up the code. Fix Zero Width Space handling in lexer The lexer incorrectly handled the Zero Width Space character (U+200B) when it appeared inside identifiers. This commit corrects the lexer to ignore this character in such contexts, ensuring that identifiers containing it are parsed correctly. Add REPL functionality to MCP server This commit introduces a REPL (Read-Eval-Print Loop) mode to the MCP server, allowing for interactive evaluation of Myc code with persistent state between calls. Key additions: - `REPL_ENV`: A thread-local static variable to hold the persistent `Environment` for the REPL session. It's necessary because `Environment` uses `Rc<RefCell<_>>` internally and is not `Send`/`Sync`, but the `rmcp` server requires its handler to be `Send + Sync`. This is safe as the underlying Tokio runtime is single-threaded. - `repl_eval`: A new tool that evaluates a given code string within the persistent REPL environment. Bindings (variables, functions, macros) are preserved across invocations. - `repl_reset`: A tool to reset the REPL session, clearing all accumulated bindings and starting with a fresh environment. - The main tool description in `mcp_server.rs` is updated to mention the new REPL functionalities.
This commit is contained in:
@@ -662,7 +662,7 @@ mod tests {
|
||||
use crate::ast::compiler::binder::{CompilerScope, LocalInfo};
|
||||
use crate::ast::compiler::Binder;
|
||||
use crate::ast::diagnostics::Diagnostics;
|
||||
use crate::ast::nodes::{Address, GlobalIdx, VirtualId};
|
||||
use crate::ast::nodes::{Address, GlobalIdx};
|
||||
use crate::ast::parser::Parser;
|
||||
use crate::ast::types::{NodeIdentity, Purity, SourceLocation, StaticType, Value};
|
||||
|
||||
|
||||
+1
-1
@@ -309,7 +309,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_lex_invisible_chars() {
|
||||
// U+200B Zero Width Space should be ignored inside identifiers
|
||||
let source = "ab";
|
||||
let source = "a\u{200B}b";
|
||||
let mut lexer = Lexer::new(source);
|
||||
let token = lexer.next_token().unwrap();
|
||||
if let TokenKind::Identifier(id) = token.kind {
|
||||
|
||||
+42
-1
@@ -1,3 +1,5 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use rmcp::{
|
||||
ServerHandler,
|
||||
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
|
||||
@@ -24,6 +26,15 @@ pub struct SymbolInput {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
/// Persistent environment for REPL-style evaluation across multiple calls.
|
||||
/// Lives in a thread-local because `Environment` uses `Rc<RefCell<...>>`
|
||||
/// internally and is not `Send`/`Sync`, while `rmcp` requires the server
|
||||
/// struct to be `Send + Sync`. Safe because we run on a single-threaded
|
||||
/// Tokio runtime.
|
||||
static REPL_ENV: RefCell<Option<Environment>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
/// MCP server exposing the Myc compiler as a set of tools for LLMs.
|
||||
#[derive(Clone)]
|
||||
pub struct MycMcpServer {
|
||||
@@ -164,6 +175,34 @@ impl MycMcpServer {
|
||||
None => format!("No documentation found for symbol '{}'.", name),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates a Myc expression in a persistent REPL session.
|
||||
/// Bindings from previous calls are preserved.
|
||||
#[tool(description = "Evaluate a Myc expression in a persistent REPL session. \
|
||||
Unlike eval_myc, bindings (variables, functions, macros) from previous calls \
|
||||
are preserved across invocations. Use repl_reset to start a fresh session. \
|
||||
Example workflow: call repl_eval with '(def x 42)', then '(+ x 1)' returns 43.")]
|
||||
fn repl_eval(&self, Parameters(CodeInput { code }): Parameters<CodeInput>) -> String {
|
||||
REPL_ENV.with(|cell| {
|
||||
let mut env_opt = cell.borrow_mut();
|
||||
let env = env_opt.get_or_insert_with(Self::make_env);
|
||||
match env.run_script(&code) {
|
||||
Ok(value) => format!("{}", value),
|
||||
Err(e) => format!("Error: {}", e),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Resets the REPL session, discarding all accumulated bindings.
|
||||
#[tool(description = "Reset the persistent REPL session, discarding all variables, \
|
||||
functions, and macros defined in previous repl_eval calls. \
|
||||
The next repl_eval call will start with a fresh environment.")]
|
||||
fn repl_reset(&self) -> String {
|
||||
REPL_ENV.with(|cell| {
|
||||
*cell.borrow_mut() = None;
|
||||
});
|
||||
"REPL session reset.".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[tool_handler]
|
||||
@@ -180,7 +219,9 @@ impl ServerHandler for MycMcpServer {
|
||||
`list_builtins` to see all available built-in functions, \
|
||||
`check_syntax` to validate code, \
|
||||
`eval_myc` to run expressions, and \
|
||||
`dump_ast` to inspect the compiled AST.",
|
||||
`dump_ast` to inspect the compiled AST. \
|
||||
For interactive sessions, use `repl_eval` to evaluate expressions with \
|
||||
persistent bindings across calls, and `repl_reset` to start fresh.",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user