2025-08-09 15:14:44 +09:00
|
|
|
|
/*!
|
|
|
|
|
|
* Nyash Environment System - Rust所有権ベースのスコープ管理
|
2025-09-17 07:43:07 +09:00
|
|
|
|
*
|
2025-08-09 15:14:44 +09:00
|
|
|
|
* Python版の曖昧なメモリ管理をRustの借用チェッカーで完全解決!
|
|
|
|
|
|
* Everything is Box哲学 + 所有権システム = 完璧なスコープ管理
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
use crate::box_trait::{NyashBox, VoidBox};
|
|
|
|
|
|
use crate::finalization::BoxFinalizer;
|
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
|
use thiserror::Error;
|
|
|
|
|
|
|
|
|
|
|
|
/// Nyash変数環境 - スコープチェーンを安全に管理
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
|
pub struct Environment {
|
|
|
|
|
|
/// 現在のスコープの変数バインディング
|
|
|
|
|
|
bindings: Mutex<HashMap<String, Box<dyn NyashBox>>>,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// 親環境への参照 (Arc<Mutex<>>でスレッド安全)
|
|
|
|
|
|
parent: Option<Arc<Mutex<Environment>>>,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// スコープレベル (デバッグ用)
|
|
|
|
|
|
level: usize,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// スコープ名 (関数名、クラス名など)
|
|
|
|
|
|
scope_name: String,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// Box解放管理
|
|
|
|
|
|
finalizer: Mutex<BoxFinalizer>,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Environment操作エラー
|
|
|
|
|
|
#[derive(Error, Debug)]
|
|
|
|
|
|
pub enum EnvironmentError {
|
|
|
|
|
|
#[error("Variable '{name}' is not defined")]
|
|
|
|
|
|
UndefinedVariable { name: String },
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
#[error("Cannot access parent environment: {reason}")]
|
|
|
|
|
|
ParentAccessError { reason: String },
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
#[error("Borrowing conflict in environment: {details}")]
|
|
|
|
|
|
BorrowError { details: String },
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Environment {
|
|
|
|
|
|
/// 新しいグローバル環境を作成
|
|
|
|
|
|
pub fn new_global() -> Arc<Mutex<Self>> {
|
|
|
|
|
|
Arc::new(Mutex::new(Self {
|
|
|
|
|
|
bindings: Mutex::new(HashMap::new()),
|
|
|
|
|
|
parent: None,
|
|
|
|
|
|
level: 0,
|
|
|
|
|
|
scope_name: "global".to_string(),
|
|
|
|
|
|
finalizer: Mutex::new(BoxFinalizer::new()),
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// 親環境を持つ新しい環境を作成 (関数、クラス用)
|
|
|
|
|
|
pub fn new_child(
|
2025-09-17 07:43:07 +09:00
|
|
|
|
parent: Arc<Mutex<Environment>>,
|
|
|
|
|
|
scope_name: impl Into<String>,
|
2025-08-09 15:14:44 +09:00
|
|
|
|
) -> Arc<Mutex<Self>> {
|
|
|
|
|
|
let level = parent.lock().unwrap().level + 1;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
Arc::new(Mutex::new(Self {
|
|
|
|
|
|
bindings: Mutex::new(HashMap::new()),
|
|
|
|
|
|
parent: Some(parent),
|
|
|
|
|
|
level,
|
|
|
|
|
|
scope_name: scope_name.into(),
|
|
|
|
|
|
finalizer: Mutex::new(BoxFinalizer::new()),
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// 変数を現在のスコープに定義
|
|
|
|
|
|
pub fn define(&self, name: impl Into<String>, value: Box<dyn NyashBox>) {
|
|
|
|
|
|
let name = name.into();
|
|
|
|
|
|
self.bindings.lock().unwrap().insert(name, value);
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// 変数を取得 (スコープチェーンを辿る)
|
|
|
|
|
|
pub fn get(&self, name: &str) -> Result<Box<dyn NyashBox>, EnvironmentError> {
|
|
|
|
|
|
// 現在のスコープから検索
|
|
|
|
|
|
if let Some(value) = self.bindings.lock().unwrap().get(name) {
|
2025-08-22 12:09:06 +09:00
|
|
|
|
return Ok(value.clone_or_share());
|
2025-08-09 15:14:44 +09:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 親スコープから検索
|
|
|
|
|
|
if let Some(parent) = &self.parent {
|
|
|
|
|
|
return parent.lock().unwrap().get(name);
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 見つからない
|
2025-09-17 07:43:07 +09:00
|
|
|
|
Err(EnvironmentError::UndefinedVariable {
|
|
|
|
|
|
name: name.to_string(),
|
2025-08-09 15:14:44 +09:00
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// 変数を設定 (既存変数の更新 or 新規定義)
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub fn set(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
name: impl Into<String>,
|
|
|
|
|
|
value: Box<dyn NyashBox>,
|
|
|
|
|
|
) -> Result<(), EnvironmentError> {
|
2025-08-09 15:14:44 +09:00
|
|
|
|
let name = name.into();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 現在のスコープにある場合は更新
|
|
|
|
|
|
if self.bindings.lock().unwrap().contains_key(&name) {
|
|
|
|
|
|
self.bindings.lock().unwrap().insert(name, value);
|
|
|
|
|
|
return Ok(());
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-11-13 18:55:14 +09:00
|
|
|
|
// 祖先いずれかに既存があれば「既存を更新」、無ければ「現在スコープに新規定義」
|
2025-08-09 15:14:44 +09:00
|
|
|
|
if let Some(parent) = &self.parent {
|
2025-11-13 18:55:14 +09:00
|
|
|
|
if Self::exists_in_chain_locked(&parent.lock().unwrap(), &name) {
|
|
|
|
|
|
// 祖先のどこかに存在 → そこで更新
|
|
|
|
|
|
parent.lock().unwrap().set(&name, value)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// どこにも存在しない → 現在のスコープで定義
|
|
|
|
|
|
self.bindings.lock().unwrap().insert(name, value);
|
|
|
|
|
|
Ok(())
|
2025-08-09 15:14:44 +09:00
|
|
|
|
}
|
2025-11-13 18:55:14 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
// 親がない(グローバル)→ 現在で定義
|
|
|
|
|
|
self.bindings.lock().unwrap().insert(name, value);
|
|
|
|
|
|
Ok(())
|
2025-08-09 15:14:44 +09:00
|
|
|
|
}
|
2025-11-13 18:55:14 +09:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-11-13 18:55:14 +09:00
|
|
|
|
/// 祖先(自分含む)に名前が存在するか(ロック下で使用)
|
|
|
|
|
|
fn exists_in_chain_locked(&self, name: &str) -> bool {
|
|
|
|
|
|
if self.bindings.lock().unwrap().contains_key(name) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if let Some(parent) = &self.parent {
|
|
|
|
|
|
return parent.lock().unwrap().exists_in_chain_locked(name);
|
|
|
|
|
|
}
|
|
|
|
|
|
false
|
2025-08-09 15:14:44 +09:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// 変数が存在するかチェック
|
|
|
|
|
|
pub fn exists(&self, name: &str) -> bool {
|
|
|
|
|
|
self.get(name).is_ok()
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// 変数を削除 (現在のスコープからのみ)
|
|
|
|
|
|
pub fn undefine(&self, name: &str) -> bool {
|
|
|
|
|
|
self.bindings.lock().unwrap().remove(name).is_some()
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// 現在のスコープの変数一覧を取得
|
|
|
|
|
|
pub fn list_variables(&self) -> Vec<String> {
|
|
|
|
|
|
self.bindings.lock().unwrap().keys().cloned().collect()
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// スコープ情報を取得 (デバッグ用)
|
|
|
|
|
|
pub fn scope_info(&self) -> String {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
format!(
|
|
|
|
|
|
"{}[{}] (level {})",
|
|
|
|
|
|
self.scope_name,
|
|
|
|
|
|
self.bindings.lock().unwrap().len(),
|
|
|
|
|
|
self.level
|
|
|
|
|
|
)
|
2025-08-09 15:14:44 +09:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// スコープチェーン全体の情報を取得
|
|
|
|
|
|
pub fn scope_chain_info(&self) -> Vec<String> {
|
|
|
|
|
|
let mut chain = vec![self.scope_info()];
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
if let Some(parent) = &self.parent {
|
|
|
|
|
|
chain.extend(parent.lock().unwrap().scope_chain_info());
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
chain
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// 全変数をダンプ (デバッグ用)
|
|
|
|
|
|
pub fn dump_all_variables(&self) -> HashMap<String, String> {
|
|
|
|
|
|
let mut all_vars = HashMap::new();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 現在のスコープの変数
|
|
|
|
|
|
for (name, value) in self.bindings.lock().unwrap().iter() {
|
|
|
|
|
|
all_vars.insert(
|
|
|
|
|
|
format!("{}::{}", self.scope_name, name),
|
2025-09-17 07:43:07 +09:00
|
|
|
|
value.to_string_box().value,
|
2025-08-09 15:14:44 +09:00
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 親スコープの変数 (再帰的)
|
|
|
|
|
|
if let Some(parent) = &self.parent {
|
|
|
|
|
|
all_vars.extend(parent.lock().unwrap().dump_all_variables());
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
all_vars
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// 新しいBoxを追跡対象に追加
|
|
|
|
|
|
pub fn track_box(&self, nyash_box: Box<dyn NyashBox>) {
|
|
|
|
|
|
self.finalizer.lock().unwrap().track(nyash_box);
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// スコープ終了時にすべてのBoxを解放
|
|
|
|
|
|
pub fn finalize_all_boxes(&self) {
|
|
|
|
|
|
self.finalizer.lock().unwrap().finalize_all();
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// 指定したBoxを解放対象から除外(関数の返り値など)
|
|
|
|
|
|
pub fn exclude_from_finalization(&self, nyash_box: &Box<dyn NyashBox>) {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.finalizer
|
|
|
|
|
|
.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.exclude_from_finalization(nyash_box);
|
2025-08-09 15:14:44 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// PythonのEnvironmentクラスとの互換性レイヤー
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
|
pub struct PythonCompatEnvironment {
|
|
|
|
|
|
inner: Arc<Mutex<Environment>>,
|
|
|
|
|
|
pub _bindings: HashMap<String, Box<dyn NyashBox>>,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl PythonCompatEnvironment {
|
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
inner: Environment::new_global(),
|
|
|
|
|
|
_bindings: HashMap::new(),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
pub fn new_with_parent(parent: Arc<Mutex<Environment>>) -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
inner: Environment::new_child(parent, "python_compat"),
|
|
|
|
|
|
_bindings: HashMap::new(),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// Python版のdefineメソッド互換
|
|
|
|
|
|
pub fn define(&mut self, name: impl Into<String>, value: Box<dyn NyashBox>) {
|
|
|
|
|
|
let name = name.into();
|
|
|
|
|
|
self.inner.lock().unwrap().define(&name, value.clone_box());
|
|
|
|
|
|
self._bindings.insert(name, value);
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// Python版のgetメソッド互換
|
|
|
|
|
|
pub fn get(&self, name: &str) -> Box<dyn NyashBox> {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.inner
|
|
|
|
|
|
.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.get(name)
|
|
|
|
|
|
.unwrap_or_else(|_| Box::new(VoidBox::new()))
|
2025-08-09 15:14:44 +09:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
/// Rustネイティブ環境への参照を取得
|
|
|
|
|
|
pub fn as_native(&self) -> Arc<Mutex<Environment>> {
|
|
|
|
|
|
self.inner.clone()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ===== Tests =====
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::*;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
use crate::box_trait::{BoolBox, IntegerBox, StringBox};
|
|
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_global_environment() {
|
|
|
|
|
|
let env = Environment::new_global();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 変数定義
|
2025-09-17 07:43:07 +09:00
|
|
|
|
env.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.define("test_var", Box::new(StringBox::new("hello")));
|
|
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 変数取得
|
|
|
|
|
|
let value = env.lock().unwrap().get("test_var").unwrap();
|
|
|
|
|
|
let string_val = value.as_any().downcast_ref::<StringBox>().unwrap();
|
|
|
|
|
|
assert_eq!(string_val.value, "hello");
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_nested_scopes() {
|
|
|
|
|
|
let global = Environment::new_global();
|
|
|
|
|
|
let function_scope = Environment::new_child(global.clone(), "test_function");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// グローバルスコープに変数定義
|
2025-09-17 07:43:07 +09:00
|
|
|
|
global
|
|
|
|
|
|
.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.define("global_var", Box::new(StringBox::new("global")));
|
|
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 関数スコープに変数定義
|
2025-09-17 07:43:07 +09:00
|
|
|
|
function_scope
|
|
|
|
|
|
.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.define("local_var", Box::new(StringBox::new("local")));
|
|
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 関数スコープからグローバル変数にアクセス可能
|
|
|
|
|
|
let global_from_function = function_scope.lock().unwrap().get("global_var").unwrap();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
let global_str = global_from_function
|
|
|
|
|
|
.as_any()
|
|
|
|
|
|
.downcast_ref::<StringBox>()
|
|
|
|
|
|
.unwrap();
|
2025-08-09 15:14:44 +09:00
|
|
|
|
assert_eq!(global_str.value, "global");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// グローバルスコープからローカル変数にはアクセス不可
|
|
|
|
|
|
assert!(global.lock().unwrap().get("local_var").is_err());
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_variable_shadowing() {
|
|
|
|
|
|
let global = Environment::new_global();
|
|
|
|
|
|
let local = Environment::new_child(global.clone(), "local_scope");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 同名変数を両スコープに定義
|
2025-09-17 07:43:07 +09:00
|
|
|
|
global
|
|
|
|
|
|
.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.define("same_name", Box::new(StringBox::new("global_value")));
|
|
|
|
|
|
local
|
|
|
|
|
|
.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.define("same_name", Box::new(StringBox::new("local_value")));
|
|
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// ローカルスコープからはローカル値が取得される (シャドウイング)
|
|
|
|
|
|
let value = local.lock().unwrap().get("same_name").unwrap();
|
|
|
|
|
|
let string_val = value.as_any().downcast_ref::<StringBox>().unwrap();
|
|
|
|
|
|
assert_eq!(string_val.value, "local_value");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// グローバルスコープからはグローバル値が取得される
|
|
|
|
|
|
let global_value = global.lock().unwrap().get("same_name").unwrap();
|
|
|
|
|
|
let global_str = global_value.as_any().downcast_ref::<StringBox>().unwrap();
|
|
|
|
|
|
assert_eq!(global_str.value, "global_value");
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_variable_setting() {
|
|
|
|
|
|
let global = Environment::new_global();
|
|
|
|
|
|
let local = Environment::new_child(global.clone(), "local_scope");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// グローバルに変数定義
|
2025-09-17 07:43:07 +09:00
|
|
|
|
global
|
|
|
|
|
|
.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.define("shared_var", Box::new(IntegerBox::new(100)));
|
|
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// ローカルスコープから変数を更新
|
2025-09-17 07:43:07 +09:00
|
|
|
|
local
|
|
|
|
|
|
.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.set("shared_var", Box::new(IntegerBox::new(200)))
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// グローバルスコープの値が更新されている
|
|
|
|
|
|
let updated_value = global.lock().unwrap().get("shared_var").unwrap();
|
|
|
|
|
|
let int_val = updated_value.as_any().downcast_ref::<IntegerBox>().unwrap();
|
|
|
|
|
|
assert_eq!(int_val.value, 200);
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_scope_info() {
|
|
|
|
|
|
let global = Environment::new_global();
|
|
|
|
|
|
let func1 = Environment::new_child(global.clone(), "function1");
|
|
|
|
|
|
let func2 = Environment::new_child(func1.clone(), "function2");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 各スコープに変数を追加
|
2025-09-17 07:43:07 +09:00
|
|
|
|
global
|
|
|
|
|
|
.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.define("g1", Box::new(StringBox::new("global1")));
|
|
|
|
|
|
func1
|
|
|
|
|
|
.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.define("f1", Box::new(StringBox::new("func1")));
|
|
|
|
|
|
func2
|
|
|
|
|
|
.lock()
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.define("f2", Box::new(StringBox::new("func2")));
|
|
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// スコープチェーン情報を確認
|
|
|
|
|
|
let chain = func2.lock().unwrap().scope_chain_info();
|
|
|
|
|
|
assert_eq!(chain.len(), 3);
|
|
|
|
|
|
assert!(chain[0].contains("function2"));
|
|
|
|
|
|
assert!(chain[1].contains("function1"));
|
|
|
|
|
|
assert!(chain[2].contains("global"));
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_python_compat() {
|
|
|
|
|
|
let mut env = PythonCompatEnvironment::new();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// Python互換インターフェースで変数操作
|
|
|
|
|
|
env.define("test", Box::new(BoolBox::new(true)));
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
let value = env.get("test");
|
|
|
|
|
|
let bool_val = value.as_any().downcast_ref::<BoolBox>().unwrap();
|
|
|
|
|
|
assert_eq!(bool_val.value, true);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// _bindingsでも確認可能
|
|
|
|
|
|
assert!(env._bindings.contains_key("test"));
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_error_handling() {
|
|
|
|
|
|
let env = Environment::new_global();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
// 存在しない変数へのアクセス
|
|
|
|
|
|
let result = env.lock().unwrap().get("nonexistent");
|
|
|
|
|
|
assert!(result.is_err());
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-09 15:14:44 +09:00
|
|
|
|
match result {
|
|
|
|
|
|
Err(EnvironmentError::UndefinedVariable { name }) => {
|
|
|
|
|
|
assert_eq!(name, "nonexistent");
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => panic!("Expected UndefinedVariable error"),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-22 12:09:06 +09:00
|
|
|
|
}
|