use crate::ast::{BinOp, Expr, Stmt};
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::rc::Rc;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::cell::RefCell;
pub type EvalResult<T> = Result<T, String>;
#[derive(Debug, Clone)]
pub enum Value {
Number(f64),
Str(String),
Bool(bool),
Null,
Function(Rc<FunctionValue>),
Array(Rc<RefCell<Vec<Value>>>),
}
#[derive(Debug)]
pub struct FunctionValue {
pub name: String,
pub params: Vec<String>,
pub body: Vec<Stmt>,
pub closure: Env,
}
impl Value {
pub fn is_truthy(&self) -> bool {
match self {
Value::Bool(b) => *b,
Value::Null => false,
Value::Number(n) => *n != 0.0,
Value::Str(s) => !s.is_empty(),
Value::Function(_) => true,
Value::Array(a) => !a.borrow().is_empty(),
}
}
pub fn display(&self) -> String {
match self {
Value::Number(n) => {
let as_int = *n as i64;
if as_int as f64 == *n {
format!("{}", as_int)
} else {
format!("{}", n)
}
}
Value::Str(s) => s.clone(),
Value::Bool(b) => {
if *b {
"सहि".to_string()
} else {
"गलत".to_string()
}
}
Value::Null => "केहीछैन".to_string(),
Value::Function(f) => format!("<function {}>", f.name),
Value::Array(a) => {
let items: Vec<String> = a.borrow().iter().map(Value::display).collect();
format!("[{}]", items.join(", "))
}
}
}
}
#[derive(Debug)]
pub struct Scope {
vars: BTreeMap<String, Value>,
parent: Option<Env>,
}
pub type Env = Rc<RefCell<Scope>>;
pub fn new_scope(parent: Option<Env>) -> Env {
Rc::new(RefCell::new(Scope {
vars: BTreeMap::new(),
parent,
}))
}
fn env_get(env: &Env, name: &str) -> Option<Value> {
if let Some(v) = env.borrow().vars.get(name) {
return Some(v.clone());
}
match &env.borrow().parent {
Some(parent) => env_get(parent, name),
None => None,
}
}
fn env_define(env: &Env, name: String, value: Value) {
env.borrow_mut().vars.insert(name, value);
}
fn env_assign(env: &Env, name: &str, value: Value) -> Result<(), String> {
if env.borrow().vars.contains_key(name) {
env.borrow_mut().vars.insert(name.into(), value);
return Ok(());
}
let parent = env.borrow().parent.clone();
match parent {
Some(parent) => env_assign(&parent, name, value),
None => Err(format!("undefined variable '{}'", name)),
}
}
enum Signal {
Normal,
Return(Value),
}
pub trait HostFs {
fn read_file(&self, path: &str) -> Result<String, String>;
fn write_file(&self, path: &str, contents: &str) -> Result<(), String>;
fn list_dir(&self, path: &str) -> Result<Vec<String>, String>;
}
pub trait HostProcess {
fn spawn(&self, name: &str) -> Result<f64, String>;
fn list(&self) -> Result<Vec<String>, String>;
}
pub trait HostCommand {
fn run(&self, program: &str, args: &[String]) -> Result<(i32, String, String), String>;
}
pub trait HostChannel {
fn create(&self) -> Result<f64, String>;
fn send(&self, id: f64, msg: &str) -> Result<(), String>;
fn recv(&self, id: f64) -> Result<Option<String>, String>;
}
pub trait HostDb {
fn execute(&self, sql: &str) -> Result<f64, String>;
fn query(&self, sql: &str) -> Result<Vec<Value>, String>;
}
pub trait HostPython {
fn eval(&self, code: &str) -> Result<Value, String>;
}
pub trait HostRust {
fn call(&self, lib_path: &str, fn_name: &str, args: &[Value]) -> Result<Value, String>;
}
pub trait HostJs {
fn eval(&self, code: &str) -> Result<Value, String>;
fn eval_ts(&self, code: &str) -> Result<Value, String>;
}
pub trait HostCache {
fn set(&self, key: &str, value: &str, ttl_seconds: u64) -> Result<(), String>;
fn get(&self, key: &str) -> Result<Option<String>, String>;
fn delete(&self, key: &str) -> Result<(), String>;
}
pub trait HostAi {
fn ask(&self, prompt: &str) -> Result<String, String>;
fn listen(&self, audio_path: &str) -> Result<String, String>;
fn speak(&self, text: &str) -> Result<String, String>;
}
pub struct Interpreter {
pub output: Vec<String>,
globals: Env,
host_fs: Option<Rc<dyn HostFs>>,
host_process: Option<Rc<dyn HostProcess>>,
host_channel: Option<Rc<dyn HostChannel>>,
host_db: Option<Rc<dyn HostDb>>,
host_python: Option<Rc<dyn HostPython>>,
host_rust: Option<Rc<dyn HostRust>>,
host_js: Option<Rc<dyn HostJs>>,
host_cache: Option<Rc<dyn HostCache>>,
host_ai: Option<Rc<dyn HostAi>>,
host_command: Option<Rc<dyn HostCommand>>,
}
impl Interpreter {
pub fn new() -> Self {
Interpreter {
output: Vec::new(),
globals: new_scope(None),
host_fs: None,
host_process: None,
host_channel: None,
host_db: None,
host_python: None,
host_rust: None,
host_js: None,
host_cache: None,
host_ai: None,
host_command: None,
}
}
pub fn set_host_fs(&mut self, host_fs: Rc<dyn HostFs>) {
self.host_fs = Some(host_fs);
}
pub fn set_host_process(&mut self, host_process: Rc<dyn HostProcess>) {
self.host_process = Some(host_process);
}
pub fn set_host_channel(&mut self, host_channel: Rc<dyn HostChannel>) {
self.host_channel = Some(host_channel);
}
pub fn set_host_db(&mut self, host_db: Rc<dyn HostDb>) {
self.host_db = Some(host_db);
}
pub fn set_host_python(&mut self, host_python: Rc<dyn HostPython>) {
self.host_python = Some(host_python);
}
pub fn set_host_rust(&mut self, host_rust: Rc<dyn HostRust>) {
self.host_rust = Some(host_rust);
}
pub fn set_host_js(&mut self, host_js: Rc<dyn HostJs>) {
self.host_js = Some(host_js);
}
pub fn set_host_cache(&mut self, host_cache: Rc<dyn HostCache>) {
self.host_cache = Some(host_cache);
}
pub fn set_host_ai(&mut self, host_ai: Rc<dyn HostAi>) {
self.host_ai = Some(host_ai);
}
pub fn set_host_command(&mut self, host_command: Rc<dyn HostCommand>) {
self.host_command = Some(host_command);
}
pub fn run(&mut self, program: &[Stmt]) -> EvalResult<()> {
let env = self.globals.clone();
match self.exec_block(program, &env)? {
_ => Ok(()),
}
}
fn exec_block(&mut self, stmts: &[Stmt], env: &Env) -> EvalResult<Signal> {
for stmt in stmts {
match self.exec_stmt(stmt, env)? {
Signal::Normal => continue,
ret @ Signal::Return(_) => return Ok(ret),
}
}
Ok(Signal::Normal)
}
fn exec_stmt(&mut self, stmt: &Stmt, env: &Env) -> EvalResult<Signal> {
match stmt {
Stmt::Let(name, expr) => {
let value = self.eval_expr(expr, env)?;
env_define(env, name.clone(), value);
Ok(Signal::Normal)
}
Stmt::Print(exprs) => {
let mut parts = Vec::with_capacity(exprs.len());
for expr in exprs {
parts.push(self.eval_expr(expr, env)?.display());
}
self.output.push(parts.join(" "));
Ok(Signal::Normal)
}
Stmt::ExprStmt(expr) => {
self.eval_expr(expr, env)?;
Ok(Signal::Normal)
}
Stmt::If(cond, then_branch, else_branch) => {
let cond_val = self.eval_expr(cond, env)?;
if cond_val.is_truthy() {
let scope = new_scope(Some(env.clone()));
self.exec_block(then_branch, &scope)
} else if let Some(else_branch) = else_branch {
let scope = new_scope(Some(env.clone()));
self.exec_block(else_branch, &scope)
} else {
Ok(Signal::Normal)
}
}
Stmt::While(cond, body) => {
while self.eval_expr(cond, env)?.is_truthy() {
let scope = new_scope(Some(env.clone()));
match self.exec_block(body, &scope)? {
Signal::Normal => continue,
ret @ Signal::Return(_) => return Ok(ret),
}
}
Ok(Signal::Normal)
}
Stmt::FunctionDecl(name, params, body) => {
let func = Value::Function(Rc::new(FunctionValue {
name: name.clone(),
params: params.clone(),
body: body.clone(),
closure: env.clone(),
}));
env_define(env, name.clone(), func);
Ok(Signal::Normal)
}
Stmt::Return(expr) => {
let value = match expr {
Some(e) => self.eval_expr(e, env)?,
None => Value::Null,
};
Ok(Signal::Return(value))
}
Stmt::Import(path) => Err(format!(
"import \"{}\" reached the interpreter unresolved - imports must be \
resolved by a host loader (e.g. nepali-core-cli's loader module) before running",
path
)),
}
}
fn eval_expr(&mut self, expr: &Expr, env: &Env) -> EvalResult<Value> {
match expr {
Expr::Number(n) => Ok(Value::Number(*n)),
Expr::StringLit(s) => Ok(Value::Str(s.clone())),
Expr::Bool(b) => Ok(Value::Bool(*b)),
Expr::Null => Ok(Value::Null),
Expr::Ident(name) => {
env_get(env, name).ok_or_else(|| format!("undefined variable '{}'", name))
}
Expr::Neg(inner) => {
let v = self.eval_expr(inner, env)?;
match v {
Value::Number(n) => Ok(Value::Number(-n)),
other => Err(format!("cannot negate {}", other.display())),
}
}
Expr::Not(inner) => {
let v = self.eval_expr(inner, env)?;
Ok(Value::Bool(!v.is_truthy()))
}
Expr::Assign(name, value_expr) => {
let value = self.eval_expr(value_expr, env)?;
env_assign(env, name, value.clone())?;
Ok(value)
}
Expr::ArrayLit(elements) => {
let mut values = Vec::with_capacity(elements.len());
for e in elements {
values.push(self.eval_expr(e, env)?);
}
Ok(Value::Array(Rc::new(RefCell::new(values))))
}
Expr::Index(object, index) => {
let obj = self.eval_expr(object, env)?;
let idx = self.eval_expr(index, env)?;
let arr = expect_array(&obj)?;
let i = expect_index(&idx, arr.borrow().len())?;
let value = arr.borrow()[i].clone();
Ok(value)
}
Expr::IndexAssign(object, index, value_expr) => {
let obj = self.eval_expr(object, env)?;
let idx = self.eval_expr(index, env)?;
let value = self.eval_expr(value_expr, env)?;
let arr = expect_array(&obj)?;
let i = expect_index(&idx, arr.borrow().len())?;
arr.borrow_mut()[i] = value.clone();
Ok(value)
}
Expr::Binary(BinOp::And, left, right) => {
let l = self.eval_expr(left, env)?;
if !l.is_truthy() {
return Ok(Value::Bool(false));
}
let r = self.eval_expr(right, env)?;
Ok(Value::Bool(r.is_truthy()))
}
Expr::Binary(BinOp::Or, left, right) => {
let l = self.eval_expr(left, env)?;
if l.is_truthy() {
return Ok(Value::Bool(true));
}
let r = self.eval_expr(right, env)?;
Ok(Value::Bool(r.is_truthy()))
}
Expr::Binary(op, left, right) => {
let l = self.eval_expr(left, env)?;
let r = self.eval_expr(right, env)?;
self.eval_binary(op, l, r)
}
Expr::Call(callee, arg_exprs) => {
if let Expr::Ident(name) = callee.as_ref() {
if is_host_fs_builtin(name)
|| is_host_process_builtin(name)
|| is_host_channel_builtin(name)
|| is_host_db_builtin(name)
|| is_host_python_builtin(name)
|| is_host_rust_builtin(name)
|| is_host_js_builtin(name)
|| is_host_cache_builtin(name)
|| is_host_ai_builtin(name)
|| is_host_command_builtin(name)
|| is_agent_builtin(name)
|| is_builtin(name)
{
let mut args = Vec::with_capacity(arg_exprs.len());
for a in arg_exprs {
args.push(self.eval_expr(a, env)?);
}
return self.dispatch_builtin(name, &args);
}
}
let callee_val = self.eval_expr(callee, env)?;
let mut args = Vec::with_capacity(arg_exprs.len());
for a in arg_exprs {
args.push(self.eval_expr(a, env)?);
}
self.call(callee_val, args)
}
}
}
fn dispatch_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
if is_host_fs_builtin(name) {
return self.call_host_fs_builtin(name, args);
}
if is_host_process_builtin(name) {
return self.call_host_process_builtin(name, args);
}
if is_host_channel_builtin(name) {
return self.call_host_channel_builtin(name, args);
}
if is_host_db_builtin(name) {
return self.call_host_db_builtin(name, args);
}
if is_host_python_builtin(name) {
return self.call_host_python_builtin(name, args);
}
if is_host_rust_builtin(name) {
return self.call_host_rust_builtin(name, args);
}
if is_host_js_builtin(name) {
return self.call_host_js_builtin(name, args);
}
if is_host_cache_builtin(name) {
return self.call_host_cache_builtin(name, args);
}
if is_host_ai_builtin(name) {
return self.call_host_ai_builtin(name, args);
}
if is_host_command_builtin(name) {
return self.call_host_command_builtin(name, args);
}
if is_agent_builtin(name) {
return self.call_agent_builtin(name, args);
}
call_builtin(name, args)
}
fn call_host_fs_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
let host = self.host_fs.clone().ok_or_else(|| {
format!(
"'{}' needs a host filesystem, which isn't available here \
(no disk mounted, or running somewhere with no real filesystem at all)",
name
)
})?;
match name {
"ओएस_लेख्नुहोस्" => {
let path = expect_string(name, args, 0)?;
let contents = expect_string(name, args, 1)?;
host.write_file(&path, &contents)?;
Ok(Value::Null)
}
"ओएस_पढ्नुहोस्" => {
let path = expect_string(name, args, 0)?;
let contents = host.read_file(&path)?;
Ok(Value::Str(contents))
}
"ओएस_सूची" => {
let path = expect_string(name, args, 0)?;
let entries = host.list_dir(&path)?;
let values: Vec<Value> = entries.into_iter().map(Value::Str).collect();
Ok(Value::Array(Rc::new(RefCell::new(values))))
}
_ => unreachable!("is_host_fs_builtin only admits the three names handled above"),
}
}
fn call_host_process_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
let host = self.host_process.clone().ok_or_else(|| {
format!(
"'{}' needs a host process manager, which isn't available here \
(running somewhere with no real process scheduler at all)",
name
)
})?;
match name {
"नयाँ_प्रक्रिया" => {
let proc_name = expect_string(name, args, 0)?;
let pid = host.spawn(&proc_name)?;
Ok(Value::Number(pid))
}
"प्रक्रिया_सूची" => {
let names = host.list()?;
let values: Vec<Value> = names.into_iter().map(Value::Str).collect();
Ok(Value::Array(Rc::new(RefCell::new(values))))
}
_ => unreachable!("is_host_process_builtin only admits the two names handled above"),
}
}
fn call_host_channel_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
let host = self.host_channel.clone().ok_or_else(|| {
format!(
"'{}' needs a host channel manager, which isn't available here \
(running somewhere with no real channel mechanism at all)",
name
)
})?;
match name {
"नयाँ_च्यानल" => {
let id = host.create()?;
Ok(Value::Number(id))
}
"च्यानल_पठाउनुहोस्" => {
let id = expect_number(name, args, 0)?;
let msg = expect_string(name, args, 1)?;
host.send(id, &msg)?;
Ok(Value::Null)
}
"च्यानल_पाउनुहोस्" => {
let id = expect_number(name, args, 0)?;
match host.recv(id)? {
Some(msg) => Ok(Value::Str(msg)),
None => Ok(Value::Null),
}
}
_ => unreachable!("is_host_channel_builtin only admits the three names handled above"),
}
}
fn call_host_db_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
let host = self.host_db.clone().ok_or_else(|| {
format!(
"'{}' needs a host database, which isn't available here \
(running somewhere with no real database connection at all)",
name
)
})?;
match name {
"डाटाबेस_चलाउनुहोस्" => {
let sql = expect_string(name, args, 0)?;
let affected = host.execute(&sql)?;
Ok(Value::Number(affected))
}
"डाटाबेस_सोध्नुहोस्" => {
let sql = expect_string(name, args, 0)?;
let rows = host.query(&sql)?;
Ok(Value::Array(Rc::new(RefCell::new(rows))))
}
_ => unreachable!("is_host_db_builtin only admits the two names handled above"),
}
}
fn call_host_python_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
let host = self.host_python.clone().ok_or_else(|| {
format!(
"'{}' needs an embedded Python, which isn't available here \
(running somewhere with no real Python interpreter linked in)",
name
)
})?;
match name {
"पाइथन_चलाउनुहोस्" => {
let code = expect_string(name, args, 0)?;
host.eval(&code)
}
_ => unreachable!("is_host_python_builtin only admits the one name handled above"),
}
}
fn call_host_rust_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
let host = self.host_rust.clone().ok_or_else(|| {
format!(
"'{}' needs a real native-plugin loader, which isn't available here",
name
)
})?;
match name {
"रस्ट_चलाउनुहोस्" | "गो_चलाउनुहोस्" => {
let lib_path = expect_string(name, args, 0)?;
let fn_name = expect_string(name, args, 1)?;
host.call(&lib_path, &fn_name, &args[2..])
}
_ => unreachable!("is_host_rust_builtin only admits the two names handled above"),
}
}
fn call_host_js_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
let host = self.host_js.clone().ok_or_else(|| {
format!(
"'{}' needs an embedded JS/TS engine, which isn't available here",
name
)
})?;
match name {
"जेएस_चलाउनुहोस्" => {
let code = expect_string(name, args, 0)?;
host.eval(&code)
}
"टिएस_चलाउनुहोस्" => {
let code = expect_string(name, args, 0)?;
host.eval_ts(&code)
}
_ => unreachable!("is_host_js_builtin only admits the two names handled above"),
}
}
fn call_host_cache_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
let host = self.host_cache.clone().ok_or_else(|| {
format!(
"'{}' needs a real cache server, which isn't available here \
(no redis-server connection)",
name
)
})?;
match name {
"क्यास_राख्नुहोस्" => {
let key = expect_string(name, args, 0)?;
let value = expect_string(name, args, 1)?;
let ttl = if args.len() > 2 { expect_number(name, args, 2)? } else { 0.0 };
host.set(&key, &value, ttl.max(0.0) as u64)?;
Ok(Value::Null)
}
"क्यास_ल्याउनुहोस्" => {
let key = expect_string(name, args, 0)?;
match host.get(&key)? {
Some(v) => Ok(Value::Str(v)),
None => Ok(Value::Null),
}
}
"क्यास_हटाउनुहोस्" => {
let key = expect_string(name, args, 0)?;
host.delete(&key)?;
Ok(Value::Null)
}
_ => unreachable!("is_host_cache_builtin only admits the three names handled above"),
}
}
fn call_host_ai_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
let host = self.host_ai.clone().ok_or_else(|| {
format!(
"'{}' needs a real local AI backend, which isn't available here \
(no model loaded)",
name
)
})?;
match name {
"एआई_सोध्नुहोस्" => {
let prompt = expect_string(name, args, 0)?;
let response = host.ask(&prompt)?;
Ok(Value::Str(response))
}
"एआई_सुन्नुहोस्" => {
let audio_path = expect_string(name, args, 0)?;
let text = host.listen(&audio_path)?;
Ok(Value::Str(text))
}
"एआई_बोल्नुहोस्" => {
let text = expect_string(name, args, 0)?;
let out_path = host.speak(&text)?;
Ok(Value::Str(out_path))
}
_ => unreachable!("is_host_ai_builtin only admits the three names handled above"),
}
}
fn call_host_command_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
let host = self.host_command.clone().ok_or_else(|| {
format!(
"'{}' needs a real command runner, which isn't available here",
name
)
})?;
match name {
"आदेश_चलाउनुहोस्" => {
let program = expect_string(name, args, 0)?;
let cmd_args = if args.len() > 1 {
let arr = expect_array(&args[1])?;
let arr = arr.borrow();
let mut out = Vec::with_capacity(arr.len());
for v in arr.iter() {
match v {
Value::Str(s) => out.push(s.clone()),
other => {
return Err(format!(
"'{}' expects an array of strings as argument 2, got {}",
name,
other.display()
))
}
}
}
out
} else {
Vec::new()
};
let (exit_code, stdout, stderr) = host.run(&program, &cmd_args)?;
let result = alloc::vec![
Value::Number(exit_code as f64),
Value::Str(stdout),
Value::Str(stderr),
];
Ok(Value::Array(Rc::new(RefCell::new(result))))
}
_ => unreachable!("is_host_command_builtin only admits the one name handled above"),
}
}
fn call_agent_builtin(&mut self, name: &str, args: &[Value]) -> EvalResult<Value> {
match name {
"एजेन्ट_चलाउनुहोस्" => {
let goal = expect_string(name, args, 0)?;
let max_steps = if args.len() > 1 {
expect_number(name, args, 1)?.max(1.0) as usize
} else {
6
};
let answer = self.run_agent(&goal, max_steps)?;
Ok(Value::Str(answer))
}
_ => unreachable!("is_agent_builtin only admits the one name handled above"),
}
}
fn run_agent(&mut self, goal: &str, max_steps: usize) -> EvalResult<String> {
let host_ai = self.host_ai.clone().ok_or_else(|| {
"एजेन्ट_चलाउनुहोस् needs a real local AI backend, which isn't available here"
.to_string()
})?;
let mut transcript = format!(
"You are an OS agent for Nepali OS. Respond with EXACTLY one line, one of:\n\
कार्य: TOOL(args)\n\
अन्तिम: your final answer\n\n\
Available tools:\n\
- फाइल_पढ्नुहोस्(path) - reads a real file\n\
- फाइल_लेख्नुहोस्(path, contents) - writes a real file\n\
- सूची(path) - lists a real directory\n\
- आदेश(program, arg1, arg2, ...) - runs a real command, returns its exit code and output\n\n\
Example:\n\
Goal: read /tmp/x.txt and tell me what it says\n\
कार्य: फाइल_पढ्नुहोस्(/tmp/x.txt)\n\
नतिजा: hello world\n\
अन्तिम: The file says \"hello world\".\n\n\
Now the real task.\n\
Goal: {goal}\n\n\
Respond with exactly one line, starting with either कार्य: or अन्तिम: - nothing else.\n"
);
let mut last_action: Option<String> = None;
for step in 0..max_steps {
let response = host_ai.ask(&transcript)?;
let line: String = response
.lines()
.find(|l| !l.trim().is_empty())
.unwrap_or("")
.trim()
.to_string();
if let Some(answer) = line.strip_prefix("अन्तिम:") {
return Ok(answer.trim().to_string());
}
let Some(action) = line.strip_prefix("कार्य:") else {
transcript.push_str(&format!(
"\n{line}\n\nत्रुटि: अपेक्षित 'कार्य:' वा 'अन्तिम:' बाट सुरु हुने लाइन। \
पुन: प्रयास गर्नुहोस्:\n"
));
continue;
};
let action = action.trim();
if last_action.as_deref() == Some(action) {
let result = self.execute_agent_action(action);
return Ok(format!(
"(दोहोरिएको कार्य पत्ता लाग्यो, यसैले रोकियो; अन्तिम नतिजा: {result})"
));
}
last_action = Some(action.to_string());
let result = self.execute_agent_action(action);
let is_last = step == max_steps - 1;
if is_last {
return Ok(format!(
"(अधिकतम चरण पुग्यो, अन्तिम जवाफ बिना; अन्तिम नतिजा: {result})"
));
}
transcript.push_str(&format!(
"\n{line}\nनतिजा: {result}\n\nअर्को कार्य वा अन्तिम जवाफ दिनुहोस्:\n"
));
}
Ok("(कुनै जवाफ आएन)".to_string())
}
fn execute_agent_action(&mut self, action: &str) -> String {
let Some(open) = action.find('(') else {
return format!("त्रुटि: '{action}' मा '(' फेला परेन");
};
let Some(close) = action.rfind(')') else {
return format!("त्रुटि: '{action}' मा ')' फेला परेन");
};
if close < open {
return format!("त्रुटि: '{action}' मा कोष्ठक मिलेन");
}
let tool = action[..open].trim();
let args_str = &action[open + 1..close];
let raw_args: Vec<String> = if args_str.trim().is_empty() {
Vec::new()
} else {
args_str
.split(',')
.map(|a| a.trim().trim_matches('"').trim_matches('\'').to_string())
.collect()
};
match tool {
"फाइल_पढ्नुहोस्" => {
let Some(host) = self.host_fs.clone() else {
return "त्रुटि: कुनै वास्तविक फाइलसिस्टम उपलब्ध छैन".to_string();
};
let Some(path) = raw_args.first() else {
return "त्रुटि: फाइल_पढ्नुहोस् लाई path चाहिन्छ".to_string();
};
match host.read_file(path) {
Ok(contents) => contents,
Err(e) => format!("त्रुटि: {e}"),
}
}
"फाइल_लेख्नुहोस्" => {
let Some(host) = self.host_fs.clone() else {
return "त्रुटि: कुनै वास्तविक फाइलसिस्टम उपलब्ध छैन".to_string();
};
if raw_args.len() < 2 {
return "त्रुटि: फाइल_लेख्नुहोस् लाई path र contents चाहिन्छ".to_string();
}
match host.write_file(&raw_args[0], &raw_args[1]) {
Ok(()) => "ठिक छ".to_string(),
Err(e) => format!("त्रुटि: {e}"),
}
}
"सूची" => {
let Some(host) = self.host_fs.clone() else {
return "त्रुटि: कुनै वास्तविक फाइलसिस्टम उपलब्ध छैन".to_string();
};
let Some(path) = raw_args.first() else {
return "त्रुटि: सूची लाई path चाहिन्छ".to_string();
};
match host.list_dir(path) {
Ok(entries) => entries.join(", "),
Err(e) => format!("त्रुटि: {e}"),
}
}
"आदेश" => {
let Some(host) = self.host_command.clone() else {
return "त्रुटि: कुनै वास्तविक आदेश-चालक उपलब्ध छैन".to_string();
};
let Some(program) = raw_args.first() else {
return "त्रुटि: आदेश लाई program चाहिन्छ".to_string();
};
let cmd_args = raw_args[1..].to_vec();
match host.run(program, &cmd_args) {
Ok((code, stdout, stderr)) => {
format!("exit={code}\nstdout: {stdout}\nstderr: {stderr}")
}
Err(e) => format!("त्रुटि: {e}"),
}
}
other => format!("त्रुटि: अज्ञात औजार '{other}'"),
}
}
fn call(&mut self, callee: Value, args: Vec<Value>) -> EvalResult<Value> {
let func = match callee {
Value::Function(f) => f,
other => return Err(format!("'{}' is not callable", other.display())),
};
if args.len() != func.params.len() {
return Err(format!(
"function '{}' expects {} argument(s), got {}",
func.name,
func.params.len(),
args.len()
));
}
let call_scope = new_scope(Some(func.closure.clone()));
for (param, arg) in func.params.iter().zip(args.into_iter()) {
env_define(&call_scope, param.clone(), arg);
}
match self.exec_block(&func.body, &call_scope)? {
Signal::Return(v) => Ok(v),
Signal::Normal => Ok(Value::Null),
}
}
fn eval_binary(&self, op: &BinOp, l: Value, r: Value) -> EvalResult<Value> {
use BinOp::*;
match op {
Add => match (&l, &r) {
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
_ => Ok(Value::Str(format!("{}{}", l.display(), r.display()))),
},
Sub | Mul | Div | Mod => {
let (a, b) = match (&l, &r) {
(Value::Number(a), Value::Number(b)) => (*a, *b),
_ => {
return Err(format!(
"arithmetic on non-numbers: {} and {}",
l.display(),
r.display()
))
}
};
match op {
Sub => Ok(Value::Number(a - b)),
Mul => Ok(Value::Number(a * b)),
Div => Ok(Value::Number(a / b)),
Mod => Ok(Value::Number(a % b)),
_ => unreachable!(),
}
}
Eq => Ok(Value::Bool(values_equal(&l, &r))),
NotEq => Ok(Value::Bool(!values_equal(&l, &r))),
Lt | Gt | Lte | Gte => {
let (a, b) = match (&l, &r) {
(Value::Number(a), Value::Number(b)) => (*a, *b),
_ => {
return Err(format!(
"comparison on non-numbers: {} and {}",
l.display(),
r.display()
))
}
};
let result = match op {
Lt => a < b,
Gt => a > b,
Lte => a <= b,
Gte => a >= b,
_ => unreachable!(),
};
Ok(Value::Bool(result))
}
And | Or => unreachable!(
"Expr::Binary(And|Or, ..) is intercepted in eval_expr for short-circuiting \
before it ever reaches eval_binary"
),
}
}
}
impl Default for Interpreter {
fn default() -> Self {
Self::new()
}
}
fn values_equal(l: &Value, r: &Value) -> bool {
match (l, r) {
(Value::Number(a), Value::Number(b)) => a == b,
(Value::Str(a), Value::Str(b)) => a == b,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Null, Value::Null) => true,
(Value::Array(a), Value::Array(b)) => {
let a = a.borrow();
let b = b.borrow();
a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
}
_ => false,
}
}
fn expect_array(v: &Value) -> EvalResult<Rc<RefCell<Vec<Value>>>> {
match v {
Value::Array(a) => Ok(a.clone()),
other => Err(format!("'{}' is not an array", other.display())),
}
}
fn expect_index(v: &Value, len: usize) -> EvalResult<usize> {
match v {
Value::Number(n) => {
let i = *n as i64;
if i < 0 || i as usize >= len {
Err(format!(
"array index {} out of bounds (length {})",
i, len
))
} else {
Ok(i as usize)
}
}
other => Err(format!("array index must be a number, got {}", other.display())),
}
}
pub const BUILTINS: &[&str] = &[
"लम्बाइ",
"अक्षर",
"संकेत",
"थप्नुहोस्",
"ओएस_लेख्नुहोस्",
"ओएस_पढ्नुहोस्",
"ओएस_सूची",
"नयाँ_प्रक्रिया",
"प्रक्रिया_सूची",
"नयाँ_च्यानल",
"च्यानल_पठाउनुहोस्",
"च्यानल_पाउनुहोस्",
"डाटाबेस_चलाउनुहोस्",
"डाटाबेस_सोध्नुहोस्",
"पाइथन_चलाउनुहोस्",
"रस्ट_चलाउनुहोस्",
"गो_चलाउनुहोस्",
"जेएस_चलाउनुहोस्",
"टिएस_चलाउनुहोस्",
"क्यास_राख्नुहोस्",
"क्यास_ल्याउनुहोस्",
"क्यास_हटाउनुहोस्",
"एआई_सोध्नुहोस्",
"एआई_सुन्नुहोस्",
"एआई_बोल्नुहोस्",
"आदेश_चलाउनुहोस्",
"एजेन्ट_चलाउनुहोस्",
];
pub fn is_builtin(name: &str) -> bool {
BUILTINS.contains(&name)
}
fn is_host_fs_builtin(name: &str) -> bool {
matches!(name, "ओएस_लेख्नुहोस्" | "ओएस_पढ्नुहोस्" | "ओएस_सूची")
}
fn is_host_process_builtin(name: &str) -> bool {
matches!(name, "नयाँ_प्रक्रिया" | "प्रक्रिया_सूची")
}
fn is_host_channel_builtin(name: &str) -> bool {
matches!(
name,
"नयाँ_च्यानल" | "च्यानल_पठाउनुहोस्" | "च्यानल_पाउनुहोस्"
)
}
fn is_host_db_builtin(name: &str) -> bool {
matches!(name, "डाटाबेस_चलाउनुहोस्" | "डाटाबेस_सोध्नुहोस्")
}
fn is_host_python_builtin(name: &str) -> bool {
matches!(name, "पाइथन_चलाउनुहोस्")
}
fn is_host_rust_builtin(name: &str) -> bool {
matches!(name, "रस्ट_चलाउनुहोस्" | "गो_चलाउनुहोस्")
}
fn is_host_js_builtin(name: &str) -> bool {
matches!(name, "जेएस_चलाउनुहोस्" | "टिएस_चलाउनुहोस्")
}
fn is_host_cache_builtin(name: &str) -> bool {
matches!(name, "क्यास_राख्नुहोस्" | "क्यास_ल्याउनुहोस्" | "क्यास_हटाउनुहोस्")
}
fn is_host_ai_builtin(name: &str) -> bool {
matches!(name, "एआई_सोध्नुहोस्" | "एआई_सुन्नुहोस्" | "एआई_बोल्नुहोस्")
}
fn is_host_command_builtin(name: &str) -> bool {
matches!(name, "आदेश_चलाउनुहोस्")
}
fn is_agent_builtin(name: &str) -> bool {
matches!(name, "एजेन्ट_चलाउनुहोस्")
}
pub fn call_builtin(name: &str, args: &[Value]) -> EvalResult<Value> {
match name {
"लम्बाइ" => match args.first() {
Some(Value::Str(s)) => Ok(Value::Number(s.chars().count() as f64)),
Some(Value::Array(a)) => Ok(Value::Number(a.borrow().len() as f64)),
Some(other) => Err(format!(
"'{}' expects a string or array, got {}",
name,
other.display()
)),
None => Err(format!("'{}' expects an argument at position 1", name)),
},
"थप्नुहोस्" => {
let arr = match args.first() {
Some(Value::Array(a)) => a.clone(),
Some(other) => {
return Err(format!(
"'{}' expects an array at argument 1, got {}",
name,
other.display()
))
}
None => return Err(format!("'{}' expects an argument at position 1", name)),
};
let value = args
.get(1)
.cloned()
.ok_or_else(|| format!("'{}' expects an argument at position 2", name))?;
arr.borrow_mut().push(value);
Ok(Value::Null)
}
"अक्षर" => {
let s = expect_string(name, args, 0)?;
let i = expect_number(name, args, 1)? as i64;
if i < 0 {
return Ok(Value::Str(String::new()));
}
match s.chars().nth(i as usize) {
Some(c) => Ok(Value::Str(c.to_string())),
None => Ok(Value::Str(String::new())),
}
}
"संकेत" => {
let s = expect_string(name, args, 0)?;
let mut chars = s.chars();
match (chars.next(), chars.next()) {
(Some(c), None) => Ok(Value::Number(c as u32 as f64)),
_ => Err(format!(
"'{}' expects a single-character string, got \"{}\"",
name, s
)),
}
}
other => Err(format!("unknown builtin '{}'", other)),
}
}
fn expect_string(fn_name: &str, args: &[Value], idx: usize) -> EvalResult<String> {
match args.get(idx) {
Some(Value::Str(s)) => Ok(s.clone()),
Some(other) => Err(format!(
"'{}' expects a string at argument {}, got {}",
fn_name,
idx + 1,
other.display()
)),
None => Err(format!("'{}' expects an argument at position {}", fn_name, idx + 1)),
}
}
fn expect_number(fn_name: &str, args: &[Value], idx: usize) -> EvalResult<f64> {
match args.get(idx) {
Some(Value::Number(n)) => Ok(*n),
Some(other) => Err(format!(
"'{}' expects a number at argument {}, got {}",
fn_name,
idx + 1,
other.display()
)),
None => Err(format!("'{}' expects an argument at position {}", fn_name, idx + 1)),
}
}