Phase 12.7: Nyash文法革命とANCP 90%圧縮技法の発見 - 文法改革完了とFunctionBox実装
This commit is contained in:
@ -243,7 +243,7 @@ impl NyashInterpreter {
|
||||
}
|
||||
|
||||
/// 🔥 厳密変数設定: 明示的宣言のみ許可 - Everything is Box哲学
|
||||
pub(super) fn set_variable(&mut self, name: &str, value: Box<dyn NyashBox>) -> Result<(), RuntimeError> {
|
||||
pub(crate) fn set_variable(&mut self, name: &str, value: Box<dyn NyashBox>) -> Result<(), RuntimeError> {
|
||||
let shared_value = Arc::from(value); // Convert Box to Arc
|
||||
|
||||
// 1. outbox変数が存在する場合は更新
|
||||
@ -278,7 +278,7 @@ impl NyashInterpreter {
|
||||
}
|
||||
|
||||
/// local変数を宣言(関数内でのみ有効)
|
||||
pub(super) fn declare_local_variable(&mut self, name: &str, value: Box<dyn NyashBox>) {
|
||||
pub(crate) fn declare_local_variable(&mut self, name: &str, value: Box<dyn NyashBox>) {
|
||||
// Pass-by-share for plugin handle types; by-value (clone) semantics can be applied at call sites
|
||||
#[allow(unused_mut)]
|
||||
let mut store_value = value;
|
||||
@ -292,7 +292,7 @@ impl NyashInterpreter {
|
||||
}
|
||||
|
||||
/// outbox変数を宣言(static関数内で所有権移転)
|
||||
pub(super) fn declare_outbox_variable(&mut self, name: &str, value: Box<dyn NyashBox>) {
|
||||
pub(crate) fn declare_outbox_variable(&mut self, name: &str, value: Box<dyn NyashBox>) {
|
||||
#[allow(unused_mut)]
|
||||
let mut store_value = value;
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
|
||||
@ -13,15 +13,113 @@ mod access;
|
||||
mod builtins;
|
||||
|
||||
use super::*;
|
||||
use std::sync::Arc;
|
||||
// Direct implementation approach to avoid import issues
|
||||
|
||||
// TODO: Fix NullBox import issue later
|
||||
// use crate::NullBox;
|
||||
|
||||
impl NyashInterpreter {
|
||||
/// Build closure environment by capturing 'me' and free variables by value (P1)
|
||||
fn build_closure_env(&mut self, params: &Vec<String>, body: &Vec<ASTNode>) -> Result<crate::boxes::function_box::ClosureEnv, RuntimeError> {
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
let mut env = crate::boxes::function_box::ClosureEnv::new();
|
||||
// Capture 'me' if bound
|
||||
if let Ok(mev) = self.resolve_variable("me") { env.me_value = Some(Arc::downgrade(&mev)); }
|
||||
|
||||
// Collect free variables
|
||||
let mut used: HashSet<String> = HashSet::new();
|
||||
let mut locals: HashSet<String> = HashSet::new();
|
||||
// params are considered local
|
||||
for p in params { locals.insert(p.clone()); }
|
||||
// BFS walk statements
|
||||
fn collect(node: &ASTNode, used: &mut HashSet<String>, locals: &mut HashSet<String>) {
|
||||
match node {
|
||||
ASTNode::Variable { name, .. } => {
|
||||
if !locals.contains(name) && name != "me" && name != "this" { used.insert(name.clone()); }
|
||||
}
|
||||
ASTNode::Local { variables, .. } => { for v in variables { locals.insert(v.clone()); } }
|
||||
ASTNode::Assignment { target, value, .. } => { collect(target, used, locals); collect(value, used, locals); }
|
||||
ASTNode::BinaryOp { left, right, .. } => { collect(left, used, locals); collect(right, used, locals); }
|
||||
ASTNode::UnaryOp { operand, .. } => { collect(operand, used, locals); }
|
||||
ASTNode::MethodCall { object, arguments, .. } => { collect(object, used, locals); for a in arguments { collect(a, used, locals);} }
|
||||
ASTNode::FunctionCall { arguments, .. } => { for a in arguments { collect(a, used, locals);} }
|
||||
ASTNode::Call { callee, arguments, .. } => { collect(callee, used, locals); for a in arguments { collect(a, used, locals);} }
|
||||
ASTNode::FieldAccess { object, .. } => { collect(object, used, locals); }
|
||||
ASTNode::New { arguments, .. } => { for a in arguments { collect(a, used, locals);} }
|
||||
ASTNode::If { condition, then_body, else_body, .. } => {
|
||||
collect(condition, used, locals);
|
||||
for st in then_body { collect(st, used, locals); }
|
||||
if let Some(eb) = else_body { for st in eb { collect(st, used, locals); } }
|
||||
}
|
||||
ASTNode::Loop { condition, body, .. } => { collect(condition, used, locals); for st in body { collect(st, used, locals);} }
|
||||
ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => {
|
||||
for st in try_body { collect(st, used, locals); }
|
||||
for c in catch_clauses { for st in &c.body { collect(st, used, locals); } }
|
||||
if let Some(fb) = finally_body { for st in fb { collect(st, used, locals); } }
|
||||
}
|
||||
ASTNode::Throw { expression, .. } => { collect(expression, used, locals); }
|
||||
ASTNode::Print { expression, .. } => { collect(expression, used, locals); }
|
||||
ASTNode::Return { value, .. } => { if let Some(v) = value { collect(v, used, locals); } }
|
||||
ASTNode::AwaitExpression { expression, .. } => { collect(expression, used, locals); }
|
||||
ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => {
|
||||
collect(scrutinee, used, locals);
|
||||
for (_, e) in arms { collect(e, used, locals); }
|
||||
collect(else_expr, used, locals);
|
||||
}
|
||||
ASTNode::Program { statements, .. } => { for st in statements { collect(st, used, locals); } }
|
||||
ASTNode::FunctionDeclaration { params, body, .. } => {
|
||||
let mut inner = locals.clone();
|
||||
for p in params { inner.insert(p.clone()); }
|
||||
for st in body { collect(st, used, &mut inner); }
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
for st in body { collect(st, &mut used, &mut locals); }
|
||||
|
||||
// Materialize captures: local by-ref via RefCellBox, others by-value
|
||||
for name in used.into_iter() {
|
||||
if let Some(local_arc) = self.local_vars.get(&name) {
|
||||
let lb: &dyn NyashBox = &**local_arc;
|
||||
// If already RefCellBox, reuse inner; else wrap and replace local binding
|
||||
if let Some(rc) = lb.as_any().downcast_ref::<crate::boxes::ref_cell_box::RefCellBox>() {
|
||||
env.captures.insert(name.clone(), rc.share_box());
|
||||
} else {
|
||||
// wrap existing into RefCell and replace local binding
|
||||
let wrapped = crate::boxes::ref_cell_box::RefCellBox::new(lb.clone_box());
|
||||
self.local_vars.insert(name.clone(), wrapped.clone_arc());
|
||||
env.captures.insert(name, wrapped.share_box());
|
||||
}
|
||||
} else {
|
||||
// non-local (global/static): by-value capture
|
||||
if let Ok(v) = self.resolve_variable(&name) { env.captures.insert(name, v.clone_or_share()); }
|
||||
}
|
||||
}
|
||||
Ok(env)
|
||||
}
|
||||
/// 式を実行 - Expression evaluation engine
|
||||
pub(super) fn execute_expression(&mut self, expression: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
match expression {
|
||||
// P1: allow block (Program) as expression; value = last statement's value
|
||||
ASTNode::Program { statements, .. } => {
|
||||
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
|
||||
let last = statements.len().saturating_sub(1);
|
||||
for (i, st) in statements.iter().enumerate() {
|
||||
let prev = self.discard_context;
|
||||
self.discard_context = i != last;
|
||||
result = self.execute_statement(st)?;
|
||||
self.discard_context = prev;
|
||||
match &self.control_flow {
|
||||
ControlFlow::Break => { return Err(RuntimeError::BreakOutsideLoop); }
|
||||
ControlFlow::Continue => { return Err(RuntimeError::BreakOutsideLoop); }
|
||||
ControlFlow::Return(_) => { return Err(RuntimeError::ReturnOutsideFunction); }
|
||||
ControlFlow::Throw(_) => { return Err(RuntimeError::UncaughtException); }
|
||||
ControlFlow::None => {}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
ASTNode::Literal { value, .. } => {
|
||||
Ok(value.to_nyash_box())
|
||||
}
|
||||
@ -125,10 +223,84 @@ impl NyashInterpreter {
|
||||
ASTNode::FunctionCall { name, arguments, .. } => {
|
||||
self.execute_function_call(name, arguments)
|
||||
}
|
||||
ASTNode::Call { callee, arguments, .. } => {
|
||||
// callee を評価して FunctionBox なら本体を実行
|
||||
let callee_val = self.execute_expression(callee)?;
|
||||
if let Some(fun) = callee_val.as_any().downcast_ref::<crate::boxes::function_box::FunctionBox>() {
|
||||
// 引数評価
|
||||
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
||||
for a in arguments { arg_values.push(self.execute_expression(a)?); }
|
||||
if arg_values.len() != fun.params.len() {
|
||||
return Err(RuntimeError::InvalidOperation { message: format!("Function expects {} args, got {}", fun.params.len(), arg_values.len()) });
|
||||
}
|
||||
// スコープ保存
|
||||
let saved_locals = self.save_local_vars();
|
||||
self.local_vars.clear();
|
||||
// キャプチャ注入(by-value)
|
||||
for (k, v) in fun.env.captures.iter() { self.declare_local_variable(k, v.clone_or_share()); }
|
||||
if let Some(me_w) = &fun.env.me_value {
|
||||
if let Some(me_arc) = me_w.upgrade() {
|
||||
self.declare_local_variable("me", (*me_arc).clone_or_share());
|
||||
} else {
|
||||
self.declare_local_variable("me", Box::new(crate::boxes::null_box::NullBox::new()));
|
||||
}
|
||||
}
|
||||
for (p, v) in fun.params.iter().zip(arg_values.iter()) {
|
||||
self.declare_local_variable(p, v.clone_or_share());
|
||||
}
|
||||
// 実行
|
||||
crate::runtime::global_hooks::push_task_scope();
|
||||
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
|
||||
for st in &fun.body {
|
||||
result = self.execute_statement(st)?;
|
||||
if let super::ControlFlow::Return(rv) = &self.control_flow {
|
||||
result = rv.clone_box();
|
||||
self.control_flow = super::ControlFlow::None;
|
||||
break;
|
||||
}
|
||||
}
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
self.restore_local_vars(saved_locals);
|
||||
Ok(result)
|
||||
} else if let ASTNode::Lambda { params, body, .. } = callee.as_ref() {
|
||||
// 直書きLambdaは従来通り実行(後方互換)
|
||||
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
||||
for a in arguments { arg_values.push(self.execute_expression(a)?); }
|
||||
if arg_values.len() != params.len() {
|
||||
return Err(RuntimeError::InvalidOperation { message: format!("Lambda expects {} args, got {}", params.len(), arg_values.len()) });
|
||||
}
|
||||
let saved_locals = self.save_local_vars();
|
||||
self.local_vars.clear();
|
||||
for (p, v) in params.iter().zip(arg_values.iter()) { self.declare_local_variable(p, v.clone_or_share()); }
|
||||
crate::runtime::global_hooks::push_task_scope();
|
||||
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
|
||||
for st in body { result = self.execute_statement(st)?; if let super::ControlFlow::Return(rv) = &self.control_flow { result = rv.clone_box(); self.control_flow = super::ControlFlow::None; break; } }
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
self.restore_local_vars(saved_locals);
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(RuntimeError::InvalidOperation { message: "Callee is not callable".to_string() })
|
||||
}
|
||||
}
|
||||
|
||||
ASTNode::Arrow { sender, receiver, .. } => {
|
||||
self.execute_arrow(sender, receiver)
|
||||
}
|
||||
ASTNode::QMarkPropagate { expression, .. } => {
|
||||
let v = self.execute_expression(expression)?;
|
||||
if let Some(res) = v.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
|
||||
// ok -> unwrap, err -> early return (propagate)
|
||||
if matches!(res, crate::boxes::result::NyashResultBox::Ok(_)) {
|
||||
return Ok(res.get_value());
|
||||
} else {
|
||||
// Early return the Result itself
|
||||
self.control_flow = super::ControlFlow::Return(v.clone_box());
|
||||
return Ok(Box::new(crate::box_trait::VoidBox::new()));
|
||||
}
|
||||
}
|
||||
// Not a Result: pass-through
|
||||
Ok(v)
|
||||
}
|
||||
ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => {
|
||||
let val = self.execute_expression(scrutinee)?;
|
||||
let sval = val.to_string_box().value;
|
||||
@ -147,6 +319,11 @@ impl NyashInterpreter {
|
||||
}
|
||||
self.execute_expression(else_expr)
|
||||
}
|
||||
ASTNode::Lambda { params, body, .. } => {
|
||||
// 値としての関数ボックスを生成(ClosureEnv: me/by-val captures)
|
||||
let env = self.build_closure_env(¶ms, body)?;
|
||||
Ok(Box::new(crate::boxes::function_box::FunctionBox::with_env(params.clone(), body.clone(), env)))
|
||||
}
|
||||
|
||||
ASTNode::Include { filename, .. } => {
|
||||
// include式: 最初のstatic boxを返す
|
||||
|
||||
@ -11,6 +11,7 @@ use super::super::*;
|
||||
use crate::boxes::ResultBox;
|
||||
use crate::box_trait::{StringBox, NyashBox};
|
||||
use crate::boxes::FileBox;
|
||||
use crate::boxes::ref_cell_box::RefCellBox;
|
||||
// use crate::bid::plugin_box::PluginFileBox; // legacy - FileBox専用
|
||||
|
||||
impl NyashInterpreter {
|
||||
@ -108,6 +109,26 @@ impl NyashInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
/// RefCellBox のメソッド: get()/set(value)
|
||||
pub(in crate::interpreter) fn execute_refcell_method(&mut self, cell: &RefCellBox, method: &str, arguments: &[ASTNode])
|
||||
-> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
match method {
|
||||
"get" => {
|
||||
if !arguments.is_empty() {
|
||||
return Err(RuntimeError::InvalidOperation { message: format!("get() expects 0 arguments, got {}", arguments.len()) });
|
||||
}
|
||||
Ok(cell.borrow())
|
||||
}
|
||||
"set" => {
|
||||
if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { message: format!("set() expects 1 argument, got {}", arguments.len()) }); }
|
||||
let v = self.execute_expression(&arguments[0])?;
|
||||
cell.replace(v);
|
||||
Ok(Box::new(crate::box_trait::VoidBox::new()))
|
||||
}
|
||||
_ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for RefCellBox", method) })
|
||||
}
|
||||
}
|
||||
|
||||
/* legacy - PluginFileBox専用
|
||||
/// 汎用プラグインメソッド呼び出し実行 (BID-FFI system)
|
||||
/// Handles generic plugin method calls via dynamic method discovery
|
||||
|
||||
@ -5,6 +5,7 @@ use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore};
|
||||
use crate::boxes::{ArrayBox, FloatBox, BufferBox, ResultBox, FutureBox, JSONBox, HttpClientBox, StreamBox, RegexBox, MathBox};
|
||||
use crate::boxes::{null_box, time_box, map_box, random_box, sound_box, debug_box, console_box};
|
||||
use crate::boxes::{gc_config_box::GcConfigBox, debug_config_box::DebugConfigBox};
|
||||
use crate::boxes::ref_cell_box::RefCellBox as RcCell;
|
||||
use crate::boxes::file;
|
||||
use crate::channel_box::ChannelBox;
|
||||
use super::{NyashInterpreter, RuntimeError};
|
||||
@ -118,6 +119,10 @@ impl NyashInterpreter {
|
||||
if let Some(b) = obj.as_any().downcast_ref::<DebugConfigBox>() {
|
||||
return Some(self.execute_debug_config_method(b, method, arguments));
|
||||
}
|
||||
// RefCellBox (by-ref proxy)
|
||||
if let Some(b) = obj.as_any().downcast_ref::<RcCell>() {
|
||||
return Some(self.execute_refcell_method(b, method, arguments));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::boxes::ref_cell_box::RefCellBox;
|
||||
use super::BuiltinStdlib;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -46,7 +47,7 @@ impl NyashInterpreter {
|
||||
}
|
||||
}
|
||||
/// 文を実行 - Core statement execution engine
|
||||
pub(super) fn execute_statement(&mut self, statement: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
pub(crate) fn execute_statement(&mut self, statement: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
match statement {
|
||||
ASTNode::Assignment { target, value, .. } => {
|
||||
self.execute_assignment(target, value)
|
||||
@ -340,6 +341,13 @@ impl NyashInterpreter {
|
||||
val.clone_box()
|
||||
}
|
||||
};
|
||||
// セル反映: 既存が RefCellBox なら中身のみ置換
|
||||
if let Ok(existing) = self.resolve_variable(name) {
|
||||
if let Some(rc) = existing.as_any().downcast_ref::<RefCellBox>() {
|
||||
rc.replace(assigned);
|
||||
return Ok(val);
|
||||
}
|
||||
}
|
||||
self.set_variable(name, assigned)?;
|
||||
Ok(val)
|
||||
}
|
||||
@ -399,6 +407,13 @@ impl NyashInterpreter {
|
||||
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
|
||||
{ val.clone_box() }
|
||||
};
|
||||
// セル反映: 既存フィールドが RefCellBox なら中身を置換
|
||||
if let Some(cur) = instance.get_field(field) {
|
||||
if let Some(rc) = cur.as_any().downcast_ref::<RefCellBox>() {
|
||||
rc.replace(stored);
|
||||
return Ok(val);
|
||||
}
|
||||
}
|
||||
instance.set_field(field, Arc::from(stored))
|
||||
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
|
||||
Ok(val)
|
||||
@ -433,6 +448,13 @@ impl NyashInterpreter {
|
||||
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
|
||||
{ val.clone_box() }
|
||||
};
|
||||
// セル反映: 既存フィールドが RefCellBox なら中身を置換
|
||||
if let Some(cur) = instance.get_field(field) {
|
||||
if let Some(rc) = cur.as_any().downcast_ref::<RefCellBox>() {
|
||||
rc.replace(stored);
|
||||
return Ok(val);
|
||||
}
|
||||
}
|
||||
instance.set_field(field, Arc::from(stored))
|
||||
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
|
||||
Ok(val)
|
||||
|
||||
Reference in New Issue
Block a user