🎉 initial commit: Nyash Programming Language完成版

🚀 主要機能:
• Everything is Box哲学による革新的アーキテクチャ
• WebAssemblyブラウザー対応プレイグラウンド
• アーティスト協同制作デモ - 複数Boxインスタンス実証
• 視覚的デバッグシステム - DebugBox完全統合
• static box Mainパターン - メモリ安全設計

 言語機能:
• NOT/AND/OR/除算演算子完全実装
• ジェネリクス/テンプレートシステム
• 非同期処理(nowait/await)
• try/catchエラーハンドリング
• Canvas統合グラフィックス

🎨 ブラウザー体験:
• 9種類のインタラクティブデモ
• リアルタイムコード実行
• WebCanvas/WebConsole/WebDisplay
• モバイル対応完了

🤖 Built with Claude Code collaboration
Ready for public release!
This commit is contained in:
Moe Charm
2025-08-09 15:14:44 +09:00
commit 0bed0c0271
129 changed files with 33189 additions and 0 deletions

963
src/ast.rs Normal file
View File

@ -0,0 +1,963 @@
/*!
* Nyash AST (Abstract Syntax Tree) - Rust Implementation
*
* Python版nyashc_v4.pyのAST構造をRustで完全再実装
* Everything is Box哲学に基づく型安全なAST設計
*/
use crate::box_trait::NyashBox;
use std::collections::HashMap;
use std::fmt;
/// ソースコード位置情報 - エラー報告とデバッグの革命
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Span {
pub start: usize, // 開始位置(バイトオフセット)
pub end: usize, // 終了位置(バイトオフセット)
pub line: usize, // 行番号1から開始
pub column: usize, // 列番号1から開始
}
impl Span {
/// 新しいSpanを作成
pub fn new(start: usize, end: usize, line: usize, column: usize) -> Self {
Self { start, end, line, column }
}
/// デフォルトのSpan不明な位置
pub fn unknown() -> Self {
Self { start: 0, end: 0, line: 1, column: 1 }
}
/// 2つのSpanを結合開始位置から終了位置まで
pub fn merge(&self, other: Span) -> Span {
Span {
start: self.start.min(other.start),
end: self.end.max(other.end),
line: self.line,
column: self.column,
}
}
/// ソースコードから該当箇所を抽出してエラー表示用文字列を生成
pub fn error_context(&self, source: &str) -> String {
let lines: Vec<&str> = source.lines().collect();
if self.line == 0 || self.line > lines.len() {
return format!("line {}, column {}", self.line, self.column);
}
let line_content = lines[self.line - 1];
let mut context = String::new();
// 行番号とソース行を表示
context.push_str(&format!(" |\n{:3} | {}\n", self.line, line_content));
// カーソル位置を表示(簡易版)
if self.column > 0 && self.column <= line_content.len() + 1 {
context.push_str(" | ");
for _ in 1..self.column {
context.push(' ');
}
let span_length = if self.end > self.start {
(self.end - self.start).min(line_content.len() - self.column + 1)
} else {
1
};
for _ in 0..span_length.max(1) {
context.push('^');
}
context.push('\n');
}
context
}
/// 位置情報の文字列表現
pub fn location_string(&self) -> String {
format!("line {}, column {}", self.line, self.column)
}
}
impl fmt::Display for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "line {}, column {}", self.line, self.column)
}
}
/// 🌟 AST分類システム - ChatGPTアドバイス統合による3層アーキテクチャ
/// Structure/Expression/Statement の明確な分離による型安全性向上
/// ASTードの種類分類
#[derive(Debug, Clone, PartialEq)]
pub enum ASTNodeType {
Structure, // 構造定義: box, function, if, loop, try/catch
Expression, // 式: リテラル, 変数, 演算, 呼び出し
Statement, // 文: 代入, return, break, include
}
/// 構造ノード - 言語の基本構造を定義
#[derive(Debug, Clone)]
pub enum StructureNode {
BoxDeclaration {
name: String,
fields: Vec<String>,
methods: Vec<ASTNode>,
constructors: Vec<ASTNode>,
init_fields: Vec<String>,
is_interface: bool,
extends: Option<String>,
implements: Vec<String>,
/// 🔥 ジェネリクス型パラメータ (例: ["T", "U"])
type_parameters: Vec<String>,
/// 🔥 Static boxかどうかのフラグ
is_static: bool,
/// 🔥 Static初期化ブロック (static { ... })
static_init: Option<Vec<ASTNode>>,
span: Span,
},
FunctionDeclaration {
name: String,
params: Vec<String>,
body: Vec<ASTNode>,
is_static: bool, // 🔥 静的メソッドフラグ
span: Span,
},
IfStructure {
condition: Box<ASTNode>,
then_body: Vec<ASTNode>,
else_body: Option<Vec<ASTNode>>,
span: Span,
},
LoopStructure {
condition: Box<ASTNode>,
body: Vec<ASTNode>,
span: Span,
},
TryCatchStructure {
try_body: Vec<ASTNode>,
catch_clauses: Vec<CatchClause>,
finally_body: Option<Vec<ASTNode>>,
span: Span,
},
}
/// 式ノード - 値を生成する表現
#[derive(Debug, Clone)]
pub enum ExpressionNode {
Literal {
value: LiteralValue,
span: Span,
},
Variable {
name: String,
span: Span,
},
BinaryOperation {
operator: BinaryOperator,
left: Box<ASTNode>,
right: Box<ASTNode>,
span: Span,
},
UnaryOperation {
operator: UnaryOperator,
operand: Box<ASTNode>,
span: Span,
},
FunctionCall {
name: String,
arguments: Vec<ASTNode>,
span: Span,
},
MethodCall {
object: Box<ASTNode>,
method: String,
arguments: Vec<ASTNode>,
span: Span,
},
FieldAccess {
object: Box<ASTNode>,
field: String,
span: Span,
},
NewExpression {
class: String,
arguments: Vec<ASTNode>,
/// 🔥 ジェネリクス型引数 (例: ["IntegerBox", "StringBox"])
type_arguments: Vec<String>,
span: Span,
},
ThisExpression {
span: Span,
},
MeExpression {
span: Span,
},
}
/// 文ノード - 実行可能なアクション
#[derive(Debug, Clone)]
pub enum StatementNode {
Assignment {
target: Box<ASTNode>,
value: Box<ASTNode>,
span: Span,
},
Print {
expression: Box<ASTNode>,
span: Span,
},
Return {
value: Option<Box<ASTNode>>,
span: Span,
},
Break {
span: Span,
},
Include {
filename: String,
span: Span,
},
Local {
variables: Vec<String>,
span: Span,
},
Throw {
exception_type: String,
message: Box<ASTNode>,
span: Span,
},
Expression {
expr: Box<ASTNode>,
span: Span,
},
}
/// Catch節の構造体
#[derive(Debug, Clone)]
pub struct CatchClause {
pub exception_type: Option<String>, // None = catch-all
pub variable_name: Option<String>, // 例外を受け取る変数名
pub body: Vec<ASTNode>, // catch本体
pub span: Span, // ソースコード位置
}
/// リテラル値の型 (Clone可能)
#[derive(Debug, Clone)]
pub enum LiteralValue {
String(String),
Integer(i64),
Float(f64), // 浮動小数点数サポート追加
Bool(bool),
Void,
}
impl LiteralValue {
/// LiteralValueをNyashBoxに変換
pub fn to_nyash_box(&self) -> Box<dyn NyashBox> {
use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox};
use crate::boxes::math_box::FloatBox;
match self {
LiteralValue::String(s) => Box::new(StringBox::new(s)),
LiteralValue::Integer(i) => Box::new(IntegerBox::new(*i)),
LiteralValue::Float(f) => Box::new(FloatBox::new(*f)),
LiteralValue::Bool(b) => Box::new(BoolBox::new(*b)),
LiteralValue::Void => Box::new(VoidBox::new()),
}
}
/// NyashBoxからLiteralValueに変換
pub fn from_nyash_box(box_val: &dyn NyashBox) -> Option<LiteralValue> {
#[allow(unused_imports)]
use std::any::Any;
use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox};
use crate::boxes::math_box::FloatBox;
if let Some(string_box) = box_val.as_any().downcast_ref::<StringBox>() {
Some(LiteralValue::String(string_box.value.clone()))
} else if let Some(int_box) = box_val.as_any().downcast_ref::<IntegerBox>() {
Some(LiteralValue::Integer(int_box.value))
} else if let Some(float_box) = box_val.as_any().downcast_ref::<FloatBox>() {
Some(LiteralValue::Float(float_box.value))
} else if let Some(bool_box) = box_val.as_any().downcast_ref::<BoolBox>() {
Some(LiteralValue::Bool(bool_box.value))
} else if box_val.as_any().downcast_ref::<VoidBox>().is_some() {
Some(LiteralValue::Void)
} else {
None
}
}
}
impl fmt::Display for LiteralValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LiteralValue::String(s) => write!(f, "\"{}\"", s),
LiteralValue::Integer(i) => write!(f, "{}", i),
LiteralValue::Float(fl) => write!(f, "{}", fl),
LiteralValue::Bool(b) => write!(f, "{}", b),
LiteralValue::Void => write!(f, "void"),
}
}
}
/// 単項演算子の種類
#[derive(Debug, Clone, PartialEq)]
pub enum UnaryOperator {
Minus, // -x
Not, // not x
}
/// 二項演算子の種類
#[derive(Debug, Clone, PartialEq)]
pub enum BinaryOperator {
Add,
Subtract,
Multiply,
Divide,
Equal,
NotEqual,
Less,
Greater,
LessEqual,
GreaterEqual,
And,
Or,
}
impl fmt::Display for UnaryOperator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let symbol = match self {
UnaryOperator::Minus => "-",
UnaryOperator::Not => "not",
};
write!(f, "{}", symbol)
}
}
impl fmt::Display for BinaryOperator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let symbol = match self {
BinaryOperator::Add => "+",
BinaryOperator::Subtract => "-",
BinaryOperator::Multiply => "*",
BinaryOperator::Divide => "/",
BinaryOperator::Equal => "==",
BinaryOperator::NotEqual => "!=",
BinaryOperator::Less => "<",
BinaryOperator::Greater => ">",
BinaryOperator::LessEqual => "<=",
BinaryOperator::GreaterEqual => ">=",
BinaryOperator::And => "&&",
BinaryOperator::Or => "||",
};
write!(f, "{}", symbol)
}
}
/// AST Node - Everything is Box哲学に基づく統一構造
#[derive(Debug, Clone)]
pub enum ASTNode {
/// プログラム全体 - 文のリスト
Program {
statements: Vec<ASTNode>,
span: Span,
},
// ===== 文 (Statements) =====
/// 代入文: target = value
Assignment {
target: Box<ASTNode>,
value: Box<ASTNode>,
span: Span,
},
/// print文: print(expression)
Print {
expression: Box<ASTNode>,
span: Span,
},
/// if文: if condition { then_body } else { else_body }
If {
condition: Box<ASTNode>,
then_body: Vec<ASTNode>,
else_body: Option<Vec<ASTNode>>,
span: Span,
},
/// loop文: loop(condition) { body } のみ
Loop {
condition: Box<ASTNode>,
body: Vec<ASTNode>,
span: Span,
},
/// return文: return value
Return {
value: Option<Box<ASTNode>>,
span: Span,
},
/// break文
Break {
span: Span,
},
/// nowait文: nowait variable = expression
Nowait {
variable: String,
expression: Box<ASTNode>,
span: Span,
},
/// await式: await expression
AwaitExpression {
expression: Box<ASTNode>,
span: Span,
},
/// arrow文: (sender >> receiver).method(args)
Arrow {
sender: Box<ASTNode>,
receiver: Box<ASTNode>,
span: Span,
},
/// try/catch/finally文: try { ... } catch (Type e) { ... } finally { ... }
TryCatch {
try_body: Vec<ASTNode>,
catch_clauses: Vec<CatchClause>,
finally_body: Option<Vec<ASTNode>>,
span: Span,
},
/// throw文: throw expression
Throw {
expression: Box<ASTNode>,
span: Span,
},
// ===== 宣言 (Declarations) =====
/// box宣言: box Name { fields... methods... }
BoxDeclaration {
name: String,
fields: Vec<String>,
methods: HashMap<String, ASTNode>, // method_name -> FunctionDeclaration
constructors: HashMap<String, ASTNode>, // constructor_key -> FunctionDeclaration
init_fields: Vec<String>, // initブロック内のフィールド定義
is_interface: bool, // interface box かどうか
extends: Option<String>, // 継承元のBox名
implements: Vec<String>, // 実装するinterface名のリスト
type_parameters: Vec<String>, // 🔥 ジェネリクス型パラメータ (例: ["T", "U"])
/// 🔥 Static boxかどうかのフラグ
is_static: bool,
/// 🔥 Static初期化ブロック (static { ... })
static_init: Option<Vec<ASTNode>>,
span: Span,
},
/// 関数宣言: functionName(params) { body }
FunctionDeclaration {
name: String,
params: Vec<String>,
body: Vec<ASTNode>,
is_static: bool, // 🔥 静的メソッドフラグ
span: Span,
},
/// グローバル変数: global name = value
GlobalVar {
name: String,
value: Box<ASTNode>,
span: Span,
},
// ===== 式 (Expressions) =====
/// リテラル値: "string", 42, true, etc
Literal {
value: LiteralValue,
span: Span,
},
/// 変数参照: variableName
Variable {
name: String,
span: Span,
},
/// 単項演算: operator operand
UnaryOp {
operator: UnaryOperator,
operand: Box<ASTNode>,
span: Span,
},
/// 二項演算: left operator right
BinaryOp {
operator: BinaryOperator,
left: Box<ASTNode>,
right: Box<ASTNode>,
span: Span,
},
/// メソッド呼び出し: object.method(arguments)
MethodCall {
object: Box<ASTNode>,
method: String,
arguments: Vec<ASTNode>,
span: Span,
},
/// フィールドアクセス: object.field
FieldAccess {
object: Box<ASTNode>,
field: String,
span: Span,
},
/// コンストラクタ呼び出し: new ClassName(arguments)
New {
class: String,
arguments: Vec<ASTNode>,
type_arguments: Vec<String>, // 🔥 ジェネリクス型引数 (例: ["IntegerBox", "StringBox"])
span: Span,
},
/// this参照
This {
span: Span,
},
/// me参照
Me {
span: Span,
},
/// thisフィールドアクセス: this.field
ThisField {
field: String,
span: Span,
},
/// meフィールドアクセス: me.field
MeField {
field: String,
span: Span,
},
/// ファイル読み込み: include "filename.nyash"
Include {
filename: String,
span: Span,
},
/// ローカル変数宣言: local x, y, z
Local {
variables: Vec<String>,
/// 初期化値変数と同じ順序、Noneは初期化なし
initial_values: Vec<Option<Box<ASTNode>>>,
span: Span,
},
/// Outbox変数宣言: outbox x, y, z (static関数内専用)
Outbox {
variables: Vec<String>,
/// 初期化値変数と同じ順序、Noneは初期化なし
initial_values: Vec<Option<Box<ASTNode>>>,
span: Span,
},
/// 関数呼び出し: functionName(arguments)
FunctionCall {
name: String,
arguments: Vec<ASTNode>,
span: Span,
},
}
impl ASTNode {
/// AST nodeの種類を文字列で取得 (デバッグ用)
pub fn node_type(&self) -> &'static str {
match self {
ASTNode::Program { .. } => "Program",
ASTNode::Assignment { .. } => "Assignment",
ASTNode::Print { .. } => "Print",
ASTNode::If { .. } => "If",
ASTNode::Loop { .. } => "Loop",
ASTNode::Return { .. } => "Return",
ASTNode::Break { .. } => "Break",
ASTNode::BoxDeclaration { .. } => "BoxDeclaration",
ASTNode::FunctionDeclaration { .. } => "FunctionDeclaration",
ASTNode::GlobalVar { .. } => "GlobalVar",
ASTNode::Literal { .. } => "Literal",
ASTNode::Variable { .. } => "Variable",
ASTNode::UnaryOp { .. } => "UnaryOp",
ASTNode::BinaryOp { .. } => "BinaryOp",
ASTNode::MethodCall { .. } => "MethodCall",
ASTNode::FieldAccess { .. } => "FieldAccess",
ASTNode::New { .. } => "New",
ASTNode::This { .. } => "This",
ASTNode::Me { .. } => "Me",
ASTNode::ThisField { .. } => "ThisField",
ASTNode::MeField { .. } => "MeField",
ASTNode::Include { .. } => "Include",
ASTNode::Local { .. } => "Local",
ASTNode::Outbox { .. } => "Outbox",
ASTNode::FunctionCall { .. } => "FunctionCall",
ASTNode::Nowait { .. } => "Nowait",
ASTNode::Arrow { .. } => "Arrow",
ASTNode::TryCatch { .. } => "TryCatch",
ASTNode::Throw { .. } => "Throw",
ASTNode::AwaitExpression { .. } => "AwaitExpression",
}
}
/// 🌟 AST分類 - ChatGPTアドバイス統合による革新的分類システム
/// Structure/Expression/Statement の明確な分離
pub fn classify(&self) -> ASTNodeType {
match self {
// Structure nodes - 言語の基本構造
ASTNode::BoxDeclaration { .. } => ASTNodeType::Structure,
ASTNode::FunctionDeclaration { .. } => ASTNodeType::Structure,
ASTNode::If { .. } => ASTNodeType::Structure,
ASTNode::Loop { .. } => ASTNodeType::Structure,
ASTNode::TryCatch { .. } => ASTNodeType::Structure,
// Expression nodes - 値を生成する表現
ASTNode::Literal { .. } => ASTNodeType::Expression,
ASTNode::Variable { .. } => ASTNodeType::Expression,
ASTNode::BinaryOp { .. } => ASTNodeType::Expression,
ASTNode::UnaryOp { .. } => ASTNodeType::Expression,
ASTNode::FunctionCall { .. } => ASTNodeType::Expression,
ASTNode::MethodCall { .. } => ASTNodeType::Expression,
ASTNode::FieldAccess { .. } => ASTNodeType::Expression,
ASTNode::New { .. } => ASTNodeType::Expression,
ASTNode::This { .. } => ASTNodeType::Expression,
ASTNode::Me { .. } => ASTNodeType::Expression,
ASTNode::ThisField { .. } => ASTNodeType::Expression,
ASTNode::MeField { .. } => ASTNodeType::Expression,
// Statement nodes - 実行可能なアクション
ASTNode::Program { .. } => ASTNodeType::Statement, // プログラム全体
ASTNode::Assignment { .. } => ASTNodeType::Statement,
ASTNode::Print { .. } => ASTNodeType::Statement,
ASTNode::Return { .. } => ASTNodeType::Statement,
ASTNode::Break { .. } => ASTNodeType::Statement,
ASTNode::GlobalVar { .. } => ASTNodeType::Statement,
ASTNode::Include { .. } => ASTNodeType::Statement,
ASTNode::Local { .. } => ASTNodeType::Statement,
ASTNode::Outbox { .. } => ASTNodeType::Statement,
ASTNode::Nowait { .. } => ASTNodeType::Statement,
ASTNode::Arrow { .. } => ASTNodeType::Statement,
ASTNode::Throw { .. } => ASTNodeType::Statement,
ASTNode::AwaitExpression { .. } => ASTNodeType::Expression,
}
}
/// 🎯 構造パターンチェック - 2段階パーサー用
pub fn is_structure(&self) -> bool {
matches!(self.classify(), ASTNodeType::Structure)
}
/// ⚡ 式パターンチェック - 評価エンジン用
pub fn is_expression(&self) -> bool {
matches!(self.classify(), ASTNodeType::Expression)
}
/// 📝 文パターンチェック - 実行エンジン用
pub fn is_statement(&self) -> bool {
matches!(self.classify(), ASTNodeType::Statement)
}
/// AST nodeの詳細情報を取得 (デバッグ用)
pub fn info(&self) -> String {
match self {
ASTNode::Program { statements, .. } => {
format!("Program({} statements)", statements.len())
}
ASTNode::Assignment { target, .. } => {
format!("Assignment(target: {})", target.info())
}
ASTNode::Print { .. } => "Print".to_string(),
ASTNode::If { .. } => "If".to_string(),
ASTNode::Loop { condition: _, body, .. } => {
format!("Loop({} statements)", body.len())
}
ASTNode::Return { value, .. } => {
if value.is_some() {
"Return(with value)".to_string()
} else {
"Return(void)".to_string()
}
}
ASTNode::Break { .. } => "Break".to_string(),
ASTNode::BoxDeclaration { name, fields, methods, constructors, is_interface, extends, implements, .. } => {
let mut desc = if *is_interface {
format!("InterfaceBox({}, {} methods", name, methods.len())
} else {
format!("BoxDeclaration({}, {} fields, {} methods, {} constructors", name, fields.len(), methods.len(), constructors.len())
};
if let Some(parent) = extends {
desc.push_str(&format!(", extends {}", parent));
}
if !implements.is_empty() {
desc.push_str(&format!(", implements [{}]", implements.join(", ")));
}
desc.push(')');
desc
}
ASTNode::FunctionDeclaration { name, params, body, is_static, .. } => {
let static_str = if *is_static { "static " } else { "" };
format!("FunctionDeclaration({}{}({}), {} statements)",
static_str, name, params.join(", "), body.len())
}
ASTNode::GlobalVar { name, .. } => {
format!("GlobalVar({})", name)
}
ASTNode::Literal { .. } => "Literal".to_string(),
ASTNode::Variable { name, .. } => {
format!("Variable({})", name)
}
ASTNode::UnaryOp { operator, .. } => {
format!("UnaryOp({})", operator)
}
ASTNode::BinaryOp { operator, .. } => {
format!("BinaryOp({})", operator)
}
ASTNode::MethodCall { method, arguments, .. } => {
format!("MethodCall({}, {} args)", method, arguments.len())
}
ASTNode::FieldAccess { field, .. } => {
format!("FieldAccess({})", field)
}
ASTNode::New { class, arguments, type_arguments, .. } => {
if type_arguments.is_empty() {
format!("New({}, {} args)", class, arguments.len())
} else {
format!("New({}<{}>, {} args)", class, type_arguments.join(", "), arguments.len())
}
}
ASTNode::This { .. } => "This".to_string(),
ASTNode::Me { .. } => "Me".to_string(),
ASTNode::ThisField { field, .. } => {
format!("ThisField({})", field)
}
ASTNode::MeField { field, .. } => {
format!("MeField({})", field)
}
ASTNode::Include { filename, .. } => {
format!("Include({})", filename)
}
ASTNode::Local { variables, .. } => {
format!("Local({})", variables.join(", "))
}
ASTNode::Outbox { variables, .. } => {
format!("Outbox({})", variables.join(", "))
}
ASTNode::FunctionCall { name, arguments, .. } => {
format!("FunctionCall({}, {} args)", name, arguments.len())
}
ASTNode::Nowait { variable, .. } => {
format!("Nowait({})", variable)
}
ASTNode::Arrow { .. } => {
"Arrow(>>)".to_string()
}
ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => {
let mut desc = format!("TryCatch({} try statements, {} catch clauses",
try_body.len(), catch_clauses.len());
if finally_body.is_some() {
desc.push_str(", has finally");
}
desc.push(')');
desc
}
ASTNode::Throw { .. } => "Throw".to_string(),
ASTNode::AwaitExpression { expression, .. } => {
format!("Await({:?})", expression)
}
}
}
/// ASTードからSpan情報を取得
pub fn span(&self) -> Span {
match self {
ASTNode::Program { span, .. } => *span,
ASTNode::Assignment { span, .. } => *span,
ASTNode::Print { span, .. } => *span,
ASTNode::If { span, .. } => *span,
ASTNode::Loop { span, .. } => *span,
ASTNode::Return { span, .. } => *span,
ASTNode::Break { span, .. } => *span,
ASTNode::Nowait { span, .. } => *span,
ASTNode::Arrow { span, .. } => *span,
ASTNode::TryCatch { span, .. } => *span,
ASTNode::Throw { span, .. } => *span,
ASTNode::BoxDeclaration { span, .. } => *span,
ASTNode::FunctionDeclaration { span, .. } => *span,
ASTNode::GlobalVar { span, .. } => *span,
ASTNode::Literal { span, .. } => *span,
ASTNode::Variable { span, .. } => *span,
ASTNode::UnaryOp { span, .. } => *span,
ASTNode::BinaryOp { span, .. } => *span,
ASTNode::MethodCall { span, .. } => *span,
ASTNode::FieldAccess { span, .. } => *span,
ASTNode::New { span, .. } => *span,
ASTNode::This { span, .. } => *span,
ASTNode::Me { span, .. } => *span,
ASTNode::ThisField { span, .. } => *span,
ASTNode::MeField { span, .. } => *span,
ASTNode::Include { span, .. } => *span,
ASTNode::Local { span, .. } => *span,
ASTNode::Outbox { span, .. } => *span,
ASTNode::FunctionCall { span, .. } => *span,
ASTNode::AwaitExpression { span, .. } => *span,
}
}
}
impl fmt::Display for ASTNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.info())
}
}
impl ASTNode {
/// FunctionDeclarationのパラメータ数を取得
pub fn get_param_count(&self) -> usize {
match self {
ASTNode::FunctionDeclaration { params, .. } => params.len(),
_ => 0,
}
}
}
// ===== Tests =====
#[cfg(test)]
mod tests {
use super::*;
use crate::box_trait::{StringBox, IntegerBox, BoolBox};
#[test]
fn test_ast_node_creation() {
// Program node
let program = ASTNode::Program {
statements: vec![],
span: Span::unknown(),
};
assert_eq!(program.node_type(), "Program");
assert!(program.info().contains("Program(0 statements)"));
// Variable node
let variable = ASTNode::Variable {
name: "test_var".to_string(),
span: Span::unknown(),
};
assert_eq!(variable.node_type(), "Variable");
assert!(variable.is_expression());
assert!(!variable.is_statement());
// Assignment node
let assignment = ASTNode::Assignment {
target: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }),
value: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(42),
span: Span::unknown(),
}),
span: Span::unknown(),
};
assert_eq!(assignment.node_type(), "Assignment");
assert!(!assignment.is_expression());
assert!(assignment.is_statement());
}
#[test]
fn test_binary_operator() {
let add_op = BinaryOperator::Add;
assert_eq!(format!("{}", add_op), "+");
let equals_op = BinaryOperator::Equal;
assert_eq!(format!("{}", equals_op), "==");
let less_equals_op = BinaryOperator::LessEqual;
assert_eq!(format!("{}", less_equals_op), "<=");
}
#[test]
fn test_complex_ast() {
// box TestBox { value }のAST
let mut methods = HashMap::new();
methods.insert("getValue".to_string(), ASTNode::FunctionDeclaration {
name: "getValue".to_string(),
params: vec![],
body: vec![
ASTNode::Return {
value: Some(Box::new(ASTNode::FieldAccess {
object: Box::new(ASTNode::This { span: Span::unknown() }),
field: "value".to_string(),
span: Span::unknown(),
})),
span: Span::unknown(),
}
],
is_static: false, // 通常のメソッド
span: Span::unknown(),
});
let box_decl = ASTNode::BoxDeclaration {
name: "TestBox".to_string(),
fields: vec!["value".to_string()],
methods,
constructors: HashMap::new(),
init_fields: vec![],
is_interface: false,
extends: None,
implements: vec![],
span: Span::unknown(),
};
assert_eq!(box_decl.node_type(), "BoxDeclaration");
assert!(box_decl.info().contains("TestBox"));
assert!(box_decl.info().contains("1 fields"));
assert!(box_decl.info().contains("1 methods"));
}
#[test]
fn test_method_call() {
// obj.getValue()のAST
let method_call = ASTNode::MethodCall {
object: Box::new(ASTNode::Variable { name: "obj".to_string(), span: Span::unknown() }),
method: "getValue".to_string(),
arguments: vec![],
span: Span::unknown(),
};
assert_eq!(method_call.node_type(), "MethodCall");
assert!(method_call.is_expression());
assert!(method_call.info().contains("getValue"));
assert!(method_call.info().contains("0 args"));
}
#[test]
fn test_binary_operation() {
// x + y のAST
let binary_op = ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }),
right: Box::new(ASTNode::Variable { name: "y".to_string(), span: Span::unknown() }),
span: Span::unknown(),
};
assert_eq!(binary_op.node_type(), "BinaryOp");
assert!(binary_op.is_expression());
assert!(binary_op.info().contains("+"));
}
}

1444
src/box_trait.rs Normal file

File diff suppressed because it is too large Load Diff

67
src/boxes/bool_box.rs Normal file
View File

@ -0,0 +1,67 @@
// BoolBox implementation - Boolean values in Nyash
use crate::box_trait::NyashBox;
use std::any::Any;
use std::fmt::Display;
/// Boolean values in Nyash - true/false
#[derive(Debug, Clone, PartialEq)]
pub struct BoolBox {
pub value: bool,
id: u64,
}
impl BoolBox {
pub fn new(value: bool) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self { value, id }
}
pub fn true_box() -> Self {
Self::new(true)
}
pub fn false_box() -> Self {
Self::new(false)
}
}
impl NyashBox for BoolBox {
fn to_string_box(&self) -> crate::box_trait::StringBox {
crate::box_trait::StringBox::new(if self.value { "true" } else { "false" })
}
fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox {
if let Some(other_bool) = other.as_any().downcast_ref::<BoolBox>() {
crate::box_trait::BoolBox::new(self.value == other_bool.value)
} else {
crate::box_trait::BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"BoolBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for BoolBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", if self.value { "true" } else { "false" })
}
}

154
src/boxes/console_box.rs Normal file
View File

@ -0,0 +1,154 @@
/*!
* ConsoleBox - ブラウザコンソール制御Box
*
* WebAssembly環境でブラウザのconsole APIにアクセス
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox};
use std::any::Any;
use std::fmt::Display;
// 🌐 Browser console access Box
#[cfg(target_arch = "wasm32")]
#[derive(Debug, Clone)]
pub struct ConsoleBox {
id: u64,
}
#[cfg(target_arch = "wasm32")]
impl ConsoleBox {
pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self { id }
}
/// Log messages to browser console
pub fn log(&self, message: &str) {
web_sys::console::log_1(&message.into());
}
/// Log warning to browser console
pub fn warn(&self, message: &str) {
web_sys::console::warn_1(&message.into());
}
/// Log error to browser console
pub fn error(&self, message: &str) {
web_sys::console::error_1(&message.into());
}
/// Clear browser console
pub fn clear(&self) {
web_sys::console::clear();
}
}
#[cfg(target_arch = "wasm32")]
impl NyashBox for ConsoleBox {
fn to_string_box(&self) -> StringBox {
StringBox::new("[ConsoleBox - Browser Console Interface]")
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<ConsoleBox>())
}
fn type_name(&self) -> &'static str {
"ConsoleBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
// Non-WASM版 - モックアップ実装
#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug, Clone)]
pub struct ConsoleBox {
id: u64,
}
#[cfg(not(target_arch = "wasm32"))]
impl ConsoleBox {
pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self { id }
}
/// Mock log method for non-WASM environments
pub fn log(&self, message: &str) {
println!("[Console LOG] {}", message);
}
pub fn warn(&self, message: &str) {
println!("[Console WARN] {}", message);
}
pub fn error(&self, message: &str) {
println!("[Console ERROR] {}", message);
}
pub fn clear(&self) {
println!("[Console CLEAR]");
}
}
#[cfg(not(target_arch = "wasm32"))]
impl NyashBox for ConsoleBox {
fn to_string_box(&self) -> StringBox {
StringBox::new("[ConsoleBox - Mock Implementation]")
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<ConsoleBox>())
}
fn type_name(&self) -> &'static str {
"ConsoleBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
// Display implementations for both WASM and non-WASM versions
#[cfg(target_arch = "wasm32")]
impl Display for ConsoleBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[ConsoleBox - Browser Console Interface]")
}
}
#[cfg(not(target_arch = "wasm32"))]
impl Display for ConsoleBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[ConsoleBox - Mock Implementation]")
}
}

246
src/boxes/debug_box.rs Normal file
View File

@ -0,0 +1,246 @@
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use chrono::Local;
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox};
use crate::interpreter::RuntimeError;
use crate::instance::InstanceBox;
use std::any::Any;
#[derive(Debug, Clone)]
pub struct DebugBox {
tracking_enabled: Arc<Mutex<bool>>,
tracked_boxes: Arc<Mutex<HashMap<String, TrackedBoxInfo>>>,
breakpoints: Arc<Mutex<Vec<String>>>,
call_stack: Arc<Mutex<Vec<CallInfo>>>,
id: u64,
}
#[derive(Debug, Clone)]
struct TrackedBoxInfo {
box_type: String,
created_at: String,
fields: String,
value_repr: String,
}
#[derive(Debug, Clone)]
struct CallInfo {
function_name: String,
args: Vec<String>,
timestamp: String,
}
impl DebugBox {
pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
DebugBox {
tracking_enabled: Arc::new(Mutex::new(false)),
tracked_boxes: Arc::new(Mutex::new(HashMap::new())),
breakpoints: Arc::new(Mutex::new(Vec::new())),
call_stack: Arc::new(Mutex::new(Vec::new())),
id,
}
}
pub fn start_tracking(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut enabled = self.tracking_enabled.lock().unwrap();
*enabled = true;
println!("[DEBUG] Tracking started");
Ok(Box::new(VoidBox::new()))
}
pub fn stop_tracking(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut enabled = self.tracking_enabled.lock().unwrap();
*enabled = false;
println!("[DEBUG] Tracking stopped");
Ok(Box::new(VoidBox::new()))
}
pub fn track_box(&self, box_value: &dyn NyashBox, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
let enabled = self.tracking_enabled.lock().unwrap();
if !*enabled {
return Ok(Box::new(VoidBox::new()));
}
let mut tracked = self.tracked_boxes.lock().unwrap();
let info = TrackedBoxInfo {
box_type: box_value.type_name().to_string(),
created_at: Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
fields: self.get_box_fields(box_value),
value_repr: box_value.to_string_box().value,
};
tracked.insert(name.to_string(), info);
Ok(Box::new(VoidBox::new()))
}
fn get_box_fields(&self, box_value: &dyn NyashBox) -> String {
// Try to downcast to InstanceBox to get fields
if let Some(instance) = box_value.as_any().downcast_ref::<InstanceBox>() {
let fields = instance.fields.lock().unwrap();
let field_names: Vec<String> = fields.keys().cloned().collect();
field_names.join(", ")
} else {
"N/A".to_string()
}
}
pub fn dump_all(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let tracked = self.tracked_boxes.lock().unwrap();
let mut output = String::from("=== Box State Dump ===\n");
output.push_str(&format!("Time: {}\n", Local::now().format("%Y-%m-%d %H:%M:%S")));
output.push_str(&format!("Total tracked boxes: {}\n\n", tracked.len()));
for (name, info) in tracked.iter() {
output.push_str(&format!("Box: {}\n", name));
output.push_str(&format!(" Type: {}\n", info.box_type));
output.push_str(&format!(" Created: {}\n", info.created_at));
output.push_str(&format!(" Fields: {}\n", info.fields));
output.push_str(&format!(" Value: {}\n", info.value_repr));
output.push_str("\n");
}
Ok(Box::new(StringBox::new(output)))
}
pub fn save_to_file(&self, filename: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
let dump_result = self.dump_all()?;
let content = dump_result.to_string_box().value;
// Write to file using std::fs
std::fs::write(filename, content)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("Failed to write debug file: {}", e),
})?;
println!("[DEBUG] Saved debug info to {}", filename);
Ok(Box::new(VoidBox::new()))
}
pub fn watch(&self, box_value: &dyn NyashBox, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
let value_str = box_value.to_string_box().value;
let type_name = box_value.type_name();
println!("[DEBUG] Watching {} ({}): {}", name, type_name, value_str);
Ok(Box::new(VoidBox::new()))
}
pub fn memory_report(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let tracked = self.tracked_boxes.lock().unwrap();
let mut report = String::from("=== Memory Report ===\n");
report.push_str(&format!("Tracked boxes: {}\n", tracked.len()));
// Count by type
let mut type_counts: HashMap<String, usize> = HashMap::new();
for info in tracked.values() {
*type_counts.entry(info.box_type.clone()).or_insert(0) += 1;
}
report.push_str("\nBoxes by type:\n");
for (box_type, count) in type_counts.iter() {
report.push_str(&format!(" {}: {}\n", box_type, count));
}
Ok(Box::new(StringBox::new(report)))
}
// Advanced features
pub fn set_breakpoint(&self, function_name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut breakpoints = self.breakpoints.lock().unwrap();
breakpoints.push(function_name.to_string());
println!("[DEBUG] Breakpoint set at function: {}", function_name);
Ok(Box::new(VoidBox::new()))
}
pub fn trace_call(&self, function_name: &str, args: Vec<String>) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut stack = self.call_stack.lock().unwrap();
stack.push(CallInfo {
function_name: function_name.to_string(),
args,
timestamp: Local::now().format("%H:%M:%S.%3f").to_string(),
});
// Keep only last 100 calls to prevent memory issues
if stack.len() > 100 {
stack.remove(0);
}
Ok(Box::new(VoidBox::new()))
}
pub fn show_call_stack(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let stack = self.call_stack.lock().unwrap();
let mut output = String::from("=== Call Stack ===\n");
for (i, call) in stack.iter().enumerate() {
output.push_str(&format!("{}: [{}] {}({})\n",
i,
call.timestamp,
call.function_name,
call.args.join(", ")
));
}
Ok(Box::new(StringBox::new(output)))
}
pub fn clear(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut tracked = self.tracked_boxes.lock().unwrap();
tracked.clear();
let mut stack = self.call_stack.lock().unwrap();
stack.clear();
println!("[DEBUG] Cleared all debug information");
Ok(Box::new(VoidBox::new()))
}
pub fn is_tracking(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let enabled = self.tracking_enabled.lock().unwrap();
Ok(Box::new(BoolBox::new(*enabled)))
}
pub fn get_tracked_count(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let tracked = self.tracked_boxes.lock().unwrap();
Ok(Box::new(crate::box_trait::IntegerBox::new(tracked.len() as i64)))
}
}
// Implement NyashBox trait for DebugBox
impl NyashBox for DebugBox {
fn to_string_box(&self) -> StringBox {
let tracked = self.tracked_boxes.lock().unwrap();
StringBox::new(format!("DebugBox[{} tracked]", tracked.len()))
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_debug) = other.as_any().downcast_ref::<DebugBox>() {
BoolBox::new(self.id == other_debug.id)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"DebugBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}

64
src/boxes/integer_box.rs Normal file
View File

@ -0,0 +1,64 @@
// IntegerBox implementation - Integer values in Nyash
use crate::box_trait::NyashBox;
use std::any::Any;
use std::fmt::Display;
/// Integer values in Nyash - 64-bit signed integers
#[derive(Debug, Clone, PartialEq)]
pub struct IntegerBox {
pub value: i64,
id: u64,
}
impl IntegerBox {
pub fn new(value: i64) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self { value, id }
}
pub fn zero() -> Self {
Self::new(0)
}
}
impl NyashBox for IntegerBox {
fn to_string_box(&self) -> crate::box_trait::StringBox {
crate::box_trait::StringBox::new(self.value.to_string())
}
fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox {
use crate::box_trait::BoolBox;
if let Some(other_int) = other.as_any().downcast_ref::<IntegerBox>() {
BoolBox::new(self.value == other_int.value)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"IntegerBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for IntegerBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}

175
src/boxes/map_box.rs Normal file
View File

@ -0,0 +1,175 @@
/*!
* Nyash Map Box - Key-Value store implementation
*
* キーバリューストアを提供するBox型
* Everything is Box哲学に基づくマップデータ構造
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, ArrayBox};
use std::fmt::{Debug, Display};
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
/// キーバリューストアを表すBox
#[derive(Clone)]
pub struct MapBox {
data: Arc<Mutex<HashMap<String, Box<dyn NyashBox>>>>,
id: u64,
}
impl MapBox {
pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self {
data: Arc::new(Mutex::new(HashMap::new())),
id,
}
}
/// 値を設定
pub fn set(&self, key: Box<dyn NyashBox>, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value;
self.data.lock().unwrap().insert(key_str.clone(), value);
Box::new(StringBox::new(&format!("Set key: {}", key_str)))
}
/// 値を取得
pub fn get(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value;
match self.data.lock().unwrap().get(&key_str) {
Some(value) => value.clone_box(),
None => Box::new(StringBox::new(&format!("Key not found: {}", key_str))),
}
}
/// キーが存在するかチェック
pub fn has(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value;
Box::new(BoolBox::new(self.data.lock().unwrap().contains_key(&key_str)))
}
/// キーを削除
pub fn delete(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value;
match self.data.lock().unwrap().remove(&key_str) {
Some(_) => Box::new(StringBox::new(&format!("Deleted key: {}", key_str))),
None => Box::new(StringBox::new(&format!("Key not found: {}", key_str))),
}
}
/// 全てのキーを取得
pub fn keys(&self) -> Box<dyn NyashBox> {
let keys: Vec<String> = self.data.lock().unwrap().keys().cloned().collect();
let array = ArrayBox::new();
for key in keys {
array.push(Box::new(StringBox::new(&key)));
}
Box::new(array)
}
/// 全ての値を取得
pub fn values(&self) -> Box<dyn NyashBox> {
let values: Vec<Box<dyn NyashBox>> = self.data.lock().unwrap()
.values()
.map(|v| v.clone_box())
.collect();
let array = ArrayBox::new();
for value in values {
array.push(value);
}
Box::new(array)
}
/// サイズを取得
pub fn size(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.data.lock().unwrap().len() as i64))
}
/// 全てクリア
pub fn clear(&self) -> Box<dyn NyashBox> {
self.data.lock().unwrap().clear();
Box::new(StringBox::new("Map cleared"))
}
/// 各要素に対して関数を実行
pub fn forEach(&self, _callback: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
// 簡易実装callbackの実行はスキップ
let count = self.data.lock().unwrap().len();
Box::new(StringBox::new(&format!("Iterated over {} items", count)))
}
/// JSON文字列に変換
pub fn toJSON(&self) -> Box<dyn NyashBox> {
let data = self.data.lock().unwrap();
let mut json_parts = Vec::new();
for (key, value) in data.iter() {
let value_str = value.to_string_box().value;
// 値が数値の場合はそのまま、文字列の場合は引用符で囲む
let formatted_value = if value.as_any().downcast_ref::<IntegerBox>().is_some()
|| value.as_any().downcast_ref::<BoolBox>().is_some() {
value_str
} else {
format!("\"{}\"", value_str.replace("\"", "\\\""))
};
json_parts.push(format!("\"{}\":{}", key, formatted_value));
}
Box::new(StringBox::new(&format!("{{{}}}", json_parts.join(","))))
}
}
impl NyashBox for MapBox {
fn type_name(&self) -> &'static str {
"MapBox"
}
fn to_string_box(&self) -> StringBox {
let size = self.data.lock().unwrap().len();
StringBox::new(&format!("MapBox(size={})", size))
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_map) = other.as_any().downcast_ref::<MapBox>() {
// 同じインスタンスかチェック(データの共有を考慮)
BoolBox::new(self.id == other_map.id)
} else {
BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for MapBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_string_box().value)
}
}
impl Debug for MapBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let data = self.data.lock().unwrap();
f.debug_struct("MapBox")
.field("id", &self.id)
.field("size", &data.len())
.field("keys", &data.keys().collect::<Vec<_>>())
.finish()
}
}

410
src/boxes/math_box.rs Normal file
View File

@ -0,0 +1,410 @@
/*!
* Nyash Math Box - Mathematical operations
*
* 数学演算を提供するBox型
* Everything is Box哲学に基づく数学ライブラリ
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox};
use std::fmt::{Debug, Display};
use std::any::Any;
/// 数学演算を提供するBox
#[derive(Debug, Clone)]
pub struct MathBox {
id: u64,
}
impl MathBox {
pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self { id }
}
/// 絶対値を計算
pub fn abs(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Box::new(IntegerBox::new(int_box.value.abs()))
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Box::new(FloatBox::new(float_box.value.abs()))
} else {
Box::new(StringBox::new("Error: abs() requires numeric input"))
}
}
/// 最大値を返す
pub fn max(&self, a: Box<dyn NyashBox>, b: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(a_int), Some(b_int)) = (
a.as_any().downcast_ref::<IntegerBox>(),
b.as_any().downcast_ref::<IntegerBox>()
) {
Box::new(IntegerBox::new(a_int.value.max(b_int.value)))
} else if let (Some(a_float), Some(b_float)) = (
a.as_any().downcast_ref::<FloatBox>(),
b.as_any().downcast_ref::<FloatBox>()
) {
Box::new(FloatBox::new(a_float.value.max(b_float.value)))
} else {
Box::new(StringBox::new("Error: max() requires numeric inputs"))
}
}
/// 最小値を返す
pub fn min(&self, a: Box<dyn NyashBox>, b: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(a_int), Some(b_int)) = (
a.as_any().downcast_ref::<IntegerBox>(),
b.as_any().downcast_ref::<IntegerBox>()
) {
Box::new(IntegerBox::new(a_int.value.min(b_int.value)))
} else if let (Some(a_float), Some(b_float)) = (
a.as_any().downcast_ref::<FloatBox>(),
b.as_any().downcast_ref::<FloatBox>()
) {
Box::new(FloatBox::new(a_float.value.min(b_float.value)))
} else {
Box::new(StringBox::new("Error: min() requires numeric inputs"))
}
}
/// 累乗を計算
pub fn pow(&self, base: Box<dyn NyashBox>, exp: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(base_int), Some(exp_int)) = (
base.as_any().downcast_ref::<IntegerBox>(),
exp.as_any().downcast_ref::<IntegerBox>()
) {
if exp_int.value >= 0 {
let result = (base_int.value as f64).powi(exp_int.value as i32);
Box::new(FloatBox::new(result))
} else {
Box::new(StringBox::new("Error: negative exponent"))
}
} else {
Box::new(StringBox::new("Error: pow() requires numeric inputs"))
}
}
/// 平方根を計算
pub fn sqrt(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
if int_box.value >= 0 {
Box::new(FloatBox::new((int_box.value as f64).sqrt()))
} else {
Box::new(StringBox::new("Error: sqrt() of negative number"))
}
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
if float_box.value >= 0.0 {
Box::new(FloatBox::new(float_box.value.sqrt()))
} else {
Box::new(StringBox::new("Error: sqrt() of negative number"))
}
} else {
Box::new(StringBox::new("Error: sqrt() requires numeric input"))
}
}
/// 円周率πを返す
#[allow(non_snake_case)]
pub fn getPi(&self) -> Box<dyn NyashBox> {
Box::new(FloatBox::new(std::f64::consts::PI))
}
/// 自然対数の底eを返す
#[allow(non_snake_case)]
pub fn getE(&self) -> Box<dyn NyashBox> {
Box::new(FloatBox::new(std::f64::consts::E))
}
/// サイン(正弦)
pub fn sin(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Box::new(FloatBox::new((int_box.value as f64).sin()))
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Box::new(FloatBox::new(float_box.value.sin()))
} else {
Box::new(StringBox::new("Error: sin() requires numeric input"))
}
}
/// コサイン(余弦)
pub fn cos(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Box::new(FloatBox::new((int_box.value as f64).cos()))
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Box::new(FloatBox::new(float_box.value.cos()))
} else {
Box::new(StringBox::new("Error: cos() requires numeric input"))
}
}
/// タンジェント(正接)
pub fn tan(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Box::new(FloatBox::new((int_box.value as f64).tan()))
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Box::new(FloatBox::new(float_box.value.tan()))
} else {
Box::new(StringBox::new("Error: tan() requires numeric input"))
}
}
/// 自然対数
pub fn log(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
if int_box.value > 0 {
Box::new(FloatBox::new((int_box.value as f64).ln()))
} else {
Box::new(StringBox::new("Error: log() of non-positive number"))
}
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
if float_box.value > 0.0 {
Box::new(FloatBox::new(float_box.value.ln()))
} else {
Box::new(StringBox::new("Error: log() of non-positive number"))
}
} else {
Box::new(StringBox::new("Error: log() requires numeric input"))
}
}
/// 常用対数底10
pub fn log10(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
if int_box.value > 0 {
Box::new(FloatBox::new((int_box.value as f64).log10()))
} else {
Box::new(StringBox::new("Error: log10() of non-positive number"))
}
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
if float_box.value > 0.0 {
Box::new(FloatBox::new(float_box.value.log10()))
} else {
Box::new(StringBox::new("Error: log10() of non-positive number"))
}
} else {
Box::new(StringBox::new("Error: log10() requires numeric input"))
}
}
/// 指数関数e^x
pub fn exp(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Box::new(FloatBox::new((int_box.value as f64).exp()))
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Box::new(FloatBox::new(float_box.value.exp()))
} else {
Box::new(StringBox::new("Error: exp() requires numeric input"))
}
}
/// 床関数(切り下げ)
pub fn floor(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Box::new(IntegerBox::new(float_box.value.floor() as i64))
} else {
Box::new(StringBox::new("Error: floor() requires numeric input"))
}
}
/// 天井関数(切り上げ)
pub fn ceil(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Box::new(IntegerBox::new(float_box.value.ceil() as i64))
} else {
Box::new(StringBox::new("Error: ceil() requires numeric input"))
}
}
/// 四捨五入
pub fn round(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Box::new(IntegerBox::new(float_box.value.round() as i64))
} else {
Box::new(StringBox::new("Error: round() requires numeric input"))
}
}
}
impl NyashBox for MathBox {
fn type_name(&self) -> &'static str {
"MathBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("MathBox()")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_math) = other.as_any().downcast_ref::<MathBox>() {
BoolBox::new(self.id == other_math.id)
} else {
BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for MathBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MathBox()")
}
}
/// 浮動小数点数Box
#[derive(Debug, Clone)]
pub struct FloatBox {
pub value: f64,
id: u64,
}
impl FloatBox {
pub fn new(value: f64) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self { value, id }
}
}
impl NyashBox for FloatBox {
fn type_name(&self) -> &'static str {
"FloatBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new(&self.value.to_string())
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() {
BoolBox::new((self.value - other_float.value).abs() < f64::EPSILON)
} else if let Some(other_int) = other.as_any().downcast_ref::<IntegerBox>() {
BoolBox::new((self.value - other_int.value as f64).abs() < f64::EPSILON)
} else {
BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for FloatBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}
/// 範囲を表すBox
#[derive(Debug, Clone)]
pub struct RangeBox {
pub start: i64,
pub end: i64,
pub step: i64,
id: u64,
}
impl RangeBox {
pub fn new(start: i64, end: i64, step: i64) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self { start, end, step, id }
}
/// イテレータとして値を生成
pub fn iter(&self) -> Vec<i64> {
let mut result = Vec::new();
let mut current = self.start;
if self.step > 0 {
while current < self.end {
result.push(current);
current += self.step;
}
} else if self.step < 0 {
while current > self.end {
result.push(current);
current += self.step;
}
}
result
}
}
impl NyashBox for RangeBox {
fn type_name(&self) -> &'static str {
"RangeBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new(&format!("Range({}, {}, {})", self.start, self.end, self.step))
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_range) = other.as_any().downcast_ref::<RangeBox>() {
BoolBox::new(
self.start == other_range.start &&
self.end == other_range.end &&
self.step == other_range.step
)
} else {
BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for RangeBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Range({}, {}, {})", self.start, self.end, self.step)
}
}

46
src/boxes/mod.rs Normal file
View File

@ -0,0 +1,46 @@
// Nyash Box Implementations Module
// Everything is Box哲学に基づく各Box型の実装
// Nyashは意図的にJavaScript/TypeScriptスタイルのcamelCase命名規約を採用
#![allow(non_snake_case)]
// 各Boxモジュールを宣言
pub mod string_box;
pub mod integer_box;
pub mod bool_box;
pub mod math_box;
pub mod time_box;
pub mod debug_box;
pub mod random_box;
pub mod sound_box;
pub mod map_box;
pub mod console_box;
// Web専用Box群ブラウザ環境でのみ利用可能
#[cfg(target_arch = "wasm32")]
pub mod web;
// 共通で使う型とトレイトを再エクスポート
pub use string_box::StringBox;
pub use integer_box::IntegerBox;
pub use bool_box::BoolBox;
pub use math_box::MathBox;
pub use time_box::TimeBox;
pub use debug_box::DebugBox;
pub use random_box::RandomBox;
pub use sound_box::SoundBox;
pub use map_box::MapBox;
pub use console_box::ConsoleBox;
// Web Box群の再エクスポートWASM環境のみ
#[cfg(target_arch = "wasm32")]
pub use web::{WebDisplayBox, WebConsoleBox, WebCanvasBox};
pub mod null_box;
// 今後追加予定のBox型コメントアウト
// pub mod array_box;
// pub use array_box::ArrayBox;
// null関数も再エクスポート
pub use null_box::{NullBox, null};

149
src/boxes/null_box.rs Normal file
View File

@ -0,0 +1,149 @@
/*!
* Nyash Null Box - Null value representation
*
* null値を表現するBox型
* Everything is Box哲学に基づくnull実装
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox};
use std::fmt::{Debug, Display};
use std::any::Any;
/// null値を表現するBox
#[derive(Debug, Clone)]
pub struct NullBox {
id: u64,
}
impl NullBox {
pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self { id }
}
/// null値かどうかを判定
pub fn is_null(&self) -> bool {
true // NullBoxは常にnull
}
/// 値がnullでないかを判定
pub fn is_not_null(&self) -> bool {
false // NullBoxは常にnull
}
/// 他の値がnullかどうかを判定
pub fn check_null(value: &dyn NyashBox) -> bool {
value.as_any().downcast_ref::<NullBox>().is_some()
}
/// 他の値がnullでないかを判定
pub fn check_not_null(value: &dyn NyashBox) -> bool {
!Self::check_null(value)
}
/// null安全な値の取得
pub fn get_or_default(
value: &dyn NyashBox,
default: Box<dyn NyashBox>
) -> Box<dyn NyashBox> {
if Self::check_null(value) {
default
} else {
value.clone_box()
}
}
}
impl NyashBox for NullBox {
fn type_name(&self) -> &'static str {
"NullBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("null")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
// すべてのNullBoxは等しい
BoolBox::new(other.as_any().downcast_ref::<NullBox>().is_some())
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for NullBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "null")
}
}
// グローバルnullインスタンス用の関数
pub fn null() -> Box<dyn NyashBox> {
Box::new(NullBox::new())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::box_trait::IntegerBox;
#[test]
fn test_null_creation() {
let null_box = NullBox::new();
assert!(null_box.is_null());
assert!(!null_box.is_not_null());
assert_eq!(null_box.to_string_box().value, "null");
}
#[test]
fn test_null_check() {
let null_box = null();
let int_box = Box::new(IntegerBox::new(42));
assert!(NullBox::check_null(null_box.as_ref()));
assert!(!NullBox::check_null(int_box.as_ref()));
assert!(!NullBox::check_not_null(null_box.as_ref()));
assert!(NullBox::check_not_null(int_box.as_ref()));
}
#[test]
fn test_null_equality() {
let null1 = NullBox::new();
let null2 = NullBox::new();
let int_box = IntegerBox::new(42);
assert!(null1.equals(&null2).value);
assert!(!null1.equals(&int_box).value);
}
#[test]
fn test_get_or_default() {
let null_box = null();
let default_value = Box::new(IntegerBox::new(100));
let actual_value = Box::new(IntegerBox::new(42));
// nullの場合はデフォルト値を返す
let result1 = NullBox::get_or_default(null_box.as_ref(), default_value.clone());
assert_eq!(result1.to_string_box().value, "100");
// null以外の場合は元の値を返す
let result2 = NullBox::get_or_default(actual_value.as_ref(), default_value);
assert_eq!(result2.to_string_box().value, "42");
}
}

225
src/boxes/random_box.rs Normal file
View File

@ -0,0 +1,225 @@
/*!
* Nyash Random Box - Random number generation
*
* 乱数生成を提供するBox型
* Everything is Box哲学に基づく乱数ライブラリ
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, ArrayBox};
use crate::boxes::math_box::FloatBox;
use std::fmt::{Debug, Display};
use std::any::Any;
use std::sync::{Arc, Mutex};
/// 乱数生成を提供するBox
#[derive(Debug, Clone)]
pub struct RandomBox {
// 簡易線形合同法による疑似乱数生成器
seed: Arc<Mutex<u64>>,
id: u64,
}
impl RandomBox {
pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
// 現在時刻を種として使用
let seed = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
Self {
seed: Arc::new(Mutex::new(seed)),
id,
}
}
/// 種を設定
pub fn seed(&self, new_seed: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = new_seed.as_any().downcast_ref::<IntegerBox>() {
*self.seed.lock().unwrap() = int_box.value as u64;
Box::new(StringBox::new("Seed set"))
} else {
Box::new(StringBox::new("Error: seed() requires integer input"))
}
}
/// 次の乱数を生成(線形合同法)
fn next_random(&self) -> u64 {
let mut seed = self.seed.lock().unwrap();
// 線形合同法の定数Numerical Recipes より)
*seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
*seed
}
/// 0.0-1.0の浮動小数点乱数
pub fn random(&self) -> Box<dyn NyashBox> {
let r = self.next_random();
let normalized = (r as f64) / (u64::MAX as f64);
Box::new(FloatBox::new(normalized))
}
/// 指定範囲の整数乱数
pub fn randInt(&self, min: Box<dyn NyashBox>, max: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(min_int), Some(max_int)) = (
min.as_any().downcast_ref::<IntegerBox>(),
max.as_any().downcast_ref::<IntegerBox>()
) {
if min_int.value > max_int.value {
return Box::new(StringBox::new("Error: min must be <= max"));
}
let range = (max_int.value - min_int.value + 1) as u64;
let r = self.next_random() % range;
Box::new(IntegerBox::new(min_int.value + r as i64))
} else {
Box::new(StringBox::new("Error: randInt() requires two integer inputs"))
}
}
/// true/falseのランダム選択
pub fn randBool(&self) -> Box<dyn NyashBox> {
let r = self.next_random();
Box::new(BoolBox::new(r % 2 == 0))
}
/// 配列からランダム選択
pub fn choice(&self, array: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(array_box) = array.as_any().downcast_ref::<ArrayBox>() {
let length = array_box.length().to_string_box().value.parse::<i64>().unwrap_or(0);
if length == 0 {
return Box::new(StringBox::new("Error: cannot choose from empty array"));
}
let index = self.next_random() % (length as u64);
match array_box.get(index as usize) {
Some(element) => element,
None => Box::new(StringBox::new("Error: index out of bounds")),
}
} else {
Box::new(StringBox::new("Error: choice() requires array input"))
}
}
/// 配列をシャッフル
pub fn shuffle(&self, array: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(array_box) = array.as_any().downcast_ref::<ArrayBox>() {
let length = array_box.length().to_string_box().value.parse::<i64>().unwrap_or(0);
if length <= 1 {
return array;
}
// 新しい配列を作成
let shuffled = ArrayBox::new();
// 元の配列の要素を全て新しい配列にコピー
for i in 0..length {
if let Some(element) = array_box.get(i as usize) {
shuffled.push(element);
}
}
// 簡易シャッフル実装完全なFisher-Yatesは複雑なので
// 代わりに、元の配列からランダムに選んで新しい配列を作る
let result = ArrayBox::new();
let mut remaining_indices: Vec<usize> = (0..length as usize).collect();
while !remaining_indices.is_empty() {
let random_idx = (self.next_random() % remaining_indices.len() as u64) as usize;
let actual_idx = remaining_indices.remove(random_idx);
if let Some(element) = array_box.get(actual_idx) {
result.push(element);
}
}
Box::new(result)
} else {
Box::new(StringBox::new("Error: shuffle() requires array input"))
}
}
/// ランダムな文字列生成
pub fn randString(&self, length: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(len_int) = length.as_any().downcast_ref::<IntegerBox>() {
if len_int.value < 0 {
return Box::new(StringBox::new("Error: length must be positive"));
}
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let char_vec: Vec<char> = chars.chars().collect();
let mut result = String::new();
for _ in 0..len_int.value {
let index = self.next_random() % (char_vec.len() as u64);
result.push(char_vec[index as usize]);
}
Box::new(StringBox::new(&result))
} else {
Box::new(StringBox::new("Error: randString() requires integer length"))
}
}
/// 指定確率でtrue
pub fn probability(&self, prob: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(float_box) = prob.as_any().downcast_ref::<FloatBox>() {
if float_box.value < 0.0 || float_box.value > 1.0 {
return Box::new(StringBox::new("Error: probability must be 0.0-1.0"));
}
let r = self.next_random() as f64 / u64::MAX as f64;
Box::new(BoolBox::new(r < float_box.value))
} else if let Some(int_box) = prob.as_any().downcast_ref::<IntegerBox>() {
let prob_val = int_box.value as f64;
if prob_val < 0.0 || prob_val > 1.0 {
return Box::new(StringBox::new("Error: probability must be 0.0-1.0"));
}
let r = self.next_random() as f64 / u64::MAX as f64;
Box::new(BoolBox::new(r < prob_val))
} else {
Box::new(StringBox::new("Error: probability() requires numeric input"))
}
}
}
impl NyashBox for RandomBox {
fn type_name(&self) -> &'static str {
"RandomBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("RandomBox()")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_random) = other.as_any().downcast_ref::<RandomBox>() {
BoolBox::new(self.id == other_random.id)
} else {
BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for RandomBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RandomBox()")
}
}

221
src/boxes/sound_box.rs Normal file
View File

@ -0,0 +1,221 @@
/*!
* Nyash Sound Box - Simple sound generation
*
* 音響効果を提供するBox型
* Everything is Box哲学に基づく音響ライブラリ
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox};
use std::fmt::{Debug, Display};
use std::any::Any;
use std::process::Command;
use std::time::Duration;
/// 音響効果を提供するBox
#[derive(Debug, Clone)]
pub struct SoundBox {
id: u64,
}
impl SoundBox {
pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self { id }
}
/// ビープ音を鳴らす(基本)
pub fn beep(&self) -> Box<dyn NyashBox> {
// 端末ベル文字を出力
print!("\x07");
Box::new(StringBox::new("Beep!"))
}
/// 指定回数ビープ
pub fn beeps(&self, count: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(count_int) = count.as_any().downcast_ref::<IntegerBox>() {
if count_int.value <= 0 {
return Box::new(StringBox::new("Beep count must be positive"));
}
for i in 0..count_int.value {
print!("\x07");
if i < count_int.value - 1 {
std::thread::sleep(Duration::from_millis(100));
}
}
Box::new(StringBox::new(&format!("Beeped {} times", count_int.value)))
} else {
Box::new(StringBox::new("Error: beeps() requires integer input"))
}
}
/// 指定周波数のビープLinuxのみ
pub fn tone(&self, frequency: Box<dyn NyashBox>, duration: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(freq_int), Some(dur_int)) = (
frequency.as_any().downcast_ref::<IntegerBox>(),
duration.as_any().downcast_ref::<IntegerBox>()
) {
if freq_int.value <= 0 || dur_int.value <= 0 {
return Box::new(StringBox::new("Frequency and duration must be positive"));
}
// Linuxのbeepコマンドを試行
match Command::new("beep")
.arg("-f")
.arg(&freq_int.value.to_string())
.arg("-l")
.arg(&dur_int.value.to_string())
.output()
{
Ok(_) => Box::new(StringBox::new(&format!("Played {}Hz for {}ms", freq_int.value, dur_int.value))),
Err(_) => {
// beepコマンドが無い場合は端末ベルを使用
print!("\x07");
std::thread::sleep(Duration::from_millis(dur_int.value as u64));
Box::new(StringBox::new(&format!("Fallback beep ({}Hz, {}ms)", freq_int.value, dur_int.value)))
}
}
} else {
Box::new(StringBox::new("Error: tone() requires two integer inputs (frequency, duration)"))
}
}
/// 警告音
pub fn alert(&self) -> Box<dyn NyashBox> {
// 3回短いビープ
for i in 0..3 {
print!("\x07");
if i < 2 {
std::thread::sleep(Duration::from_millis(150));
}
}
Box::new(StringBox::new("Alert sound played"))
}
/// 成功音
pub fn success(&self) -> Box<dyn NyashBox> {
// 1回長めのビープ
print!("\x07");
std::thread::sleep(Duration::from_millis(50));
print!("\x07");
Box::new(StringBox::new("Success sound played"))
}
/// エラー音
pub fn error(&self) -> Box<dyn NyashBox> {
// 2回素早いビープ
print!("\x07");
std::thread::sleep(Duration::from_millis(80));
print!("\x07");
Box::new(StringBox::new("Error sound played"))
}
/// カスタムビープパターン
pub fn pattern(&self, pattern: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(pattern_str) = pattern.as_any().downcast_ref::<StringBox>() {
let mut beep_count = 0;
for ch in pattern_str.value.chars() {
match ch {
'.' => {
// 短いビープ
print!("\x07");
std::thread::sleep(Duration::from_millis(100));
beep_count += 1;
}
'-' => {
// 長いビープ
print!("\x07");
std::thread::sleep(Duration::from_millis(300));
beep_count += 1;
}
' ' => {
// 無音(待機)
std::thread::sleep(Duration::from_millis(200));
}
_ => {
// その他の文字は無視
}
}
// 文字間の短い間隔
std::thread::sleep(Duration::from_millis(50));
}
Box::new(StringBox::new(&format!("Played pattern '{}' ({} beeps)", pattern_str.value, beep_count)))
} else {
Box::new(StringBox::new("Error: pattern() requires string input (use '.' for short, '-' for long, ' ' for pause)"))
}
}
/// システム音量チェック(簡易)
pub fn volumeTest(&self) -> Box<dyn NyashBox> {
print!("\x07");
Box::new(StringBox::new("Volume test beep - can you hear it?"))
}
/// 指定間隔でビープ
pub fn interval(&self, times: Box<dyn NyashBox>, interval_ms: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(times_int), Some(interval_int)) = (
times.as_any().downcast_ref::<IntegerBox>(),
interval_ms.as_any().downcast_ref::<IntegerBox>()
) {
if times_int.value <= 0 || interval_int.value < 0 {
return Box::new(StringBox::new("Times must be positive, interval must be non-negative"));
}
for i in 0..times_int.value {
print!("\x07");
if i < times_int.value - 1 {
std::thread::sleep(Duration::from_millis(interval_int.value as u64));
}
}
Box::new(StringBox::new(&format!("Played {} beeps with {}ms intervals", times_int.value, interval_int.value)))
} else {
Box::new(StringBox::new("Error: interval() requires two integer inputs (times, interval_ms)"))
}
}
}
impl NyashBox for SoundBox {
fn type_name(&self) -> &'static str {
"SoundBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("SoundBox()")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_sound) = other.as_any().downcast_ref::<SoundBox>() {
BoolBox::new(self.id == other_sound.id)
} else {
BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for SoundBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SoundBox()")
}
}

141
src/boxes/string_box.rs Normal file
View File

@ -0,0 +1,141 @@
// StringBox implementation - String values in Nyash
use crate::box_trait::NyashBox;
use std::any::Any;
use std::fmt::Display;
/// String values in Nyash - UTF-8 encoded text
#[derive(Debug, Clone, PartialEq)]
pub struct StringBox {
pub value: String,
id: u64,
}
impl StringBox {
pub fn new(value: impl Into<String>) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self {
value: value.into(),
id,
}
}
pub fn empty() -> Self {
Self::new("")
}
// ===== String Methods for Nyash =====
/// Split string by delimiter and return ArrayBox
pub fn split(&self, delimiter: &str) -> Box<dyn NyashBox> {
use crate::box_trait::ArrayBox;
let parts: Vec<String> = self.value.split(delimiter).map(|s| s.to_string()).collect();
let array_elements: Vec<Box<dyn NyashBox>> = parts.into_iter()
.map(|s| Box::new(StringBox::new(s)) as Box<dyn NyashBox>)
.collect();
Box::new(ArrayBox::new_with_elements(array_elements))
}
/// Find substring and return position (or -1 if not found)
pub fn find(&self, search: &str) -> Box<dyn NyashBox> {
use crate::boxes::IntegerBox;
match self.value.find(search) {
Some(pos) => Box::new(IntegerBox::new(pos as i64)),
None => Box::new(IntegerBox::new(-1)),
}
}
/// Replace all occurrences of old with new
pub fn replace(&self, old: &str, new: &str) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.replace(old, new)))
}
/// Trim whitespace from both ends
pub fn trim(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.trim()))
}
/// Convert to uppercase
pub fn to_upper(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.to_uppercase()))
}
/// Convert to lowercase
pub fn to_lower(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.to_lowercase()))
}
/// Check if string contains substring
pub fn contains(&self, search: &str) -> Box<dyn NyashBox> {
use crate::boxes::BoolBox;
Box::new(BoolBox::new(self.value.contains(search)))
}
/// Check if string starts with prefix
pub fn starts_with(&self, prefix: &str) -> Box<dyn NyashBox> {
use crate::boxes::BoolBox;
Box::new(BoolBox::new(self.value.starts_with(prefix)))
}
/// Check if string ends with suffix
pub fn ends_with(&self, suffix: &str) -> Box<dyn NyashBox> {
use crate::boxes::BoolBox;
Box::new(BoolBox::new(self.value.ends_with(suffix)))
}
/// Join array elements using this string as delimiter
pub fn join(&self, array_box: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
use crate::box_trait::ArrayBox;
if let Some(array) = array_box.as_any().downcast_ref::<ArrayBox>() {
let strings: Vec<String> = array.elements.lock().unwrap()
.iter()
.map(|element| element.to_string_box().value)
.collect();
Box::new(StringBox::new(strings.join(&self.value)))
} else {
// If not an ArrayBox, treat as single element
Box::new(StringBox::new(array_box.to_string_box().value))
}
}
}
impl NyashBox for StringBox {
fn to_string_box(&self) -> crate::box_trait::StringBox {
crate::box_trait::StringBox::new(self.value.clone())
}
fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox {
use crate::box_trait::BoolBox;
if let Some(other_string) = other.as_any().downcast_ref::<StringBox>() {
BoolBox::new(self.value == other_string.value)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"StringBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for StringBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}

366
src/boxes/time_box.rs Normal file
View File

@ -0,0 +1,366 @@
/*!
* Nyash Time Box - Time and Date operations
*
* 時間と日付操作を提供するBox型
* Everything is Box哲学に基づく時間ライブラリ
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox};
use std::fmt::{Debug, Display};
use std::any::Any;
use std::time::{SystemTime, Duration};
use chrono::{DateTime, Local, TimeZone, Datelike, Timelike};
/// 時間操作を提供するBox
#[derive(Debug, Clone)]
pub struct TimeBox {
id: u64,
}
impl TimeBox {
pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self { id }
}
/// 現在時刻を取得
pub fn now(&self) -> Box<dyn NyashBox> {
Box::new(DateTimeBox::now())
}
/// UNIXタイムスタンプから日時を作成
pub fn fromTimestamp(&self, timestamp: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = timestamp.as_any().downcast_ref::<IntegerBox>() {
Box::new(DateTimeBox::from_timestamp(int_box.value))
} else {
Box::new(StringBox::new("Error: fromTimestamp() requires integer input"))
}
}
/// 日時文字列をパース
pub fn parse(&self, date_str: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(string_box) = date_str.as_any().downcast_ref::<StringBox>() {
match DateTimeBox::parse(&string_box.value) {
Ok(dt) => Box::new(dt),
Err(e) => Box::new(StringBox::new(&format!("Error: {}", e))),
}
} else {
Box::new(StringBox::new("Error: parse() requires string input"))
}
}
/// ミリ秒スリープ
pub fn sleep(&self, millis: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = millis.as_any().downcast_ref::<IntegerBox>() {
if int_box.value > 0 {
std::thread::sleep(Duration::from_millis(int_box.value as u64));
Box::new(StringBox::new("ok"))
} else {
Box::new(StringBox::new("Error: sleep() requires positive milliseconds"))
}
} else {
Box::new(StringBox::new("Error: sleep() requires integer input"))
}
}
/// 現在時刻をフォーマット
pub fn format(&self, format_str: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(str_box) = format_str.as_any().downcast_ref::<StringBox>() {
let now = Local::now();
let formatted = now.format(&str_box.value).to_string();
Box::new(StringBox::new(formatted))
} else {
Box::new(StringBox::new("Error: format() requires string format pattern"))
}
}
}
impl NyashBox for TimeBox {
fn type_name(&self) -> &'static str {
"TimeBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("TimeBox()")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_time) = other.as_any().downcast_ref::<TimeBox>() {
BoolBox::new(self.id == other_time.id)
} else {
BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for TimeBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TimeBox()")
}
}
/// 日時を表すBox
#[derive(Debug, Clone)]
pub struct DateTimeBox {
pub datetime: DateTime<Local>,
id: u64,
}
impl DateTimeBox {
/// 現在時刻で作成
pub fn now() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self {
datetime: Local::now(),
id,
}
}
/// UNIXタイムスタンプから作成
pub fn from_timestamp(timestamp: i64) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
let datetime = Local.timestamp_opt(timestamp, 0).unwrap();
Self { datetime, id }
}
/// 文字列からパース
pub fn parse(date_str: &str) -> Result<Self, String> {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
// ISO 8601形式でパース
match DateTime::parse_from_rfc3339(date_str) {
Ok(dt) => Ok(Self {
datetime: dt.with_timezone(&Local),
id,
}),
Err(_) => {
// シンプルな形式でパース (YYYY-MM-DD HH:MM:SS)
match chrono::NaiveDateTime::parse_from_str(date_str, "%Y-%m-%d %H:%M:%S") {
Ok(naive_dt) => {
let datetime = Local.from_local_datetime(&naive_dt).unwrap();
Ok(Self { datetime, id })
}
Err(e) => Err(format!("Failed to parse date: {}", e)),
}
}
}
}
/// 年を取得
pub fn year(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.year() as i64))
}
/// 月を取得
pub fn month(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.month() as i64))
}
/// 日を取得
pub fn day(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.day() as i64))
}
/// 時を取得
pub fn hour(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.hour() as i64))
}
/// 分を取得
pub fn minute(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.minute() as i64))
}
/// 秒を取得
pub fn second(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.second() as i64))
}
/// UNIXタイムスタンプを取得
pub fn timestamp(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.timestamp()))
}
/// ISO 8601形式でフォーマット
pub fn toISOString(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(&self.datetime.to_rfc3339()))
}
/// カスタムフォーマット
pub fn format(&self, fmt: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(string_box) = fmt.as_any().downcast_ref::<StringBox>() {
let formatted = self.datetime.format(&string_box.value).to_string();
Box::new(StringBox::new(&formatted))
} else {
Box::new(StringBox::new("Error: format() requires string input"))
}
}
/// 日付を加算
pub fn addDays(&self, days: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = days.as_any().downcast_ref::<IntegerBox>() {
let new_datetime = self.datetime + chrono::Duration::days(int_box.value);
Box::new(DateTimeBox {
datetime: new_datetime,
id: self.id,
})
} else {
Box::new(StringBox::new("Error: addDays() requires integer input"))
}
}
/// 時間を加算
pub fn addHours(&self, hours: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = hours.as_any().downcast_ref::<IntegerBox>() {
let new_datetime = self.datetime + chrono::Duration::hours(int_box.value);
Box::new(DateTimeBox {
datetime: new_datetime,
id: self.id,
})
} else {
Box::new(StringBox::new("Error: addHours() requires integer input"))
}
}
}
impl NyashBox for DateTimeBox {
fn type_name(&self) -> &'static str {
"DateTimeBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new(&self.datetime.format("%Y-%m-%d %H:%M:%S").to_string())
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_dt) = other.as_any().downcast_ref::<DateTimeBox>() {
BoolBox::new(self.datetime == other_dt.datetime)
} else {
BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for DateTimeBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.datetime.format("%Y-%m-%d %H:%M:%S"))
}
}
/// タイマーを表すBox
#[derive(Debug, Clone)]
pub struct TimerBox {
start_time: SystemTime,
id: u64,
}
impl TimerBox {
pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self {
start_time: SystemTime::now(),
id,
}
}
/// 経過時間をミリ秒で取得
pub fn elapsed(&self) -> Box<dyn NyashBox> {
match self.start_time.elapsed() {
Ok(duration) => {
let millis = duration.as_millis() as i64;
Box::new(IntegerBox::new(millis))
}
Err(_) => Box::new(IntegerBox::new(0)),
}
}
/// タイマーをリセット
pub fn reset(&mut self) -> Box<dyn NyashBox> {
self.start_time = SystemTime::now();
Box::new(StringBox::new("Timer reset"))
}
}
impl NyashBox for TimerBox {
fn type_name(&self) -> &'static str {
"TimerBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("TimerBox()")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_timer) = other.as_any().downcast_ref::<TimerBox>() {
BoolBox::new(self.id == other_timer.id)
} else {
BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for TimerBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TimerBox()")
}
}

24
src/boxes/web/mod.rs Normal file
View File

@ -0,0 +1,24 @@
/*!
* Web Boxes Module - ブラウザ専用Box群
*
* WebAssembly環境専用のBox群を管理
* HTML5 APIs、DOM操作、Canvas描画等をNyashから利用可能にする
*/
#[cfg(target_arch = "wasm32")]
pub mod web_display_box;
#[cfg(target_arch = "wasm32")]
pub mod web_console_box;
#[cfg(target_arch = "wasm32")]
pub mod web_canvas_box;
#[cfg(target_arch = "wasm32")]
pub use web_display_box::WebDisplayBox;
#[cfg(target_arch = "wasm32")]
pub use web_console_box::WebConsoleBox;
#[cfg(target_arch = "wasm32")]
pub use web_canvas_box::WebCanvasBox;

View File

@ -0,0 +1,304 @@
/*!
* WebCanvasBox - ブラウザCanvas完全制御Box
*
* WebAssembly環境でHTML5 Canvasの完全制御
* ピクセルの世界を制圧する革命的Box
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox};
use std::any::Any;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use web_sys::{
HtmlCanvasElement,
CanvasRenderingContext2d,
};
// 🎨 Browser Canvas complete control Box
#[cfg(target_arch = "wasm32")]
#[derive(Debug, Clone)]
pub struct WebCanvasBox {
id: u64,
canvas_id: String,
width: u32,
height: u32,
}
#[cfg(target_arch = "wasm32")]
impl WebCanvasBox {
pub fn new(canvas_id: String, width: u32, height: u32) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
let instance = Self {
id,
canvas_id: canvas_id.clone(),
width,
height,
};
// キャンバス要素を初期化
if let Some(canvas) = instance.get_canvas_element() {
canvas.set_width(width);
canvas.set_height(height);
}
instance
}
/// Canvas要素を取得
fn get_canvas_element(&self) -> Option<HtmlCanvasElement> {
let window = web_sys::window()?;
let document = window.document()?;
let element = document.get_element_by_id(&self.canvas_id)?;
element.dyn_into::<HtmlCanvasElement>().ok()
}
/// 2Dレンダリングコンテキストを取得
fn get_2d_context(&self) -> Option<CanvasRenderingContext2d> {
let canvas = self.get_canvas_element()?;
canvas
.get_context("2d")
.ok()?
.and_then(|ctx| ctx.dyn_into::<CanvasRenderingContext2d>().ok())
}
/// キャンバスをクリア
pub fn clear(&self) {
if let Some(ctx) = self.get_2d_context() {
ctx.clear_rect(0.0, 0.0, self.width as f64, self.height as f64);
}
}
/// 塗りつぶし色を設定
pub fn set_fill_style(&self, color: &str) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(color));
}
}
/// 線の色を設定
pub fn set_stroke_style(&self, color: &str) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
}
}
/// 線の太さを設定
pub fn set_line_width(&self, width: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_line_width(width);
}
}
/// 塗りつぶし矩形を描画
pub fn fill_rect(&self, x: f64, y: f64, width: f64, height: f64, color: &str) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(color));
ctx.fill_rect(x, y, width, height);
}
}
/// 枠線矩形を描画
pub fn stroke_rect(&self, x: f64, y: f64, width: f64, height: f64, color: &str, line_width: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
ctx.set_line_width(line_width);
ctx.stroke_rect(x, y, width, height);
}
}
/// 塗りつぶし円を描画
pub fn fill_circle(&self, x: f64, y: f64, radius: f64, color: &str) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(color));
ctx.begin_path();
ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI).unwrap_or_default();
ctx.fill();
}
}
/// 枠線円を描画
pub fn stroke_circle(&self, x: f64, y: f64, radius: f64, color: &str, line_width: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
ctx.set_line_width(line_width);
ctx.begin_path();
ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI).unwrap_or_default();
ctx.stroke();
}
}
/// 直線を描画
pub fn draw_line(&self, x1: f64, y1: f64, x2: f64, y2: f64, color: &str, line_width: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
ctx.set_line_width(line_width);
ctx.begin_path();
ctx.move_to(x1, y1);
ctx.line_to(x2, y2);
ctx.stroke();
}
}
/// テキストを描画(塗りつぶし)
pub fn fill_text(&self, text: &str, x: f64, y: f64, font: &str, color: &str) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_font(font);
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(color));
ctx.fill_text(text, x, y).unwrap_or_default();
}
}
/// テキストを描画(枠線)
pub fn stroke_text(&self, text: &str, x: f64, y: f64, font: &str, color: &str, line_width: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_font(font);
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
ctx.set_line_width(line_width);
ctx.stroke_text(text, x, y).unwrap_or_default();
}
}
/// パス描画開始
pub fn begin_path(&self) {
if let Some(ctx) = self.get_2d_context() {
ctx.begin_path();
}
}
/// パスを指定位置に移動
pub fn move_to(&self, x: f64, y: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.move_to(x, y);
}
}
/// パスに直線を追加
pub fn line_to(&self, x: f64, y: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.line_to(x, y);
}
}
/// パスを閉じる
pub fn close_path(&self) {
if let Some(ctx) = self.get_2d_context() {
ctx.close_path();
}
}
/// パスを塗りつぶし
pub fn fill(&self, color: &str) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(color));
ctx.fill();
}
}
/// パスを枠線描画
pub fn stroke(&self, color: &str, line_width: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
ctx.set_line_width(line_width);
ctx.stroke();
}
}
/// 現在の描画状態を保存
pub fn save(&self) {
if let Some(ctx) = self.get_2d_context() {
ctx.save();
}
}
/// 描画状態を復元
pub fn restore(&self) {
if let Some(ctx) = self.get_2d_context() {
ctx.restore();
}
}
/// 座標系を回転
pub fn rotate(&self, angle: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.rotate(angle).unwrap_or_default();
}
}
/// 座標系をスケール
pub fn scale(&self, x: f64, y: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.scale(x, y).unwrap_or_default();
}
}
/// 座標系を平行移動
pub fn translate(&self, x: f64, y: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.translate(x, y).unwrap_or_default();
}
}
/// キャンバスのサイズを取得
pub fn get_width(&self) -> u32 {
self.width
}
pub fn get_height(&self) -> u32 {
self.height
}
/// キャンバスのサイズを変更
pub fn resize(&mut self, width: u32, height: u32) {
self.width = width;
self.height = height;
if let Some(canvas) = self.get_canvas_element() {
canvas.set_width(width);
canvas.set_height(height);
}
}
}
#[cfg(target_arch = "wasm32")]
impl NyashBox for WebCanvasBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn to_string_box(&self) -> StringBox {
StringBox::new(format!(
"WebCanvasBox({}, {}x{})",
self.canvas_id,
self.width,
self.height
))
}
fn as_any(&self) -> &dyn Any {
self
}
fn type_name(&self) -> &'static str {
"WebCanvasBox"
}
fn box_id(&self) -> u64 {
self.id
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_canvas) = other.as_any().downcast_ref::<WebCanvasBox>() {
BoolBox::new(self.id == other_canvas.id)
} else {
BoolBox::new(false)
}
}
}

View File

@ -0,0 +1,175 @@
/*!
* WebConsoleBox - ブラウザHTML要素コンソール出力Box
*
* WebAssembly環境でHTML要素へのコンソール風出力
* F12コンソールの代わりに指定要素に出力
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox};
use std::any::Any;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use web_sys::{Element, HtmlElement};
// 🌐 Browser HTML element console output Box
#[cfg(target_arch = "wasm32")]
#[derive(Debug, Clone)]
pub struct WebConsoleBox {
id: u64,
target_element_id: String,
}
#[cfg(target_arch = "wasm32")]
impl WebConsoleBox {
pub fn new(element_id: String) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self {
id,
target_element_id: element_id,
}
}
/// 指定した要素IDのHTML要素を取得
fn get_target_element(&self) -> Option<Element> {
let window = web_sys::window()?;
let document = window.document()?;
document.get_element_by_id(&self.target_element_id)
}
/// コンソール出力を追加(改行付き)
fn append_console_line(&self, message: &str, level: &str) {
if let Some(element) = self.get_target_element() {
let timestamp = js_sys::Date::new_0().to_iso_string().as_string().unwrap_or_default();
let time_part = timestamp.split('T').nth(1).unwrap_or("00:00:00").split('.').nth(0).unwrap_or("00:00:00");
let (level_prefix, color) = match level {
"log" => ("📝", "white"),
"warn" => ("⚠️", "yellow"),
"error" => ("", "red"),
"info" => ("", "cyan"),
"debug" => ("🔍", "gray"),
_ => ("📝", "white"),
};
let formatted_line = format!(
"<span style='color: {}'>[{}] {} {}</span><br>",
color,
time_part,
level_prefix,
message
);
let current_content = element.inner_html();
let new_content = format!("{}{}", current_content, formatted_line);
element.set_inner_html(&new_content);
// 自動スクロール
if let Some(html_element) = element.dyn_ref::<HtmlElement>() {
html_element.set_scroll_top(html_element.scroll_height());
}
}
}
/// ログメッセージを出力
pub fn log(&self, message: &str) {
self.append_console_line(message, "log");
}
/// 警告メッセージを出力
pub fn warn(&self, message: &str) {
self.append_console_line(message, "warn");
}
/// エラーメッセージを出力
pub fn error(&self, message: &str) {
self.append_console_line(message, "error");
}
/// 情報メッセージを出力
pub fn info(&self, message: &str) {
self.append_console_line(message, "info");
}
/// デバッグメッセージを出力
pub fn debug(&self, message: &str) {
self.append_console_line(message, "debug");
}
/// コンソールをクリア
pub fn clear(&self) {
if let Some(element) = self.get_target_element() {
element.set_inner_html("");
}
}
/// 区切り線を追加
pub fn separator(&self) {
if let Some(element) = self.get_target_element() {
let current_content = element.inner_html();
let separator_line = "<hr style='border: 1px solid #333; margin: 5px 0;'>";
let new_content = format!("{}{}", current_content, separator_line);
element.set_inner_html(&new_content);
}
}
/// グループ開始(見出し付き)
pub fn group(&self, title: &str) {
if let Some(element) = self.get_target_element() {
let current_content = element.inner_html();
let group_header = format!(
"<div style='font-weight: bold; color: #4ecdc4; margin: 10px 0 5px 0;'>📂 {}</div><div style='margin-left: 20px; color: white;'>",
title
);
let new_content = format!("{}{}", current_content, group_header);
element.set_inner_html(&new_content);
}
}
/// グループ終了
pub fn group_end(&self) {
if let Some(element) = self.get_target_element() {
let current_content = element.inner_html();
let group_footer = "</div>";
let new_content = format!("{}{}", current_content, group_footer);
element.set_inner_html(&new_content);
}
}
}
#[cfg(target_arch = "wasm32")]
impl NyashBox for WebConsoleBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("WebConsoleBox({})", self.target_element_id))
}
fn as_any(&self) -> &dyn Any {
self
}
fn type_name(&self) -> &'static str {
"WebConsoleBox"
}
fn box_id(&self) -> u64 {
self.id
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_console) = other.as_any().downcast_ref::<WebConsoleBox>() {
BoolBox::new(self.id == other_console.id)
} else {
BoolBox::new(false)
}
}
}

View File

@ -0,0 +1,169 @@
/*!
* WebDisplayBox - ブラウザHTML要素表示制御Box
*
* WebAssembly環境でHTML要素への直接出力・スタイル制御
* プレイグラウンドの出力パネル等を完全制御
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox};
use std::any::Any;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use web_sys::{Element, HtmlElement};
// 🌐 Browser HTML element display control Box
#[cfg(target_arch = "wasm32")]
#[derive(Debug, Clone)]
pub struct WebDisplayBox {
id: u64,
target_element_id: String,
}
#[cfg(target_arch = "wasm32")]
impl WebDisplayBox {
pub fn new(element_id: String) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self {
id,
target_element_id: element_id,
}
}
/// 指定した要素IDのHTML要素を取得
fn get_target_element(&self) -> Option<Element> {
let window = web_sys::window()?;
let document = window.document()?;
document.get_element_by_id(&self.target_element_id)
}
/// テキストを追加出力
pub fn print(&self, message: &str) {
if let Some(element) = self.get_target_element() {
let current_content = element.inner_html();
let new_content = if current_content.is_empty() {
message.to_string()
} else {
format!("{}{}", current_content, message)
};
element.set_inner_html(&new_content);
}
}
/// テキストを改行付きで追加出力
pub fn println(&self, message: &str) {
if let Some(element) = self.get_target_element() {
let current_content = element.inner_html();
let new_content = if current_content.is_empty() {
message.to_string()
} else {
format!("{}<br>{}", current_content, message)
};
element.set_inner_html(&new_content);
}
}
/// HTMLコンテンツを完全置換
pub fn set_html(&self, html_content: &str) {
if let Some(element) = self.get_target_element() {
element.set_inner_html(html_content);
}
}
/// HTMLコンテンツを追加
pub fn append_html(&self, html_content: &str) {
if let Some(element) = self.get_target_element() {
let current_content = element.inner_html();
let new_content = format!("{}{}", current_content, html_content);
element.set_inner_html(&new_content);
}
}
/// CSSスタイルを設定
pub fn set_css(&self, property: &str, value: &str) {
if let Some(element) = self.get_target_element() {
if let Some(html_element) = element.dyn_ref::<HtmlElement>() {
// HTMLElement の style プロパティへアクセス
let _ = html_element.style().set_property(property, value);
}
}
}
/// CSSクラスを追加
pub fn add_class(&self, class_name: &str) {
if let Some(element) = self.get_target_element() {
let _ = element.class_list().add_1(class_name);
}
}
/// CSSクラスを削除
pub fn remove_class(&self, class_name: &str) {
if let Some(element) = self.get_target_element() {
let _ = element.class_list().remove_1(class_name);
}
}
/// 内容をクリア
pub fn clear(&self) {
if let Some(element) = self.get_target_element() {
element.set_inner_html("");
}
}
/// 要素を表示
pub fn show(&self) {
self.set_css("display", "block");
}
/// 要素を非表示
pub fn hide(&self) {
self.set_css("display", "none");
}
/// スクロールを最下部に移動
pub fn scroll_to_bottom(&self) {
if let Some(element) = self.get_target_element() {
if let Some(html_element) = element.dyn_ref::<HtmlElement>() {
html_element.set_scroll_top(html_element.scroll_height());
}
}
}
}
#[cfg(target_arch = "wasm32")]
impl NyashBox for WebDisplayBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("WebDisplayBox({})", self.target_element_id))
}
fn as_any(&self) -> &dyn Any {
self
}
fn type_name(&self) -> &'static str {
"WebDisplayBox"
}
fn box_id(&self) -> u64 {
self.id
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_display) = other.as_any().downcast_ref::<WebDisplayBox>() {
BoolBox::new(self.id == other_display.id)
} else {
BoolBox::new(false)
}
}
}

226
src/channel_box.rs Normal file
View File

@ -0,0 +1,226 @@
/*!
* Nyash P2P Channel System - Arrow構文によるBox間通信
*
* alice >> bob でメッセージ送信を可能にする
* Everything is Box哲学に基づくP2P通信システム
*/
use crate::box_trait::{NyashBox, StringBox, VoidBox};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, Weak};
use std::fmt::{Debug, Display};
use std::any::Any;
/// チャンネル - Box間の通信路
#[derive(Clone)]
pub struct ChannelBox {
/// 送信者の名前
pub sender_name: String,
/// 受信者の名前
pub receiver_name: String,
/// リンクされたBox弱参照
linked_boxes: Arc<Mutex<HashMap<String, Weak<Mutex<dyn NyashBox>>>>>,
/// メッセージハンドラー
handlers: Arc<Mutex<HashMap<String, Box<dyn Fn(Box<dyn NyashBox>) -> Box<dyn NyashBox> + Send>>>>,
/// チャンネルID
id: u64,
}
impl ChannelBox {
/// 新しいチャンネルを作成
pub fn new(sender: &str, receiver: &str) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self {
sender_name: sender.to_string(),
receiver_name: receiver.to_string(),
linked_boxes: Arc::new(Mutex::new(HashMap::new())),
handlers: Arc::new(Mutex::new(HashMap::new())),
id,
}
}
/// Boxをリンク
pub fn link(&self, name: &str, target: Arc<Mutex<dyn NyashBox>>) {
self.linked_boxes.lock().unwrap().insert(
name.to_string(),
Arc::downgrade(&target)
);
}
/// メッセージハンドラーを登録
pub fn register_handler<F>(&self, method: &str, handler: F)
where
F: Fn(Box<dyn NyashBox>) -> Box<dyn NyashBox> + Send + 'static
{
self.handlers.lock().unwrap().insert(
method.to_string(),
Box::new(handler)
);
}
/// メソッド呼び出しを実行
pub fn invoke(&self, method: &str, args: Vec<Box<dyn NyashBox>>) -> Box<dyn NyashBox> {
// "*" はブロードキャスト
if self.receiver_name == "*" {
return self.broadcast(method, args);
}
// 通常の送信
let handlers = self.handlers.lock().unwrap();
if let Some(handler) = handlers.get(method) {
// 簡易実装:最初の引数のみ使用
let arg = args.get(0)
.map(|a| a.clone_box())
.unwrap_or_else(|| Box::new(VoidBox::new()));
handler(arg)
} else {
Box::new(StringBox::new(&format!("No handler for method: {}", method)))
}
}
/// 送信者名を取得
pub fn sender(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(&self.sender_name))
}
/// 受信者名を取得
pub fn receiver(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(&self.receiver_name))
}
/// ブロードキャスト
fn broadcast(&self, _method: &str, _args: Vec<Box<dyn NyashBox>>) -> Box<dyn NyashBox> {
let linked = self.linked_boxes.lock().unwrap();
let mut results = Vec::new();
for (name, weak_box) in linked.iter() {
if let Some(_strong_box) = weak_box.upgrade() {
// 各Boxにメッセージを送信
results.push(format!("Sent to {}", name));
}
}
Box::new(StringBox::new(&format!("Broadcast complete: {:?}", results)))
}
}
impl NyashBox for ChannelBox {
fn type_name(&self) -> &'static str {
"ChannelBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new(&format!("Channel({} >> {})", self.sender_name, self.receiver_name))
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox {
if let Some(other_channel) = other.as_any().downcast_ref::<ChannelBox>() {
crate::box_trait::BoolBox::new(
self.sender_name == other_channel.sender_name &&
self.receiver_name == other_channel.receiver_name
)
} else {
crate::box_trait::BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for ChannelBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_string_box().value)
}
}
impl Debug for ChannelBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChannelBox")
.field("sender_name", &self.sender_name)
.field("receiver_name", &self.receiver_name)
.field("id", &self.id)
.finish()
}
}
/// メッセージを表すBox
#[derive(Debug, Clone)]
pub struct MessageBox {
pub sender: String,
pub content: String,
pub timestamp: u64,
}
impl MessageBox {
pub fn new(sender: &str, content: &str) -> Self {
static mut COUNTER: u64 = 0;
let timestamp = unsafe {
COUNTER += 1;
COUNTER
};
Self {
sender: sender.to_string(),
content: content.to_string(),
timestamp,
}
}
}
impl NyashBox for MessageBox {
fn type_name(&self) -> &'static str {
"MessageBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new(&format!("[{}] {}: {}", self.timestamp, self.sender, self.content))
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox {
if let Some(other_msg) = other.as_any().downcast_ref::<MessageBox>() {
crate::box_trait::BoolBox::new(
self.sender == other_msg.sender &&
self.content == other_msg.content
)
} else {
crate::box_trait::BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.timestamp
}
}
impl Display for MessageBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_string_box().value)
}
}

359
src/environment.rs Normal file
View File

@ -0,0 +1,359 @@
/*!
* Nyash Environment System - Rust所有権ベースのスコープ管理
*
* 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>>>,
/// 親環境への参照 (Arc<Mutex<>>でスレッド安全)
parent: Option<Arc<Mutex<Environment>>>,
/// スコープレベル (デバッグ用)
level: usize,
/// スコープ名 (関数名、クラス名など)
scope_name: String,
/// Box解放管理
finalizer: Mutex<BoxFinalizer>,
}
/// Environment操作エラー
#[derive(Error, Debug)]
pub enum EnvironmentError {
#[error("Variable '{name}' is not defined")]
UndefinedVariable { name: String },
#[error("Cannot access parent environment: {reason}")]
ParentAccessError { reason: String },
#[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()),
}))
}
/// 親環境を持つ新しい環境を作成 (関数、クラス用)
pub fn new_child(
parent: Arc<Mutex<Environment>>,
scope_name: impl Into<String>
) -> Arc<Mutex<Self>> {
let level = parent.lock().unwrap().level + 1;
Arc::new(Mutex::new(Self {
bindings: Mutex::new(HashMap::new()),
parent: Some(parent),
level,
scope_name: scope_name.into(),
finalizer: Mutex::new(BoxFinalizer::new()),
}))
}
/// 変数を現在のスコープに定義
pub fn define(&self, name: impl Into<String>, value: Box<dyn NyashBox>) {
let name = name.into();
self.bindings.lock().unwrap().insert(name, value);
}
/// 変数を取得 (スコープチェーンを辿る)
pub fn get(&self, name: &str) -> Result<Box<dyn NyashBox>, EnvironmentError> {
// 現在のスコープから検索
if let Some(value) = self.bindings.lock().unwrap().get(name) {
return Ok(value.clone_box());
}
// 親スコープから検索
if let Some(parent) = &self.parent {
return parent.lock().unwrap().get(name);
}
// 見つからない
Err(EnvironmentError::UndefinedVariable {
name: name.to_string()
})
}
/// 変数を設定 (既存変数の更新 or 新規定義)
pub fn set(&self, name: impl Into<String>, value: Box<dyn NyashBox>) -> Result<(), EnvironmentError> {
let name = name.into();
// 現在のスコープにある場合は更新
if self.bindings.lock().unwrap().contains_key(&name) {
self.bindings.lock().unwrap().insert(name, value);
return Ok(());
}
// 親スコープで再帰的に検索・設定
if let Some(parent) = &self.parent {
match parent.lock().unwrap().set(&name, value.clone_box()) {
Ok(()) => return Ok(()),
Err(EnvironmentError::UndefinedVariable { .. }) => {
// 親にもない場合は現在のスコープに新規定義
}
Err(e) => return Err(e),
}
}
// 新規定義として現在のスコープに追加
self.bindings.lock().unwrap().insert(name, value);
Ok(())
}
/// 変数が存在するかチェック
pub fn exists(&self, name: &str) -> bool {
self.get(name).is_ok()
}
/// 変数を削除 (現在のスコープからのみ)
pub fn undefine(&self, name: &str) -> bool {
self.bindings.lock().unwrap().remove(name).is_some()
}
/// 現在のスコープの変数一覧を取得
pub fn list_variables(&self) -> Vec<String> {
self.bindings.lock().unwrap().keys().cloned().collect()
}
/// スコープ情報を取得 (デバッグ用)
pub fn scope_info(&self) -> String {
format!("{}[{}] (level {})", self.scope_name, self.bindings.lock().unwrap().len(), self.level)
}
/// スコープチェーン全体の情報を取得
pub fn scope_chain_info(&self) -> Vec<String> {
let mut chain = vec![self.scope_info()];
if let Some(parent) = &self.parent {
chain.extend(parent.lock().unwrap().scope_chain_info());
}
chain
}
/// 全変数をダンプ (デバッグ用)
pub fn dump_all_variables(&self) -> HashMap<String, String> {
let mut all_vars = HashMap::new();
// 現在のスコープの変数
for (name, value) in self.bindings.lock().unwrap().iter() {
all_vars.insert(
format!("{}::{}", self.scope_name, name),
value.to_string_box().value
);
}
// 親スコープの変数 (再帰的)
if let Some(parent) = &self.parent {
all_vars.extend(parent.lock().unwrap().dump_all_variables());
}
all_vars
}
/// 新しいBoxを追跡対象に追加
pub fn track_box(&self, nyash_box: Box<dyn NyashBox>) {
self.finalizer.lock().unwrap().track(nyash_box);
}
/// スコープ終了時にすべてのBoxを解放
pub fn finalize_all_boxes(&self) {
self.finalizer.lock().unwrap().finalize_all();
}
/// 指定したBoxを解放対象から除外関数の返り値など
pub fn exclude_from_finalization(&self, nyash_box: &Box<dyn NyashBox>) {
self.finalizer.lock().unwrap().exclude_from_finalization(nyash_box);
}
}
/// 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(),
}
}
pub fn new_with_parent(parent: Arc<Mutex<Environment>>) -> Self {
Self {
inner: Environment::new_child(parent, "python_compat"),
_bindings: HashMap::new(),
}
}
/// 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);
}
/// Python版のgetメソッド互換
pub fn get(&self, name: &str) -> Box<dyn NyashBox> {
self.inner.lock().unwrap().get(name).unwrap_or_else(|_| {
Box::new(VoidBox::new())
})
}
/// Rustネイティブ環境への参照を取得
pub fn as_native(&self) -> Arc<Mutex<Environment>> {
self.inner.clone()
}
}
// ===== Tests =====
#[cfg(test)]
mod tests {
use super::*;
use crate::box_trait::{StringBox, IntegerBox, BoolBox};
#[test]
fn test_global_environment() {
let env = Environment::new_global();
// 変数定義
env.lock().unwrap().define("test_var", Box::new(StringBox::new("hello")));
// 変数取得
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");
}
#[test]
fn test_nested_scopes() {
let global = Environment::new_global();
let function_scope = Environment::new_child(global.clone(), "test_function");
// グローバルスコープに変数定義
global.lock().unwrap().define("global_var", Box::new(StringBox::new("global")));
// 関数スコープに変数定義
function_scope.lock().unwrap().define("local_var", Box::new(StringBox::new("local")));
// 関数スコープからグローバル変数にアクセス可能
let global_from_function = function_scope.lock().unwrap().get("global_var").unwrap();
let global_str = global_from_function.as_any().downcast_ref::<StringBox>().unwrap();
assert_eq!(global_str.value, "global");
// グローバルスコープからローカル変数にはアクセス不可
assert!(global.lock().unwrap().get("local_var").is_err());
}
#[test]
fn test_variable_shadowing() {
let global = Environment::new_global();
let local = Environment::new_child(global.clone(), "local_scope");
// 同名変数を両スコープに定義
global.lock().unwrap().define("same_name", Box::new(StringBox::new("global_value")));
local.lock().unwrap().define("same_name", Box::new(StringBox::new("local_value")));
// ローカルスコープからはローカル値が取得される (シャドウイング)
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");
// グローバルスコープからはグローバル値が取得される
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");
}
#[test]
fn test_variable_setting() {
let global = Environment::new_global();
let local = Environment::new_child(global.clone(), "local_scope");
// グローバルに変数定義
global.lock().unwrap().define("shared_var", Box::new(IntegerBox::new(100)));
// ローカルスコープから変数を更新
local.lock().unwrap().set("shared_var", Box::new(IntegerBox::new(200))).unwrap();
// グローバルスコープの値が更新されている
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);
}
#[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");
// 各スコープに変数を追加
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")));
// スコープチェーン情報を確認
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"));
}
#[test]
fn test_python_compat() {
let mut env = PythonCompatEnvironment::new();
// Python互換インターフェースで変数操作
env.define("test", Box::new(BoolBox::new(true)));
let value = env.get("test");
let bool_val = value.as_any().downcast_ref::<BoolBox>().unwrap();
assert_eq!(bool_val.value, true);
// _bindingsでも確認可能
assert!(env._bindings.contains_key("test"));
}
#[test]
fn test_error_handling() {
let env = Environment::new_global();
// 存在しない変数へのアクセス
let result = env.lock().unwrap().get("nonexistent");
assert!(result.is_err());
match result {
Err(EnvironmentError::UndefinedVariable { name }) => {
assert_eq!(name, "nonexistent");
}
_ => panic!("Expected UndefinedVariable error"),
}
}
}

104
src/exception_box.rs Normal file
View File

@ -0,0 +1,104 @@
/*!
* Exception Boxes for Nyash try/catch/throw system
*
* Everything is Box哲学に基づく例外システム
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox};
use std::any::Any;
use std::collections::HashMap;
/// 基底例外Box
#[derive(Debug, Clone)]
pub struct ErrorBox {
pub message: String,
pub stack_trace: Vec<String>,
pub cause: Option<Box<ErrorBox>>,
id: u64,
}
impl ErrorBox {
pub fn new(message: &str) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self {
message: message.to_string(),
stack_trace: Vec::new(),
cause: None,
id,
}
}
pub fn with_cause(message: &str, cause: ErrorBox) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self {
message: message.to_string(),
stack_trace: Vec::new(),
cause: Some(Box::new(cause)),
id,
}
}
pub fn add_stack_frame(&mut self, frame: String) {
self.stack_trace.push(frame);
}
pub fn get_full_message(&self) -> String {
let mut msg = self.message.clone();
if let Some(ref cause) = self.cause {
msg.push_str(&format!("\nCaused by: {}", cause.get_full_message()));
}
msg
}
}
impl NyashBox for ErrorBox {
fn type_name(&self) -> &'static str {
"ErrorBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("ErrorBox({})", self.message))
}
fn box_id(&self) -> u64 {
self.id
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_error) = other.as_any().downcast_ref::<ErrorBox>() {
BoolBox::new(self.message == other_error.message)
} else {
BoolBox::new(false)
}
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
}
/// 例外タイプの判定ユーティリティ
pub fn is_exception_type(exception: &dyn NyashBox, type_name: &str) -> bool {
match type_name {
"Error" | "ErrorBox" => exception.as_any().downcast_ref::<ErrorBox>().is_some(),
_ => false,
}
}
/// 例外の作成ヘルパー
pub fn create_exception(_type_name: &str, message: &str, _extra_info: &HashMap<String, String>) -> Box<dyn NyashBox> {
// 現在はErrorBoxのみサポートシンプル実装
Box::new(ErrorBox::new(message))
}

111
src/finalization.rs Normal file
View File

@ -0,0 +1,111 @@
/*!
* Nyash Finalization System - Memory Management
*
* fini()によるメモリ管理システムの実装
* - 解放済みBoxの追跡
* - スコープベースの自動解放
* - 再代入時の自動解放
*/
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
use std::fmt;
use crate::box_trait::NyashBox;
use lazy_static::lazy_static;
lazy_static! {
/// グローバルな解放済みBox ID管理
static ref FINALIZED_BOXES: Arc<Mutex<HashSet<u64>>> = Arc::new(Mutex::new(HashSet::new()));
}
/// Boxが既に解放済みかチェック
pub fn is_finalized(box_id: u64) -> bool {
FINALIZED_BOXES.lock().unwrap().contains(&box_id)
}
/// Boxを解放済みとして記録
pub fn mark_as_finalized(box_id: u64) {
FINALIZED_BOXES.lock().unwrap().insert(box_id);
}
/// Box解放管理
pub struct BoxFinalizer {
/// このスコープで作成されたBox ID
created_boxes: Vec<(u64, Box<dyn NyashBox>)>,
/// finalization除外対象のBox ID関数の返り値など
excluded_boxes: HashSet<u64>,
}
impl BoxFinalizer {
pub fn new() -> Self {
Self {
created_boxes: Vec::new(),
excluded_boxes: HashSet::new(),
}
}
/// 新しいBoxを追跡対象に追加
pub fn track(&mut self, nyash_box: Box<dyn NyashBox>) {
let box_id = nyash_box.box_id();
self.created_boxes.push((box_id, nyash_box));
}
/// 指定したBoxを解放対象から除外関数の返り値など
pub fn exclude_from_finalization(&mut self, nyash_box: &Box<dyn NyashBox>) {
let box_id = nyash_box.box_id();
self.excluded_boxes.insert(box_id);
}
/// スコープ終了時に全てのBoxを解放除外対象を除く
pub fn finalize_all(&mut self) {
// 作成順(古い順)に解放
for (box_id, nyash_box) in &self.created_boxes {
// 除外対象は解放しない
if self.excluded_boxes.contains(box_id) {
continue;
}
if !is_finalized(*box_id) {
// fini()メソッドを呼び出す(存在する場合)
if let Some(instance) = nyash_box.as_any().downcast_ref::<crate::instance::InstanceBox>() {
let _ = instance.fini();
}
mark_as_finalized(*box_id);
}
}
self.created_boxes.clear();
self.excluded_boxes.clear();
}
}
impl Drop for BoxFinalizer {
fn drop(&mut self) {
self.finalize_all();
}
}
impl fmt::Debug for BoxFinalizer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BoxFinalizer")
.field("created_boxes_count", &self.created_boxes.len())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_finalization_tracking() {
let box_id = 12345;
assert!(!is_finalized(box_id));
mark_as_finalized(box_id);
assert!(is_finalized(box_id));
// 二重解放チェック
mark_as_finalized(box_id); // 問題なし
assert!(is_finalized(box_id));
}
}

204
src/instance.rs Normal file
View File

@ -0,0 +1,204 @@
/*!
* Nyash Instance System - Box Instance Implementation
*
* BoxインスタンスとClassBoxの実装
* Everything is Box哲学に基づくオブジェクト指向システム
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox};
use crate::ast::ASTNode;
use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::any::Any;
use std::sync::{Arc, Mutex};
/// Boxインスタンス - フィールドとメソッドを持つオブジェクト
#[derive(Debug, Clone)]
pub struct InstanceBox {
/// クラス名
pub class_name: String,
/// フィールド値
pub fields: Arc<Mutex<HashMap<String, Box<dyn NyashBox>>>>,
/// メソッド定義ClassBoxから共有
pub methods: Arc<HashMap<String, ASTNode>>,
/// インスタンスID
id: u64,
/// 解放済みフラグ
finalized: Arc<Mutex<bool>>,
}
impl InstanceBox {
pub fn new(class_name: String, fields: Vec<String>, methods: HashMap<String, ASTNode>) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
// フィールドをVoidBoxで初期化
let mut field_map = HashMap::new();
for field in fields {
field_map.insert(field, Box::new(VoidBox::new()) as Box<dyn NyashBox>);
}
Self {
class_name,
fields: Arc::new(Mutex::new(field_map)),
methods: Arc::new(methods),
id,
finalized: Arc::new(Mutex::new(false)),
}
}
/// フィールドの値を取得
pub fn get_field(&self, field_name: &str) -> Option<Box<dyn NyashBox>> {
self.fields.lock().unwrap().get(field_name).map(|v| v.clone_box())
}
/// フィールドに値を設定
pub fn set_field(&self, field_name: &str, value: Box<dyn NyashBox>) -> Result<(), String> {
let mut fields = self.fields.lock().unwrap();
if fields.contains_key(field_name) {
fields.insert(field_name.to_string(), value);
Ok(())
} else {
Err(format!("Field '{}' does not exist in {}", field_name, self.class_name))
}
}
/// 🌍 GlobalBox用フィールドを動的に追加・設定
pub fn set_field_dynamic(&mut self, field_name: String, value: Box<dyn NyashBox>) {
let mut fields = self.fields.lock().unwrap();
fields.insert(field_name, value);
}
/// メソッド定義を取得
pub fn get_method(&self, method_name: &str) -> Option<&ASTNode> {
self.methods.get(method_name)
}
/// メソッドが存在するかチェック
pub fn has_method(&self, method_name: &str) -> bool {
self.methods.contains_key(method_name)
}
/// 🌍 GlobalBox用メソッドを動的に追加
pub fn add_method(&mut self, method_name: String, method_ast: ASTNode) {
// Arc<T>は不変なので、新しいHashMapを作成してArcで包む
let mut new_methods = (*self.methods).clone();
new_methods.insert(method_name, method_ast);
self.methods = Arc::new(new_methods);
}
/// fini()メソッド - インスタンスの解放
pub fn fini(&self) -> Result<(), String> {
let mut finalized = self.finalized.lock().unwrap();
if *finalized {
// 既に解放済みなら何もしない
return Ok(());
}
*finalized = true;
// フィールドをクリア
let mut fields = self.fields.lock().unwrap();
fields.clear();
Ok(())
}
/// 解放済みかチェック
pub fn is_finalized(&self) -> bool {
*self.finalized.lock().unwrap()
}
}
impl NyashBox for InstanceBox {
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("<{} instance #{}>", self.class_name, self.id))
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_instance) = other.as_any().downcast_ref::<InstanceBox>() {
// 同じインスタンスIDなら等しい
BoolBox::new(self.id == other_instance.id)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"InstanceBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
// インスタンスは同じフィールドを共有
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for InstanceBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<{} instance>", self.class_name)
}
}
// ===== Tests =====
#[cfg(test)]
mod tests {
use super::*;
use crate::box_trait::IntegerBox;
#[test]
fn test_instance_creation() {
let fields = vec!["x".to_string(), "y".to_string()];
let methods = HashMap::new();
let instance = InstanceBox::new("Point".to_string(), fields, methods);
assert_eq!(instance.class_name, "Point");
assert!(instance.get_field("x").is_some());
assert!(instance.get_field("y").is_some());
assert!(instance.get_field("z").is_none());
}
#[test]
fn test_field_access() {
let fields = vec!["value".to_string()];
let methods = HashMap::new();
let instance = InstanceBox::new("TestBox".to_string(), fields, methods);
// フィールドに値を設定
let int_value = Box::new(IntegerBox::new(42)) as Box<dyn NyashBox>;
instance.set_field("value", int_value).unwrap();
// フィールドの値を取得
let retrieved = instance.get_field("value").unwrap();
let int_box = retrieved.as_any().downcast_ref::<IntegerBox>().unwrap();
assert_eq!(int_box.value, 42);
}
#[test]
fn test_instance_equality() {
let instance1 = InstanceBox::new("Test".to_string(), vec![], HashMap::new());
let instance2 = InstanceBox::new("Test".to_string(), vec![], HashMap::new());
// 異なるインスタンスは等しくない
assert!(!instance1.equals(&instance2).value);
// 同じインスタンスは等しい
assert!(instance1.equals(&instance1).value);
}
}

File diff suppressed because it is too large Load Diff

702
src/interpreter/core.rs Normal file
View File

@ -0,0 +1,702 @@
/*!
* Nyash Interpreter - Rust Implementation
*
* Python版nyashc_v4.pyのインタープリターをRustで完全再実装
* Everything is Box哲学に基づくAST実行エンジン
*/
use crate::ast::{ASTNode, Span};
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox};
use crate::instance::InstanceBox;
use crate::parser::ParseError;
use std::sync::{Arc, Mutex, RwLock};
use std::collections::{HashMap, HashSet};
use thiserror::Error;
use super::{ControlFlow, BoxDeclaration, ConstructorContext, StaticBoxDefinition, StaticBoxState};
/// 実行時エラー
#[derive(Error, Debug)]
pub enum RuntimeError {
#[error("Undefined variable '{name}'")]
UndefinedVariable { name: String },
#[error("Undefined function '{name}'")]
UndefinedFunction { name: String },
#[error("Undefined class '{name}'")]
UndefinedClass { name: String },
#[error("Type error: {message}")]
TypeError { message: String },
#[error("Invalid operation: {message}")]
InvalidOperation { message: String },
#[error("Break outside of loop")]
BreakOutsideLoop,
#[error("Return outside of function")]
ReturnOutsideFunction,
#[error("Uncaught exception")]
UncaughtException,
#[error("Parse error: {0}")]
ParseError(#[from] ParseError),
#[error("Environment error: {0}")]
EnvironmentError(String),
// === 🔥 Enhanced Errors with Span Information ===
#[error("Undefined variable '{name}' at {span}")]
UndefinedVariableAt { name: String, span: Span },
#[error("Type error: {message} at {span}")]
TypeErrorAt { message: String, span: Span },
#[error("Invalid operation: {message} at {span}")]
InvalidOperationAt { message: String, span: Span },
#[error("Break outside of loop at {span}")]
BreakOutsideLoopAt { span: Span },
#[error("Return outside of function at {span}")]
ReturnOutsideFunctionAt { span: Span },
#[error("Runtime failure: {message}")]
RuntimeFailure { message: String },
}
impl RuntimeError {
/// エラーの詳細な文脈付きメッセージを生成
pub fn detailed_message(&self, source: Option<&str>) -> String {
match self {
// Enhanced errors with span information
RuntimeError::UndefinedVariableAt { name, span } => {
let mut msg = format!("⚠️ Undefined variable '{}'", name);
if let Some(src) = source {
msg.push('\n');
msg.push_str(&span.error_context(src));
} else {
msg.push_str(&format!(" at {}", span));
}
msg
}
RuntimeError::TypeErrorAt { message, span } => {
let mut msg = format!("⚠️ Type error: {}", message);
if let Some(src) = source {
msg.push('\n');
msg.push_str(&span.error_context(src));
} else {
msg.push_str(&format!(" at {}", span));
}
msg
}
RuntimeError::InvalidOperationAt { message, span } => {
let mut msg = format!("⚠️ Invalid operation: {}", message);
if let Some(src) = source {
msg.push('\n');
msg.push_str(&span.error_context(src));
} else {
msg.push_str(&format!(" at {}", span));
}
msg
}
RuntimeError::BreakOutsideLoopAt { span } => {
let mut msg = "⚠️ Break statement outside of loop".to_string();
if let Some(src) = source {
msg.push('\n');
msg.push_str(&span.error_context(src));
} else {
msg.push_str(&format!(" at {}", span));
}
msg
}
RuntimeError::ReturnOutsideFunctionAt { span } => {
let mut msg = "⚠️ Return statement outside of function".to_string();
if let Some(src) = source {
msg.push('\n');
msg.push_str(&span.error_context(src));
} else {
msg.push_str(&format!(" at {}", span));
}
msg
}
// Fallback for old error variants without span
_ => format!("⚠️ {}", self),
}
}
}
/// スレッド間で共有される状態
#[derive(Clone)]
pub struct SharedState {
/// 🌍 GlobalBox - すべてのトップレベル関数とグローバル変数を管理
pub global_box: Arc<Mutex<InstanceBox>>,
/// Box宣言のレジストリ読み込みが多いのでRwLock
pub box_declarations: Arc<RwLock<HashMap<String, BoxDeclaration>>>,
/// 🔥 静的関数のレジストリ読み込みが多いのでRwLock
pub static_functions: Arc<RwLock<HashMap<String, HashMap<String, ASTNode>>>>,
/// 🔥 Static Box定義レジストリ遅延初期化用
pub static_box_definitions: Arc<RwLock<HashMap<String, StaticBoxDefinition>>>,
/// 読み込み済みファイル(重複防止)
pub included_files: Arc<Mutex<HashSet<String>>>,
}
impl SharedState {
/// 新しい共有状態を作成
pub fn new() -> Self {
let global_box = InstanceBox::new(
"Global".to_string(),
vec![], // フィールド名(空から始める)
HashMap::new(), // メソッド(グローバル関数)
);
Self {
global_box: Arc::new(Mutex::new(global_box)),
box_declarations: Arc::new(RwLock::new(HashMap::new())),
static_functions: Arc::new(RwLock::new(HashMap::new())),
static_box_definitions: Arc::new(RwLock::new(HashMap::new())),
included_files: Arc::new(Mutex::new(HashSet::new())),
}
}
}
/// Nyashインタープリター - AST実行エンジン
pub struct NyashInterpreter {
/// 共有状態(スレッド間で共有)
pub(super) shared: SharedState,
/// 📦 local変数スタック関数呼び出し時の一時変数
pub(super) local_vars: HashMap<String, Box<dyn NyashBox>>,
/// 📤 outbox変数スタックstatic関数内の所有権移転変数
pub(super) outbox_vars: HashMap<String, Box<dyn NyashBox>>,
/// 制御フロー状態
pub(super) control_flow: ControlFlow,
/// 現在実行中のコンストラクタ情報
pub(super) current_constructor_context: Option<ConstructorContext>,
}
impl NyashInterpreter {
/// 新しいインタープリターを作成
pub fn new() -> Self {
let shared = SharedState::new();
Self {
shared,
local_vars: HashMap::new(),
outbox_vars: HashMap::new(),
control_flow: ControlFlow::None,
current_constructor_context: None,
}
}
/// 共有状態から新しいインタープリターを作成(非同期実行用)
pub fn with_shared(shared: SharedState) -> Self {
Self {
shared,
local_vars: HashMap::new(),
outbox_vars: HashMap::new(),
control_flow: ControlFlow::None,
current_constructor_context: None,
}
}
/// ASTを実行
pub fn execute(&mut self, ast: ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
self.execute_node(&ast)
}
/// ノードを実行
fn execute_node(&mut self, node: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
match node {
ASTNode::Program { statements, .. } => {
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
for statement in statements {
result = self.execute_statement(statement)?;
// 制御フローチェック
match &self.control_flow {
ControlFlow::Break => {
return Err(RuntimeError::BreakOutsideLoop);
}
ControlFlow::Return(_) => {
return Err(RuntimeError::ReturnOutsideFunction);
}
ControlFlow::Throw(_) => {
return Err(RuntimeError::UncaughtException);
}
ControlFlow::None => {}
}
}
// 🎯 Static Box Main パターン - main()メソッドの自動実行
let has_main_method = {
if let Ok(definitions) = self.shared.static_box_definitions.read() {
if let Some(main_definition) = definitions.get("Main") {
main_definition.methods.contains_key("main")
} else {
false
}
} else {
false
}
};
if has_main_method {
// Main static boxを初期化
self.ensure_static_box_initialized("Main")?;
// Main.main() を呼び出し
let main_call_ast = ASTNode::MethodCall {
object: Box::new(ASTNode::FieldAccess {
object: Box::new(ASTNode::Variable {
name: "statics".to_string(),
span: crate::ast::Span::unknown(),
}),
field: "Main".to_string(),
span: crate::ast::Span::unknown(),
}),
method: "main".to_string(),
arguments: vec![],
span: crate::ast::Span::unknown(),
};
// main()の戻り値を最終結果として使用
result = self.execute_statement(&main_call_ast)?;
}
Ok(result)
}
_ => self.execute_statement(node),
}
}
// ========== 🌍 GlobalBox変数解決システム ==========
/// 革命的変数解決: local変数 → GlobalBoxフィールド → エラー
pub(super) fn resolve_variable(&self, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 1. outbox変数を最初にチェックstatic関数内で優先
if let Some(outbox_value) = self.outbox_vars.get(name) {
return Ok(outbox_value.clone_box());
}
// 2. local変数をチェック
if let Some(local_value) = self.local_vars.get(name) {
return Ok(local_value.clone_box());
}
// 3. GlobalBoxのフィールドをチェック
let global_box = self.shared.global_box.lock().unwrap();
if let Some(field_value) = global_box.get_field(name) {
return Ok(field_value);
}
// 4. エラー:見つからない
Err(RuntimeError::UndefinedVariable {
name: name.to_string(),
})
}
/// 🔥 厳密変数設定: 明示的宣言のみ許可 - Everything is Box哲学
pub(super) fn set_variable(&mut self, name: &str, value: Box<dyn NyashBox>) -> Result<(), RuntimeError> {
// 1. outbox変数が存在する場合は更新
if self.outbox_vars.contains_key(name) {
self.outbox_vars.insert(name.to_string(), value);
return Ok(());
}
// 2. local変数が存在する場合は更新
if self.local_vars.contains_key(name) {
self.local_vars.insert(name.to_string(), value);
return Ok(());
}
// 3. GlobalBoxのフィールドが既に存在する場合は更新
{
let global_box = self.shared.global_box.lock().unwrap();
if global_box.get_field(name).is_some() {
drop(global_box); // lockを解放
let mut global_box = self.shared.global_box.lock().unwrap();
global_box.set_field_dynamic(name.to_string(), value);
return Ok(());
}
}
// 4. 🚨 未宣言変数への代入は厳密にエラー
Err(RuntimeError::UndefinedVariable {
name: format!(
"{}\n💡 Suggestion: Declare the variable first:\n • For fields: Add '{}' to 'init {{ }}' block\n • For local variables: Use 'local {}'\n • For field access: Use 'me.{}'",
name, name, name, name
),
})
}
/// local変数を宣言関数内でのみ有効
pub(super) fn declare_local_variable(&mut self, name: &str, value: Box<dyn NyashBox>) {
self.local_vars.insert(name.to_string(), value);
}
/// outbox変数を宣言static関数内で所有権移転
pub(super) fn declare_outbox_variable(&mut self, name: &str, value: Box<dyn NyashBox>) {
self.outbox_vars.insert(name.to_string(), value);
}
/// local変数スタックを保存・復元関数呼び出し時
pub(super) fn save_local_vars(&self) -> HashMap<String, Box<dyn NyashBox>> {
self.local_vars.iter()
.map(|(k, v)| (k.clone(), v.clone_box()))
.collect()
}
pub(super) fn restore_local_vars(&mut self, saved: HashMap<String, Box<dyn NyashBox>>) {
self.local_vars = saved;
}
/// outbox変数スタックを保存・復元static関数呼び出し時
pub(super) fn save_outbox_vars(&self) -> HashMap<String, Box<dyn NyashBox>> {
self.outbox_vars.iter()
.map(|(k, v)| (k.clone(), v.clone_box()))
.collect()
}
pub(super) fn restore_outbox_vars(&mut self, saved: HashMap<String, Box<dyn NyashBox>>) {
self.outbox_vars = saved;
}
/// トップレベル関数をGlobalBoxのメソッドとして登録
pub(super) fn register_global_function(&mut self, name: String, func_ast: ASTNode) -> Result<(), RuntimeError> {
let mut global_box = self.shared.global_box.lock().unwrap();
global_box.add_method(name, func_ast);
Ok(())
}
/// 値が真と評価されるかチェック
pub(super) fn is_truthy(&self, value: &Box<dyn NyashBox>) -> bool {
#[allow(unused_imports)]
use std::any::Any;
if let Some(bool_box) = value.as_any().downcast_ref::<BoolBox>() {
bool_box.value
} else if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
int_box.value != 0
} else if let Some(string_box) = value.as_any().downcast_ref::<StringBox>() {
!string_box.value.is_empty()
} else if value.as_any().downcast_ref::<VoidBox>().is_some() {
false
} else {
true // 他のBoxは真とみなす
}
}
/// 🌍 革命的変数取得テスト用GlobalBoxのフィールドから取得
pub fn get_variable(&self, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
self.resolve_variable(name)
}
}
// ===== Tests =====
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::NyashParser;
#[test]
fn test_simple_execution() {
let code = r#"
x = 42
print(x)
"#;
let ast = NyashParser::parse_from_string(code).unwrap();
let mut interpreter = NyashInterpreter::new();
let result = interpreter.execute(ast);
assert!(result.is_ok());
}
#[test]
fn test_arithmetic() {
let code = r#"
result = 10 + 32
"#;
let ast = NyashParser::parse_from_string(code).unwrap();
let mut interpreter = NyashInterpreter::new();
interpreter.execute(ast).unwrap();
// 🌍 革命的変数取得GlobalBoxから
let result = interpreter.get_variable("result").unwrap();
assert_eq!(result.to_string_box().value, "42");
}
#[test]
fn test_if_statement() {
let code = r#"
x = true
if x {
y = "success"
} else {
y = "failure"
}
"#;
let ast = NyashParser::parse_from_string(code).unwrap();
let mut interpreter = NyashInterpreter::new();
interpreter.execute(ast).unwrap();
// 🌍 革命的変数取得GlobalBoxから
let result = interpreter.get_variable("y").unwrap();
assert_eq!(result.to_string_box().value, "success");
}
#[test]
fn test_box_instance_creation() {
let code = r#"
box TestBox {
value
getValue() {
return this.value
}
setValue(newValue) {
this.value = newValue
}
}
obj = new TestBox()
obj.value = "test123"
result = obj.getValue()
"#;
let ast = NyashParser::parse_from_string(code).unwrap();
let mut interpreter = NyashInterpreter::new();
interpreter.execute(ast).unwrap();
// 🌍 革命的変数取得:インスタンス作成確認
let obj = interpreter.get_variable("obj").unwrap();
assert!(obj.as_any().downcast_ref::<InstanceBox>().is_some());
// 🌍 革命的変数取得:メソッド呼び出し結果確認
let result = interpreter.get_variable("result").unwrap();
assert_eq!(result.to_string_box().value, "test123");
}
}
// ===== 🔥 Static Box管理システム =====
impl NyashInterpreter {
/// Static Box定義を登録
pub fn register_static_box(&mut self, definition: StaticBoxDefinition) -> Result<(), RuntimeError> {
let mut definitions = self.shared.static_box_definitions.write()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire write lock for static box definitions".to_string()
})?;
definitions.insert(definition.name.clone(), definition);
Ok(())
}
/// Static Box宣言を登録AST処理から呼ばれる
pub fn register_static_box_declaration(
&mut self,
name: String,
fields: Vec<String>,
methods: HashMap<String, ASTNode>,
init_fields: Vec<String>,
static_init: Option<Vec<ASTNode>>,
extends: Option<String>,
implements: Vec<String>,
type_parameters: Vec<String>
) -> Result<(), RuntimeError> {
// 🌍 Static Box定義時にstatics名前空間を確実に作成
self.ensure_statics_namespace()?;
let definition = StaticBoxDefinition {
name: name.clone(),
fields,
methods,
init_fields,
static_init,
extends,
implements,
type_parameters,
initialization_state: StaticBoxState::NotInitialized,
};
eprintln!("🔥 Static Box '{}' definition registered in statics namespace", name);
self.register_static_box(definition)
}
/// Static Boxの初期化を実行遅延初期化
pub fn ensure_static_box_initialized(&mut self, name: &str) -> Result<(), RuntimeError> {
// 1. 定義を取得
let definition = {
let definitions = self.shared.static_box_definitions.read()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire read lock for static box definitions".to_string()
})?;
match definitions.get(name) {
Some(def) => def.clone(),
None => return Err(RuntimeError::UndefinedClass { name: name.to_string() }),
}
};
// 2. 初期化状態をチェック
if definition.initialization_state == StaticBoxState::Initialized {
return Ok(()); // 既に初期化済み
}
if definition.initialization_state == StaticBoxState::Initializing {
return Err(RuntimeError::RuntimeFailure {
message: format!("Circular dependency detected during initialization of static box '{}'", name)
});
}
// 3. 初期化開始をマーク
self.set_static_box_state(name, StaticBoxState::Initializing)?;
// 4. 「statics」名前空間をGlobalBoxに作成未存在の場合
self.ensure_statics_namespace()?;
// 5. シングルトンインスタンスを作成(メソッドも含む)
let singleton = InstanceBox::new(
format!("{}_singleton", name),
definition.init_fields.clone(),
definition.methods.clone(), // ★ メソッドを正しく設定
);
// 6. GlobalBox.staticsに登録
self.set_static_instance(name, singleton)?;
// 7. static初期化ブロックを実行me変数をバインドして
if let Some(ref init_statements) = definition.static_init {
// statics名前空間からシングルトンインスタンスを取得
let static_instance = {
let global_box = self.shared.global_box.lock().unwrap();
let statics_box = global_box.get_field("statics").unwrap();
let statics_instance = statics_box.as_any().downcast_ref::<InstanceBox>().unwrap();
statics_instance.get_field(name).unwrap()
};
// 🌍 this変数をバインドしてstatic初期化実行me構文のため
self.declare_local_variable("me", static_instance);
for stmt in init_statements {
self.execute_statement(stmt)?;
}
// 🌍 this変数をクリーンアップ
self.local_vars.remove("me");
}
// 8. 初期化完了をマーク
self.set_static_box_state(name, StaticBoxState::Initialized)?;
Ok(())
}
/// Static Box初期化状態を設定
fn set_static_box_state(&mut self, name: &str, state: StaticBoxState) -> Result<(), RuntimeError> {
let mut definitions = self.shared.static_box_definitions.write()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire write lock for static box definitions".to_string()
})?;
if let Some(definition) = definitions.get_mut(name) {
definition.initialization_state = state;
}
Ok(())
}
/// 「statics」名前空間をGlobalBoxに作成
fn ensure_statics_namespace(&mut self) -> Result<(), RuntimeError> {
let global_box = self.shared.global_box.lock()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire global box lock".to_string()
})?;
// 既に存在する場合はスキップ
if global_box.get_field("statics").is_some() {
eprintln!("🌍 statics namespace already exists - skipping creation");
return Ok(());
}
// 「statics」用のInstanceBoxを作成
let statics_box = InstanceBox::new(
"statics".to_string(),
vec![],
HashMap::new(),
);
// GlobalBoxのfieldsに直接挿入
{
let mut fields = global_box.fields.lock().unwrap();
fields.insert("statics".to_string(), Box::new(statics_box));
}
eprintln!("🌍 statics namespace created in GlobalBox successfully");
Ok(())
}
/// Static Boxシングルトンインスタンスを設定
fn set_static_instance(&mut self, name: &str, instance: InstanceBox) -> Result<(), RuntimeError> {
let global_box = self.shared.global_box.lock()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire global box lock".to_string()
})?;
// statics名前空間を取得
let statics_box = global_box.get_field("statics")
.ok_or(RuntimeError::TypeError {
message: "statics namespace not found in GlobalBox".to_string()
})?;
let statics_instance = statics_box.as_any()
.downcast_ref::<InstanceBox>()
.ok_or(RuntimeError::TypeError {
message: "statics field is not an InstanceBox".to_string()
})?;
// statics InstanceBoxのfieldsに直接挿入動的フィールド追加
{
let mut fields = statics_instance.fields.lock().unwrap();
fields.insert(name.to_string(), Box::new(instance));
}
eprintln!("🔥 Static box '{}' instance registered in statics namespace", name);
Ok(())
}
/// 🔥 Static Boxかどうかをチェック
pub(super) fn is_static_box(&self, name: &str) -> bool {
if let Ok(definitions) = self.shared.static_box_definitions.read() {
definitions.contains_key(name)
} else {
false
}
}
}

View File

@ -0,0 +1,617 @@
/*!
* Expression Processing Module
*
* Extracted from core.rs lines 408-787 (~380 lines)
* Handles expression evaluation, binary operations, method calls, and field access
* Core philosophy: "Everything is Box" with clean expression evaluation
*/
use super::*;
use crate::ast::UnaryOperator;
// TODO: Fix NullBox import issue later
// use crate::NullBox;
impl NyashInterpreter {
/// 式を実行 - Expression evaluation engine
pub(super) fn execute_expression(&mut self, expression: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
match expression {
ASTNode::Literal { value, .. } => {
Ok(value.to_nyash_box())
}
ASTNode::Variable { name, .. } => {
// 🌍 革命的変数解決local変数 → GlobalBoxフィールド → エラー
self.resolve_variable(name)
.map_err(|_| RuntimeError::UndefinedVariableAt {
name: name.clone(),
span: expression.span()
})
}
ASTNode::BinaryOp { operator, left, right, .. } => {
self.execute_binary_op(operator, left, right)
}
ASTNode::UnaryOp { operator, operand, .. } => {
self.execute_unary_op(operator, operand)
}
ASTNode::AwaitExpression { expression, .. } => {
self.execute_await(expression)
}
ASTNode::MethodCall { object, method, arguments, .. } => {
self.execute_method_call(object, method, arguments)
}
ASTNode::FieldAccess { object, field, .. } => {
self.execute_field_access(object, field)
}
ASTNode::New { class, arguments, type_arguments, .. } => {
self.execute_new(class, arguments, type_arguments)
}
ASTNode::This { .. } => {
// 🌍 革命的this解決local変数から取得
self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' is only available inside methods".to_string(),
})
}
ASTNode::Me { .. } => {
// 🌍 革命的me解決local変数から取得thisと同じ
self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'me' is only available inside methods".to_string(),
})
}
ASTNode::ThisField { field, .. } => {
// 🌍 革命的this.fieldアクセスlocal変数から取得
let this_value = self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' is not bound in the current context".to_string(),
})?;
if let Some(instance) = this_value.as_any().downcast_ref::<InstanceBox>() {
instance.get_field(field)
.ok_or_else(|| RuntimeError::InvalidOperation {
message: format!("Field '{}' not found on this", field)
})
} else {
Err(RuntimeError::TypeError {
message: "'this' is not an instance".to_string(),
})
}
}
ASTNode::MeField { field, .. } => {
// 🌍 革命的me.fieldアクセスlocal変数から取得
let me_value = self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' is not bound in the current context".to_string(),
})?;
if let Some(instance) = me_value.as_any().downcast_ref::<InstanceBox>() {
instance.get_field(field)
.ok_or_else(|| RuntimeError::InvalidOperation {
message: format!("Field '{}' not found on me", field)
})
} else {
Err(RuntimeError::TypeError {
message: "'this' is not an instance".to_string(),
})
}
}
ASTNode::FunctionCall { name, arguments, .. } => {
self.execute_function_call(name, arguments)
}
ASTNode::Arrow { sender, receiver, .. } => {
self.execute_arrow(sender, receiver)
}
ASTNode::Include { filename, .. } => {
self.execute_include(filename)?;
Ok(Box::new(VoidBox::new()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Cannot execute {:?} as expression", expression.node_type()),
}),
}
}
/// 二項演算を実行 - Binary operation processing
pub(super) fn execute_binary_op(&mut self, op: &BinaryOperator, left: &ASTNode, right: &ASTNode)
-> Result<Box<dyn NyashBox>, RuntimeError> {
let left_val = self.execute_expression(left)?;
let right_val = self.execute_expression(right)?;
match op {
BinaryOperator::Add => {
let add_box = AddBox::new(left_val, right_val);
Ok(add_box.execute())
}
BinaryOperator::Equal => {
let result = left_val.equals(right_val.as_ref());
Ok(Box::new(result))
}
BinaryOperator::NotEqual => {
let result = left_val.equals(right_val.as_ref());
Ok(Box::new(BoolBox::new(!result.value)))
}
BinaryOperator::And => {
let left_bool = self.is_truthy(&left_val);
if !left_bool {
Ok(Box::new(BoolBox::new(false)))
} else {
let right_bool = self.is_truthy(&right_val);
Ok(Box::new(BoolBox::new(right_bool)))
}
}
BinaryOperator::Or => {
let left_bool = self.is_truthy(&left_val);
if left_bool {
Ok(Box::new(BoolBox::new(true)))
} else {
let right_bool = self.is_truthy(&right_val);
Ok(Box::new(BoolBox::new(right_bool)))
}
}
BinaryOperator::Subtract => {
let sub_box = SubtractBox::new(left_val, right_val);
Ok(sub_box.execute())
}
BinaryOperator::Multiply => {
let mul_box = MultiplyBox::new(left_val, right_val);
Ok(mul_box.execute())
}
BinaryOperator::Divide => {
let div_box = DivideBox::new(left_val, right_val);
Ok(div_box.execute())
}
BinaryOperator::Less => {
let result = CompareBox::less(left_val.as_ref(), right_val.as_ref());
Ok(Box::new(result))
}
BinaryOperator::Greater => {
let result = CompareBox::greater(left_val.as_ref(), right_val.as_ref());
Ok(Box::new(result))
}
BinaryOperator::LessEqual => {
let result = CompareBox::less_equal(left_val.as_ref(), right_val.as_ref());
Ok(Box::new(result))
}
BinaryOperator::GreaterEqual => {
let result = CompareBox::greater_equal(left_val.as_ref(), right_val.as_ref());
Ok(Box::new(result))
}
}
}
/// 単項演算を実行 - Unary operation processing
pub(super) fn execute_unary_op(&mut self, operator: &UnaryOperator, operand: &ASTNode)
-> Result<Box<dyn NyashBox>, RuntimeError> {
let operand_val = self.execute_expression(operand)?;
match operator {
UnaryOperator::Minus => {
// 数値の符号反転
if let Some(int_box) = operand_val.as_any().downcast_ref::<IntegerBox>() {
Ok(Box::new(IntegerBox::new(-int_box.value)))
} else if let Some(float_box) = operand_val.as_any().downcast_ref::<FloatBox>() {
Ok(Box::new(FloatBox::new(-float_box.value)))
} else {
Err(RuntimeError::TypeError {
message: "Unary minus can only be applied to Integer or Float".to_string(),
})
}
}
UnaryOperator::Not => {
// 論理否定
if let Some(bool_box) = operand_val.as_any().downcast_ref::<BoolBox>() {
Ok(Box::new(BoolBox::new(!bool_box.value)))
} else {
// どんな値でもtruthyness判定してnot演算を適用
let is_truthy = self.is_truthy(&operand_val);
Ok(Box::new(BoolBox::new(!is_truthy)))
}
}
}
}
/// メソッド呼び出しを実行 - Method call processing
pub(super) fn execute_method_call(&mut self, object: &ASTNode, method: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 🔥 static関数のチェック
if let ASTNode::Variable { name, .. } = object {
// static関数が存在するかチェック
let static_func = {
let static_funcs = self.shared.static_functions.read().unwrap();
if let Some(box_statics) = static_funcs.get(name) {
if let Some(func) = box_statics.get(method) {
Some(func.clone())
} else {
None
}
} else {
None
}
};
if let Some(static_func) = static_func {
// static関数を実行
if let ASTNode::FunctionDeclaration { params, body, .. } = static_func {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// パラメータ数チェック
if arg_values.len() != params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!("Static method {}.{} expects {} arguments, got {}",
name, method, params.len(), arg_values.len()),
});
}
// 🌍 local変数スタックを保存・クリアstatic関数呼び出し開始
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// 📤 outbox変数スタックも保存・クリアstatic関数専用
let saved_outbox = self.save_outbox_vars();
self.outbox_vars.clear();
// 引数をlocal変数として設定
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_box());
}
// static関数の本体を実行
let mut result = Box::new(VoidBox::new()) as Box<dyn NyashBox>;
for statement in &body {
result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(return_val) = &self.control_flow {
result = return_val.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
// local変数スタックを復元
self.restore_local_vars(saved_locals);
// outbox変数スタックを復元
self.restore_outbox_vars(saved_outbox);
return Ok(result);
}
}
}
// オブジェクトを評価(通常のメソッド呼び出し)
let obj_value = self.execute_expression(object)?;
// StringBox method calls
if let Some(string_box) = obj_value.as_any().downcast_ref::<StringBox>() {
return self.execute_string_method(string_box, method, arguments);
}
// ArrayBox method calls
if let Some(array_box) = obj_value.as_any().downcast_ref::<ArrayBox>() {
return self.execute_array_method(array_box, method, arguments);
}
// FileBox method calls
if let Some(file_box) = obj_value.as_any().downcast_ref::<FileBox>() {
return self.execute_file_method(file_box, method, arguments);
}
// ResultBox method calls
if let Some(result_box) = obj_value.as_any().downcast_ref::<ResultBox>() {
return self.execute_result_method(result_box, method, arguments);
}
// FutureBox method calls
if let Some(future_box) = obj_value.as_any().downcast_ref::<FutureBox>() {
return self.execute_future_method(future_box, method, arguments);
}
// ChannelBox method calls
if let Some(channel_box) = obj_value.as_any().downcast_ref::<ChannelBox>() {
return self.execute_channel_method(channel_box, method, arguments);
}
// MathBox method calls
if let Some(math_box) = obj_value.as_any().downcast_ref::<MathBox>() {
return self.execute_math_method(math_box, method, arguments);
}
// NullBox method calls
if let Some(null_box) = obj_value.as_any().downcast_ref::<crate::boxes::null_box::NullBox>() {
return self.execute_null_method(null_box, method, arguments);
}
// TimeBox method calls
if let Some(time_box) = obj_value.as_any().downcast_ref::<TimeBox>() {
return self.execute_time_method(time_box, method, arguments);
}
// DateTimeBox method calls
if let Some(datetime_box) = obj_value.as_any().downcast_ref::<DateTimeBox>() {
return self.execute_datetime_method(datetime_box, method, arguments);
}
// TimerBox method calls
if let Some(timer_box) = obj_value.as_any().downcast_ref::<TimerBox>() {
return self.execute_timer_method(timer_box, method, arguments);
}
// MapBox method calls
if let Some(map_box) = obj_value.as_any().downcast_ref::<MapBox>() {
return self.execute_map_method(map_box, method, arguments);
}
// RandomBox method calls
if let Some(random_box) = obj_value.as_any().downcast_ref::<RandomBox>() {
return self.execute_random_method(random_box, method, arguments);
}
// SoundBox method calls
if let Some(sound_box) = obj_value.as_any().downcast_ref::<SoundBox>() {
return self.execute_sound_method(sound_box, method, arguments);
}
// DebugBox method calls
if let Some(debug_box) = obj_value.as_any().downcast_ref::<DebugBox>() {
return self.execute_debug_method(debug_box, method, arguments);
}
// ConsoleBox method calls
if let Some(console_box) = obj_value.as_any().downcast_ref::<crate::boxes::console_box::ConsoleBox>() {
return self.execute_console_method(console_box, method, arguments);
}
// WebDisplayBox method calls (WASM環境のみ)
#[cfg(target_arch = "wasm32")]
if let Some(web_display_box) = obj_value.as_any().downcast_ref::<crate::boxes::WebDisplayBox>() {
return self.execute_web_display_method(web_display_box, method, arguments);
}
// WebConsoleBox method calls (WASM環境のみ)
#[cfg(target_arch = "wasm32")]
if let Some(web_console_box) = obj_value.as_any().downcast_ref::<crate::boxes::WebConsoleBox>() {
return self.execute_web_console_method(web_console_box, method, arguments);
}
// WebCanvasBox method calls (WASM環境のみ)
#[cfg(target_arch = "wasm32")]
if let Some(web_canvas_box) = obj_value.as_any().downcast_ref::<crate::boxes::WebCanvasBox>() {
return self.execute_web_canvas_method(web_canvas_box, method, arguments);
}
// MethodBox method calls
if let Some(method_box) = obj_value.as_any().downcast_ref::<crate::method_box::MethodBox>() {
return self.execute_method_box_method(method_box, method, arguments);
}
// IntegerBox method calls
if let Some(integer_box) = obj_value.as_any().downcast_ref::<IntegerBox>() {
return self.execute_integer_method(integer_box, method, arguments);
}
// FloatBox method calls (将来的に追加予定)
// RangeBox method calls (将来的に追加予定)
// InstanceBox method calls
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
// fini()は特別処理
if method == "fini" {
// 既に解放済みの場合は何もしない二重fini()対策)
if instance.is_finalized() {
return Ok(Box::new(VoidBox::new()));
}
// まず、Box内で定義されたfini()メソッドがあれば実行
if let Some(fini_method) = instance.get_method("fini") {
if let ASTNode::FunctionDeclaration { body, .. } = fini_method.clone() {
// 🌍 革命的メソッド実行local変数スタックを使用
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// thisをlocal変数として設定
self.declare_local_variable("me", obj_value.clone_box());
// fini()メソッドの本体を実行
let mut _result = Box::new(VoidBox::new()) as Box<dyn NyashBox>;
for statement in &body {
_result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(_) = &self.control_flow {
self.control_flow = super::ControlFlow::None;
break;
}
}
// local変数スタックを復元
self.restore_local_vars(saved_locals);
}
}
// インスタンスの内部的な解放処理
instance.fini().map_err(|e| RuntimeError::InvalidOperation {
message: e,
})?;
finalization::mark_as_finalized(instance.box_id());
return Ok(Box::new(VoidBox::new()));
}
// メソッドを取得
let method_ast = instance.get_method(method)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Method '{}' not found in {}", method, instance.class_name),
})?
.clone();
// メソッドが関数宣言の形式であることを確認
if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// パラメータ数チェック
if arg_values.len() != params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!("Method {} expects {} arguments, got {}",
method, params.len(), arg_values.len()),
});
}
// 🌍 革命的メソッド実行local変数スタックを使用
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// thisをlocal変数として設定
self.declare_local_variable("me", obj_value.clone_box());
// パラメータをlocal変数として設定
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_box());
}
// メソッド本体を実行
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
for statement in &body {
result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(return_val) = &self.control_flow {
result = return_val.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
// local変数スタックを復元
self.restore_local_vars(saved_locals);
Ok(result)
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Method '{}' is not a valid function declaration", method),
})
}
} else {
Err(RuntimeError::TypeError {
message: format!("Cannot call method '{}' on non-instance type", method),
})
}
}
/// フィールドアクセスを実行 - Field access processing
pub(super) fn execute_field_access(&mut self, object: &ASTNode, field: &str)
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 🔥 Static Boxアクセスチェック
if let ASTNode::Variable { name, .. } = object {
// Static boxの可能性をチェック
if self.is_static_box(name) {
return self.execute_static_field_access(name, field);
}
}
// オブジェクトを評価(通常のフィールドアクセス)
let obj_value = self.execute_expression(object)?;
// InstanceBoxにキャスト
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
// フィールドの値を取得
instance.get_field(field)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Field '{}' not found in {}", field, instance.class_name),
})
} else {
Err(RuntimeError::TypeError {
message: format!("Cannot access field '{}' on non-instance type. Type: {}", field, obj_value.type_name()),
})
}
}
/// 🔥 Static Box名前空間のフィールドアクセス
fn execute_static_field_access(&mut self, static_box_name: &str, field: &str)
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 1. Static Boxの初期化を確実に実行
self.ensure_static_box_initialized(static_box_name)?;
// 2. GlobalBox.statics.{static_box_name} からインスタンスを取得
let global_box = self.shared.global_box.lock()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire global box lock".to_string()
})?;
let statics_box = global_box.get_field("statics")
.ok_or(RuntimeError::RuntimeFailure {
message: "statics namespace not found in GlobalBox".to_string()
})?;
let statics_instance = statics_box.as_any()
.downcast_ref::<InstanceBox>()
.ok_or(RuntimeError::TypeError {
message: "statics field is not an InstanceBox".to_string()
})?;
let static_box_instance = statics_instance.get_field(static_box_name)
.ok_or(RuntimeError::RuntimeFailure {
message: format!("Static box '{}' instance not found in statics namespace", static_box_name)
})?;
let instance = static_box_instance.as_any()
.downcast_ref::<InstanceBox>()
.ok_or(RuntimeError::TypeError {
message: format!("Static box '{}' is not an InstanceBox", static_box_name)
})?;
// 3. フィールドアクセス
instance.get_field(field)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Field '{}' not found in static box '{}'", field, static_box_name),
})
}
/// await式を実行 - Execute await expression
pub(super) fn execute_await(&mut self, expression: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
let value = self.execute_expression(expression)?;
// FutureBoxなら待機して結果を取得
if let Some(future) = value.as_any().downcast_ref::<FutureBox>() {
future.wait_and_get()
.map_err(|msg| RuntimeError::InvalidOperation { message: msg })
} else {
// FutureBoxでなければそのまま返す
Ok(value)
}
}
}

View File

@ -0,0 +1,96 @@
/*!
* Function Processing Module
*
* Extracted from core.rs - function call and definition handling
* Handles function declarations, calls, and function-related operations
* Core philosophy: "Everything is Box" with structured function processing
*/
use super::*;
impl NyashInterpreter {
/// 関数呼び出しを実行 - 🌍 革命的実装GlobalBoxのメソッド呼び出し
pub(super) fn execute_function_call(&mut self, name: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// コンストラクタ内での親コンストラクタ呼び出しチェック
if let Some(context) = self.current_constructor_context.clone() {
if let Some(parent_class) = context.parent_class {
if name == parent_class {
// 親コンストラクタ呼び出し
return self.execute_parent_constructor(&parent_class, arguments);
}
}
}
// 🌍 GlobalBoxのメソッドとして実行
let global_box = self.shared.global_box.lock().unwrap();
let method_ast = global_box.get_method(name)
.ok_or(RuntimeError::UndefinedFunction { name: name.to_string() })?
.clone();
drop(global_box);
// メソッド呼び出しとして実行GlobalBoxインスタンス上で
if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// パラメータ数チェック
if arg_values.len() != params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!("Function {} expects {} arguments, got {}",
name, params.len(), arg_values.len()),
});
}
// 🌍 local変数スタックを保存・クリア関数呼び出し開始
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// パラメータをlocal変数として設定
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_box());
}
// 関数本体を実行
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
for statement in &body {
result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(return_val) = &self.control_flow {
result = return_val.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
// 🌍 local変数スタックを復元関数呼び出し終了
self.restore_local_vars(saved_locals);
Ok(result)
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Function '{}' is not a valid function declaration", name),
})
}
}
/// 関数宣言を登録 - 🌍 革命的実装GlobalBoxのメソッドとして登録
pub(super) fn register_function_declaration(&mut self, name: String, params: Vec<String>, body: Vec<ASTNode>) {
// 🌍 GlobalBoxのメソッドとして登録
let func_ast = ASTNode::FunctionDeclaration {
name: name.clone(),
params,
body,
is_static: false, // 通常の関数は静的でない
span: crate::ast::Span::unknown(), // デフォルトspan
};
self.register_global_function(name, func_ast).unwrap_or_else(|err| {
eprintln!("Warning: Failed to register global function: {}", err);
});
}
}

114
src/interpreter/io.rs Normal file
View File

@ -0,0 +1,114 @@
/*!
* I/O Processing Module
*
* Extracted from core.rs - file operations and communication
* Handles include system, arrow operators, and I/O-related operations
* Core philosophy: "Everything is Box" with secure I/O processing
*/
use super::*;
use crate::parser::NyashParser;
impl NyashInterpreter {
/// include文を実行ファイル読み込み・パース・実行 - File inclusion system
pub(super) fn execute_include(&mut self, filename: &str) -> Result<(), RuntimeError> {
// パス正規化(簡易版)
let canonical_path = if filename.starts_with("./") || filename.starts_with("../") {
filename.to_string()
} else {
format!("./{}", filename)
};
// 重複読み込みチェック
if self.shared.included_files.lock().unwrap().contains(&canonical_path) {
return Ok(()); // 既に読み込み済み
}
// ファイル読み込み
let content = std::fs::read_to_string(&canonical_path)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("Failed to read file '{}': {}", filename, e),
})?;
// パース
let ast = NyashParser::parse_from_string(&content)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("Parse error in '{}': {:?}", filename, e),
})?;
// 重複防止リストに追加
self.shared.included_files.lock().unwrap().insert(canonical_path);
// 現在の環境で実行
self.execute(ast)?;
Ok(())
}
/// Arrow演算子を実行: sender >> receiver - Channel communication
pub(super) fn execute_arrow(&mut self, sender: &ASTNode, receiver: &ASTNode)
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 送信者を評価
let sender_value = self.execute_expression(sender)?;
// 受信者を評価
let receiver_str = match receiver {
ASTNode::Variable { name, .. } => name.clone(),
ASTNode::Literal { value, .. } => {
// "*" のようなリテラルの場合
value.to_string()
}
_ => {
// その他の式の場合は評価して文字列化
let receiver_value = self.execute_expression(receiver)?;
receiver_value.to_string_box().value
}
};
// 送信者の名前を取得
let sender_name = sender_value.to_string_box().value;
// ChannelBoxを作成して返す
let channel_box = Box::new(ChannelBox::new(&sender_name, &receiver_str)) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
Ok(channel_box)
}
/// nowait文を実行 - 非同期実行(真の非同期実装) - Async execution
pub(super) fn execute_nowait(&mut self, variable: &str, expression: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
use crate::box_trait::FutureBox;
use std::thread;
// FutureBoxを作成
let future_box = FutureBox::new();
let future_box_clone = future_box.clone();
// 式をクローンして別スレッドで実行
let expr_clone = expression.clone();
let shared_state = self.shared.clone();
// 別スレッドで非同期実行
thread::spawn(move || {
// 新しいインタープリタインスタンスを作成SharedStateを使用
let mut async_interpreter = NyashInterpreter::with_shared(shared_state);
// 式を評価
match async_interpreter.execute_expression(&expr_clone) {
Ok(result) => {
future_box_clone.set_result(result);
}
Err(e) => {
// エラーをErrorBoxとして設定
let error_box = Box::new(ErrorBox::new("RuntimeError", &format!("{:?}", e)));
future_box_clone.set_result(error_box);
}
}
});
// FutureBoxを変数に保存
let future_box_instance = Box::new(future_box) as Box<dyn NyashBox>;
self.set_variable(variable, future_box_instance)?;
Ok(Box::new(VoidBox::new()))
}
}

98
src/interpreter/mod.rs Normal file
View File

@ -0,0 +1,98 @@
/*!
* Nyash Interpreter - Modular Rust Implementation
*
* Refactored from massive 2,633-line interpreter.rs into logical modules
* Everything is Box philosophy with clean separation of concerns
*/
// Import all necessary dependencies
use crate::ast::{ASTNode, BinaryOperator, CatchClause};
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox, AddBox, SubtractBox, MultiplyBox, DivideBox, CompareBox, ArrayBox, FileBox, ResultBox, ErrorBox, FutureBox};
use crate::instance::InstanceBox;
use crate::channel_box::{ChannelBox, MessageBox};
use crate::boxes::math_box::{MathBox, FloatBox, RangeBox};
use crate::boxes::time_box::{TimeBox, DateTimeBox, TimerBox};
use crate::boxes::map_box::MapBox;
use crate::boxes::random_box::RandomBox;
use crate::boxes::sound_box::SoundBox;
use crate::boxes::debug_box::DebugBox;
use crate::method_box::MethodBox;
use crate::finalization;
use crate::exception_box;
use std::collections::HashMap;
// Module declarations
mod box_methods;
mod core;
mod expressions;
mod statements;
mod functions;
mod objects;
mod io;
// Main interpreter implementation - will be moved from interpreter.rs
/// 実行制御フロー
#[derive(Debug)]
pub enum ControlFlow {
None,
Break,
Return(Box<dyn NyashBox>),
Throw(Box<dyn NyashBox>),
}
/// コンストラクタ実行コンテキスト
#[derive(Debug, Clone)]
pub struct ConstructorContext {
pub class_name: String,
pub parent_class: Option<String>,
}
/// Box宣言を保持する構造体
#[derive(Debug, Clone)]
pub struct BoxDeclaration {
pub name: String,
pub fields: Vec<String>,
pub methods: HashMap<String, ASTNode>,
pub constructors: HashMap<String, ASTNode>,
pub init_fields: Vec<String>,
pub is_interface: bool,
pub extends: Option<String>,
pub implements: Vec<String>,
pub type_parameters: Vec<String>, // 🔥 ジェネリクス型パラメータ
}
/// 🔥 Static Box定義を保持する構造体
#[derive(Debug, Clone)]
pub struct StaticBoxDefinition {
pub name: String,
pub fields: Vec<String>,
pub methods: HashMap<String, ASTNode>,
pub init_fields: Vec<String>,
pub static_init: Option<Vec<ASTNode>>, // static { } ブロック
pub extends: Option<String>,
pub implements: Vec<String>,
pub type_parameters: Vec<String>,
/// 初期化状態
pub initialization_state: StaticBoxState,
}
/// 🔥 Static Box初期化状態
#[derive(Debug, Clone, PartialEq)]
pub enum StaticBoxState {
NotInitialized, // 未初期化
Initializing, // 初期化中(循環参照検出用)
Initialized, // 初期化完了
}
/// 関数宣言を保持する構造体
#[derive(Debug, Clone)]
pub struct FunctionDeclaration {
pub name: String,
pub params: Vec<String>,
pub body: Vec<ASTNode>,
}
// Re-export core interpreter types
pub use core::*;

746
src/interpreter/objects.rs Normal file
View File

@ -0,0 +1,746 @@
/*!
* Object Processing Module
*
* Extracted from core.rs - object creation, construction, and inheritance
* Handles Box declarations, instantiation, constructors, and inheritance system
* Core philosophy: "Everything is Box" with complete OOP support
*/
use super::*;
use crate::boxes::null_box::NullBox;
use crate::boxes::console_box::ConsoleBox;
impl NyashInterpreter {
/// new式を実行 - Object creation engine
pub(super) fn execute_new(&mut self, class: &str, arguments: &[ASTNode], type_arguments: &[String])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 組み込みBox型のチェック
match class {
"ArrayBox" => {
// ArrayBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("ArrayBox constructor expects 0 arguments, got {}", arguments.len()),
});
}
let array_box = Box::new(ArrayBox::new()) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(array_box);
}
"FileBox" => {
// FileBoxは引数1個ファイルパスで作成
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("FileBox constructor expects 1 argument, got {}", arguments.len()),
});
}
let path_value = self.execute_expression(&arguments[0])?;
if let Some(path_str) = path_value.as_any().downcast_ref::<StringBox>() {
let file_box = Box::new(FileBox::new(&path_str.value)) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(file_box);
} else {
return Err(RuntimeError::TypeError {
message: "FileBox constructor requires string path argument".to_string(),
});
}
}
"ResultBox" => {
// ResultBoxは引数1個成功値で作成
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("ResultBox constructor expects 1 argument, got {}", arguments.len()),
});
}
let value = self.execute_expression(&arguments[0])?;
let result_box = Box::new(ResultBox::new_success(value)) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(result_box);
}
"ErrorBox" => {
// ErrorBoxは引数2個エラータイプ、メッセージで作成
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("ErrorBox constructor expects 2 arguments, got {}", arguments.len()),
});
}
let error_type_value = self.execute_expression(&arguments[0])?;
let message_value = self.execute_expression(&arguments[1])?;
if let (Some(error_type_str), Some(message_str)) = (
error_type_value.as_any().downcast_ref::<StringBox>(),
message_value.as_any().downcast_ref::<StringBox>()
) {
let error_box = Box::new(ErrorBox::new(&error_type_str.value, &message_str.value)) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(error_box);
} else {
return Err(RuntimeError::TypeError {
message: "ErrorBox constructor requires two string arguments".to_string(),
});
}
}
"MathBox" => {
// MathBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("MathBox constructor expects 0 arguments, got {}", arguments.len()),
});
}
let math_box = Box::new(MathBox::new()) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(math_box);
}
"NullBox" => {
// NullBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("NullBox constructor expects 0 arguments, got {}", arguments.len()),
});
}
let null_box = Box::new(NullBox::new()) as Box<dyn NyashBox>;
return Ok(null_box);
}
"ConsoleBox" => {
// ConsoleBoxは引数なしで作成ブラウザconsole連携用
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("ConsoleBox constructor expects 0 arguments, got {}", arguments.len()),
});
}
let console_box = Box::new(ConsoleBox::new()) as Box<dyn NyashBox>;
return Ok(console_box);
}
#[cfg(target_arch = "wasm32")]
"WebDisplayBox" => {
// WebDisplayBoxは引数1個要素IDで作成ブラウザHTML操作用
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("WebDisplayBox constructor expects 1 argument (element_id), got {}", arguments.len()),
});
}
let element_id_value = self.execute_expression(&arguments[0])?;
if let Some(id_str) = element_id_value.as_any().downcast_ref::<StringBox>() {
let web_display_box = Box::new(crate::boxes::WebDisplayBox::new(id_str.value.clone())) as Box<dyn NyashBox>;
return Ok(web_display_box);
} else {
return Err(RuntimeError::TypeError {
message: "WebDisplayBox constructor requires string element_id argument".to_string(),
});
}
}
#[cfg(target_arch = "wasm32")]
"WebConsoleBox" => {
// WebConsoleBoxは引数1個要素IDで作成ブラウザコンソール風出力用
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("WebConsoleBox constructor expects 1 argument (element_id), got {}", arguments.len()),
});
}
let element_id_value = self.execute_expression(&arguments[0])?;
if let Some(id_str) = element_id_value.as_any().downcast_ref::<StringBox>() {
let web_console_box = Box::new(crate::boxes::WebConsoleBox::new(id_str.value.clone())) as Box<dyn NyashBox>;
return Ok(web_console_box);
} else {
return Err(RuntimeError::TypeError {
message: "WebConsoleBox constructor requires string element_id argument".to_string(),
});
}
}
#[cfg(target_arch = "wasm32")]
"WebCanvasBox" => {
// WebCanvasBoxは引数3個canvas ID、幅、高さで作成
if arguments.len() != 3 {
return Err(RuntimeError::InvalidOperation {
message: format!("WebCanvasBox constructor expects 3 arguments (canvas_id, width, height), got {}", arguments.len()),
});
}
// Canvas ID
let canvas_id_value = self.execute_expression(&arguments[0])?;
let canvas_id = if let Some(id_str) = canvas_id_value.as_any().downcast_ref::<StringBox>() {
id_str.value.clone()
} else {
return Err(RuntimeError::TypeError {
message: "WebCanvasBox constructor requires string canvas_id as first argument".to_string(),
});
};
// Width
let width_value = self.execute_expression(&arguments[1])?;
let width = if let Some(int_box) = width_value.as_any().downcast_ref::<IntegerBox>() {
int_box.value as u32
} else {
return Err(RuntimeError::TypeError {
message: "WebCanvasBox constructor requires integer width as second argument".to_string(),
});
};
// Height
let height_value = self.execute_expression(&arguments[2])?;
let height = if let Some(int_box) = height_value.as_any().downcast_ref::<IntegerBox>() {
int_box.value as u32
} else {
return Err(RuntimeError::TypeError {
message: "WebCanvasBox constructor requires integer height as third argument".to_string(),
});
};
let web_canvas_box = Box::new(crate::boxes::WebCanvasBox::new(canvas_id, width, height)) as Box<dyn NyashBox>;
return Ok(web_canvas_box);
}
"FloatBox" => {
// FloatBoxは引数1個数値で作成
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("FloatBox constructor expects 1 argument, got {}", arguments.len()),
});
}
let value = self.execute_expression(&arguments[0])?;
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
let float_box = Box::new(FloatBox::new(int_box.value as f64)) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(float_box);
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
let new_float_box = Box::new(FloatBox::new(float_box.value)) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(new_float_box);
} else {
return Err(RuntimeError::TypeError {
message: "FloatBox constructor requires numeric argument".to_string(),
});
}
}
"RangeBox" => {
// RangeBoxは引数2-3個start, end, [step])で作成
if arguments.len() < 2 || arguments.len() > 3 {
return Err(RuntimeError::InvalidOperation {
message: format!("RangeBox constructor expects 2-3 arguments, got {}", arguments.len()),
});
}
let start_value = self.execute_expression(&arguments[0])?;
let end_value = self.execute_expression(&arguments[1])?;
let step_value = if arguments.len() == 3 {
self.execute_expression(&arguments[2])?
} else {
Box::new(IntegerBox::new(1))
};
if let (Some(start_int), Some(end_int), Some(step_int)) = (
start_value.as_any().downcast_ref::<IntegerBox>(),
end_value.as_any().downcast_ref::<IntegerBox>(),
step_value.as_any().downcast_ref::<IntegerBox>()
) {
let range_box = Box::new(RangeBox::new(start_int.value, end_int.value, step_int.value)) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(range_box);
} else {
return Err(RuntimeError::TypeError {
message: "RangeBox constructor requires integer arguments".to_string(),
});
}
}
"TimeBox" => {
// TimeBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("TimeBox constructor expects 0 arguments, got {}", arguments.len()),
});
}
let time_box = Box::new(TimeBox::new()) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(time_box);
}
"DateTimeBox" => {
// DateTimeBoxは引数なしで現在時刻、または引数1個でタイムスタンプ
match arguments.len() {
0 => {
let datetime_box = Box::new(DateTimeBox::now()) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(datetime_box);
}
1 => {
let timestamp_value = self.execute_expression(&arguments[0])?;
if let Some(int_box) = timestamp_value.as_any().downcast_ref::<IntegerBox>() {
let datetime_box = Box::new(DateTimeBox::from_timestamp(int_box.value)) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(datetime_box);
} else {
return Err(RuntimeError::TypeError {
message: "DateTimeBox constructor requires integer timestamp".to_string(),
});
}
}
_ => {
return Err(RuntimeError::InvalidOperation {
message: format!("DateTimeBox constructor expects 0-1 arguments, got {}", arguments.len()),
});
}
}
}
"TimerBox" => {
// TimerBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("TimerBox constructor expects 0 arguments, got {}", arguments.len()),
});
}
let timer_box = Box::new(TimerBox::new()) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(timer_box);
}
"MapBox" => {
// MapBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("MapBox constructor expects 0 arguments, got {}", arguments.len()),
});
}
let map_box = Box::new(MapBox::new()) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(map_box);
}
"RandomBox" => {
// RandomBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("RandomBox constructor expects 0 arguments, got {}", arguments.len()),
});
}
let random_box = Box::new(RandomBox::new()) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(random_box);
}
"SoundBox" => {
// SoundBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("SoundBox constructor expects 0 arguments, got {}", arguments.len()),
});
}
let sound_box = Box::new(SoundBox::new()) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(sound_box);
}
"DebugBox" => {
// DebugBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("DebugBox constructor expects 0 arguments, got {}", arguments.len()),
});
}
let debug_box = Box::new(DebugBox::new()) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
return Ok(debug_box);
}
"MethodBox" => {
// MethodBoxは引数2個インスタンス、メソッド名で作成
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("MethodBox constructor expects 2 arguments (instance, method_name), got {}", arguments.len()),
});
}
// インスタンスを評価
let instance = self.execute_expression(&arguments[0])?;
// メソッド名を評価
let method_name_value = self.execute_expression(&arguments[1])?;
if let Some(method_name_str) = method_name_value.as_any().downcast_ref::<StringBox>() {
let method_box = Box::new(MethodBox::new(instance, method_name_str.value.clone())) as Box<dyn NyashBox>;
return Ok(method_box);
} else {
return Err(RuntimeError::TypeError {
message: "MethodBox constructor requires string method name as second argument".to_string(),
});
}
}
_ => {}
}
// 🔥 Static Boxインスタンス化禁止チェック
if self.is_static_box(class) {
return Err(RuntimeError::InvalidOperation {
message: format!("Cannot instantiate static box '{}'. Static boxes cannot be instantiated.", class),
});
}
// ユーザー定義Box宣言を探す
let box_decl = {
let box_decls = self.shared.box_declarations.read().unwrap();
box_decls.get(class)
.ok_or(RuntimeError::UndefinedClass { name: class.to_string() })?
.clone()
};
// 🔥 ジェネリクス型引数の検証
if !box_decl.type_parameters.is_empty() || !type_arguments.is_empty() {
self.validate_generic_arguments(&box_decl, type_arguments)?;
}
// インターフェースはインスタンス化できない
if box_decl.is_interface {
return Err(RuntimeError::InvalidOperation {
message: format!("Cannot instantiate interface '{}'", class),
});
}
// 🚀 ジェネリクス型の特殊化処理
let (final_box_decl, actual_class_name) = if !type_arguments.is_empty() {
// ジェネリクス型を特殊化
let specialized = self.specialize_generic_class(&box_decl, type_arguments)?;
let specialized_name = specialized.name.clone();
(specialized, specialized_name)
} else {
(box_decl.clone(), class.to_string())
};
// 継承チェーンを解決してフィールドとメソッドを収集init_fieldsも含む
let (all_fields, all_methods) = self.resolve_inheritance(&final_box_decl)?;
// インスタンスを作成
let instance = InstanceBox::new(
actual_class_name.clone(),
all_fields,
all_methods
);
let instance_box = Box::new(instance) as Box<dyn NyashBox>;
// 現在のスコープでBoxを追跡自動解放のため
// 🌍 革命的実装Environment tracking廃止
// コンストラクタを呼び出す
let constructor_key = format!("{}/{}", actual_class_name, arguments.len());
if let Some(constructor) = final_box_decl.constructors.get(&constructor_key) {
// コンストラクタを実行
self.execute_constructor(&instance_box, constructor, arguments, &final_box_decl)?;
} else if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("No constructor found for {} with {} arguments", class, arguments.len()),
});
}
Ok(instance_box)
}
/// コンストラクタを実行 - Constructor execution
pub(super) fn execute_constructor(
&mut self,
instance: &Box<dyn NyashBox>,
constructor: &ASTNode,
arguments: &[ASTNode],
box_decl: &BoxDeclaration
) -> Result<(), RuntimeError> {
if let ASTNode::FunctionDeclaration { name: _, params, body, .. } = constructor {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// パラメータ数チェック
if params.len() != arg_values.len() {
return Err(RuntimeError::InvalidOperation {
message: format!("Constructor expects {} arguments, got {}", params.len(), arg_values.len()),
});
}
// 🌍 革命的コンストラクタ実行local変数スタックを使用
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// パラメータをlocal変数として設定
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_box());
}
// thismeをlocal変数として設定
self.declare_local_variable("me", instance.clone_box());
// コンストラクタコンテキストを設定
let old_context = self.current_constructor_context.clone();
self.current_constructor_context = Some(ConstructorContext {
class_name: box_decl.name.clone(),
parent_class: box_decl.extends.clone(),
});
// コンストラクタを実行
let mut result = Ok(());
for statement in body.iter() {
if let Err(e) = self.execute_statement(statement) {
result = Err(e);
break;
}
}
// local変数スタックとコンテキストを復元
self.restore_local_vars(saved_locals);
self.current_constructor_context = old_context;
result
} else {
Err(RuntimeError::InvalidOperation {
message: "Invalid constructor node".to_string(),
})
}
}
/// Box宣言を登録 - Box declaration registration
pub(super) fn register_box_declaration(
&mut self,
name: String,
fields: Vec<String>,
methods: HashMap<String, ASTNode>,
constructors: HashMap<String, ASTNode>,
init_fields: Vec<String>,
is_interface: bool,
extends: Option<String>,
implements: Vec<String>,
type_parameters: Vec<String> // 🔥 ジェネリクス型パラメータ追加
) {
let box_decl = super::BoxDeclaration {
name: name.clone(),
fields,
methods,
constructors,
init_fields,
is_interface,
extends,
implements,
type_parameters, // 🔥 ジェネリクス型パラメータを正しく使用
};
{
let mut box_decls = self.shared.box_declarations.write().unwrap();
box_decls.insert(name, box_decl);
}
}
/// 🔥 ジェネリクス型引数の検証
fn validate_generic_arguments(&self, box_decl: &BoxDeclaration, type_arguments: &[String])
-> Result<(), RuntimeError> {
// 型パラメータと型引数の数が一致するかチェック
if box_decl.type_parameters.len() != type_arguments.len() {
return Err(RuntimeError::TypeError {
message: format!(
"Generic class '{}' expects {} type parameters, got {}. Expected: <{}>, Got: <{}>",
box_decl.name,
box_decl.type_parameters.len(),
type_arguments.len(),
box_decl.type_parameters.join(", "),
type_arguments.join(", ")
),
});
}
// 型引数がジェネリクスでない場合、型パラメータがあってはならない
if box_decl.type_parameters.is_empty() && !type_arguments.is_empty() {
return Err(RuntimeError::TypeError {
message: format!(
"Class '{}' is not generic, but got type arguments <{}>",
box_decl.name,
type_arguments.join(", ")
),
});
}
// 各型引数が有効なBox型かチェック基本型のみチェック
for type_arg in type_arguments {
if !self.is_valid_type(type_arg) {
return Err(RuntimeError::TypeError {
message: format!("Unknown type '{}'", type_arg),
});
}
}
Ok(())
}
/// 型が有効かどうかをチェック
fn is_valid_type(&self, type_name: &str) -> bool {
// 基本的なビルトイン型
let is_builtin = matches!(type_name,
"IntegerBox" | "StringBox" | "BoolBox" | "ArrayBox" | "MapBox" |
"FileBox" | "ResultBox" | "FutureBox" | "ChannelBox" | "MathBox" |
"TimeBox" | "DateTimeBox" | "TimerBox" | "RandomBox" | "SoundBox" |
"DebugBox" | "MethodBox" | "NullBox" | "ConsoleBox" | "FloatBox"
);
// Web専用BoxWASM環境のみ
#[cfg(target_arch = "wasm32")]
let is_web_box = matches!(type_name, "WebDisplayBox" | "WebConsoleBox" | "WebCanvasBox");
#[cfg(not(target_arch = "wasm32"))]
let is_web_box = false;
is_builtin || is_web_box ||
// または登録済みのユーザー定義Box
self.shared.box_declarations.read().unwrap().contains_key(type_name)
}
/// 親コンストラクタを実行 - Parent constructor execution
pub(super) fn execute_parent_constructor(&mut self, parent_class: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 親クラスの宣言を取得
let parent_decl = {
let box_decls = self.shared.box_declarations.read().unwrap();
box_decls.get(parent_class)
.ok_or(RuntimeError::UndefinedClass { name: parent_class.to_string() })?
.clone()
};
// 親コンストラクタを探す
let constructor_key = format!("{}/{}", parent_class, arguments.len());
if let Some(parent_constructor) = parent_decl.constructors.get(&constructor_key) {
// 現在のthis参照を取得
// 🌍 革命的this取得local変数から
let this_instance = self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' not available in parent constructor call".to_string(),
})?;
// 親コンストラクタを実行
self.execute_constructor(&this_instance, parent_constructor, arguments, &parent_decl)?;
// VoidBoxを返すコンストラクタ呼び出しは値を返さない
Ok(Box::new(VoidBox::new()))
} else {
Err(RuntimeError::InvalidOperation {
message: format!("No constructor found for parent class {} with {} arguments", parent_class, arguments.len()),
})
}
}
/// 継承チェーンを解決してフィールドとメソッドを収集 - Inheritance resolution
pub(super) fn resolve_inheritance(&self, box_decl: &BoxDeclaration)
-> Result<(Vec<String>, HashMap<String, ASTNode>), RuntimeError> {
let mut all_fields = Vec::new();
let mut all_methods = HashMap::new();
// 親クラスの継承チェーンを再帰的に解決
if let Some(parent_name) = &box_decl.extends {
let parent_decl = {
let box_decls = self.shared.box_declarations.read().unwrap();
box_decls.get(parent_name)
.ok_or(RuntimeError::UndefinedClass { name: parent_name.clone() })?
.clone()
};
// インターフェースは継承できない
if parent_decl.is_interface {
return Err(RuntimeError::InvalidOperation {
message: format!("Cannot extend interface '{}'. Use 'implements' instead.", parent_name),
});
}
// 親クラスの継承チェーンを再帰的に解決
let (parent_fields, parent_methods) = self.resolve_inheritance(&parent_decl)?;
// 親のフィールドとメソッドを追加
all_fields.extend(parent_fields);
all_methods.extend(parent_methods);
}
// 現在のクラスのフィールドとメソッドを追加(オーバーライド可能)
all_fields.extend(box_decl.fields.clone());
// init_fieldsも追加重複チェック
for init_field in &box_decl.init_fields {
if !all_fields.contains(init_field) {
all_fields.push(init_field.clone());
}
}
for (method_name, method_ast) in &box_decl.methods {
all_methods.insert(method_name.clone(), method_ast.clone()); // オーバーライド
}
// インターフェース実装の検証
for interface_name in &box_decl.implements {
let interface_decl = {
let box_decls = self.shared.box_declarations.read().unwrap();
box_decls.get(interface_name)
.ok_or(RuntimeError::UndefinedClass { name: interface_name.clone() })?
.clone()
};
if !interface_decl.is_interface {
return Err(RuntimeError::InvalidOperation {
message: format!("'{}' is not an interface", interface_name),
});
}
// インターフェースの全メソッドが実装されているかチェック
for (required_method, _) in &interface_decl.methods {
if !all_methods.contains_key(required_method) {
return Err(RuntimeError::InvalidOperation {
message: format!("Class '{}' must implement method '{}' from interface '{}'",
box_decl.name, required_method, interface_name),
});
}
}
}
Ok((all_fields, all_methods))
}
/// 🚀 ジェネリクス型を特殊化してBoxDeclarationを生成
fn specialize_generic_class(
&self,
generic_decl: &BoxDeclaration,
type_arguments: &[String]
) -> Result<BoxDeclaration, RuntimeError> {
use std::collections::HashMap;
// 特殊化されたクラス名を生成
let specialized_name = format!(
"{}_{}",
generic_decl.name,
type_arguments.join("_")
);
// 型パラメータ → 具体型のマッピングを作成
let mut type_mapping = HashMap::new();
for (i, param) in generic_decl.type_parameters.iter().enumerate() {
type_mapping.insert(param.clone(), type_arguments[i].clone());
}
// 特殊化されたBoxDeclarationを作成
let mut specialized = generic_decl.clone();
specialized.name = specialized_name.clone();
specialized.type_parameters.clear(); // 特殊化後は型パラメータなし
// 🔄 フィールドの型を置換
specialized.init_fields = self.substitute_types_in_fields(
&specialized.init_fields,
&type_mapping
);
// 🔧 コンストラクタキーを新しいクラス名で更新
let mut updated_constructors = HashMap::new();
for (old_key, constructor_node) in &generic_decl.constructors {
// "Container/1" -> "Container_IntegerBox/1" に変更
if let Some(args_count) = old_key.split('/').nth(1) {
let new_key = format!("{}/{}", specialized_name, args_count);
updated_constructors.insert(new_key, constructor_node.clone());
}
}
specialized.constructors = updated_constructors;
// 🔄 メソッドの型を置換(現在はプレースホルダー実装)
// TODO: メソッド内部のコードも置換が必要
Ok(specialized)
}
/// フィールドの型置換
fn substitute_types_in_fields(
&self,
fields: &[String],
_type_mapping: &HashMap<String, String>
) -> Vec<String> {
// TODO: フィールド型の置換実装
// 現在はシンプルにコピー
fields.to_vec()
}
}

View File

@ -0,0 +1,427 @@
/*!
* Statement Processing Module
*
* Extracted from core.rs - statement execution engine
* Handles all statement types: assignments, if/else, loops, control flow
* Core philosophy: "Everything is Box" with structured statement processing
*/
use super::*;
impl NyashInterpreter {
/// 文を実行 - Core statement execution engine
pub(super) fn execute_statement(&mut self, statement: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
match statement {
ASTNode::Assignment { target, value, .. } => {
self.execute_assignment(target, value)
}
ASTNode::Print { expression, .. } => {
let value = self.execute_expression(expression)?;
println!("{}", value.to_string_box());
Ok(Box::new(VoidBox::new()))
}
ASTNode::If { condition, then_body, else_body, .. } => {
self.execute_if(condition, then_body, else_body)
}
ASTNode::Loop { condition, body, .. } => {
self.execute_loop(condition, body)
}
ASTNode::Return { value, .. } => {
let return_value = if let Some(val) = value {
self.execute_expression(val)?
} else {
Box::new(VoidBox::new())
};
self.control_flow = super::ControlFlow::Return(return_value);
Ok(Box::new(VoidBox::new()))
}
ASTNode::Break { .. } => {
self.control_flow = super::ControlFlow::Break;
Ok(Box::new(VoidBox::new()))
}
ASTNode::Nowait { variable, expression, .. } => {
self.execute_nowait(variable, expression)
}
ASTNode::BoxDeclaration { name, fields, methods, constructors, init_fields, is_interface, extends, implements, type_parameters, is_static, static_init, .. } => {
if *is_static {
// 🔥 Static Box宣言の処理
self.register_static_box_declaration(
name.clone(),
fields.clone(),
methods.clone(),
init_fields.clone(),
static_init.clone(),
extends.clone(),
implements.clone(),
type_parameters.clone()
)?;
} else {
// 通常のBox宣言の処理
self.register_box_declaration(
name.clone(),
fields.clone(),
methods.clone(),
constructors.clone(),
init_fields.clone(),
*is_interface,
extends.clone(),
implements.clone(),
type_parameters.clone() // 🔥 ジェネリクス型パラメータ追加
);
}
Ok(Box::new(VoidBox::new()))
}
ASTNode::FunctionDeclaration { name, params, body, is_static, .. } => {
if *is_static {
// 🔥 静的関数box名.関数名の形式で解析
if let Some(dot_pos) = name.find('.') {
let box_name = name[..dot_pos].to_string();
let func_name = name[dot_pos + 1..].to_string();
// boxのstaticメソッドとして登録
let func_ast = ASTNode::FunctionDeclaration {
name: func_name.clone(),
params: params.clone(),
body: body.clone(),
is_static: true,
span: crate::ast::Span::unknown(),
};
{
let mut static_funcs = self.shared.static_functions.write().unwrap();
static_funcs
.entry(box_name.clone())
.or_insert_with(HashMap::new)
.insert(func_name.clone(), func_ast);
}
eprintln!("🔥 Static function '{}.{}' registered", box_name, func_name);
} else {
// box名なしのstatic関数将来的にはエラーにする
eprintln!("⚠️ Static function '{}' needs box prefix (e.g., Math.min)", name);
}
} else {
// 通常の関数従来通りGlobalBoxメソッドとして登録
self.register_function_declaration(name.clone(), params.clone(), body.clone());
}
Ok(Box::new(VoidBox::new()))
}
ASTNode::GlobalVar { name, value, .. } => {
let val = self.execute_expression(value)?;
// 🌍 革命的グローバル変数GlobalBoxのフィールドとして設定
self.set_variable(name, val.clone_box())?;
Ok(Box::new(VoidBox::new()))
}
ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => {
self.execute_try_catch(try_body, catch_clauses, finally_body)
}
ASTNode::Throw { expression, .. } => {
self.execute_throw(expression)
}
ASTNode::Local { variables, initial_values, .. } => {
// 🌍 革命的local変数宣言local変数スタックに追加初期化対応
for (i, var_name) in variables.iter().enumerate() {
if let Some(Some(init_expr)) = initial_values.get(i) {
// 🚀 初期化付きlocal宣言: local x = value
let init_value = self.execute_expression(init_expr)?;
self.declare_local_variable(var_name, init_value);
} else {
// 従来のlocal宣言: local x
self.declare_local_variable(var_name, Box::new(VoidBox::new()));
}
}
Ok(Box::new(VoidBox::new()))
}
ASTNode::Outbox { variables, initial_values, .. } => {
// 📤 革命的outbox変数宣言static関数内で所有権移転初期化対応
for (i, var_name) in variables.iter().enumerate() {
if let Some(Some(init_expr)) = initial_values.get(i) {
// 🚀 初期化付きoutbox宣言: outbox x = value
let init_value = self.execute_expression(init_expr)?;
self.declare_outbox_variable(var_name, init_value);
} else {
// 従来のoutbox宣言: outbox x
self.declare_outbox_variable(var_name, Box::new(VoidBox::new()));
}
}
Ok(Box::new(VoidBox::new()))
}
// 式文
_ => self.execute_expression(statement),
}
}
/// 条件分岐を実行 - If/else statement processing
pub(super) fn execute_if(&mut self, condition: &ASTNode, then_body: &[ASTNode], else_body: &Option<Vec<ASTNode>>)
-> Result<Box<dyn NyashBox>, RuntimeError> {
let condition_value = self.execute_expression(condition)?;
// 条件を真偉値として評価
let is_true = self.is_truthy(&condition_value);
if is_true {
for statement in then_body {
self.execute_statement(statement)?;
if !matches!(self.control_flow, super::ControlFlow::None) {
break;
}
}
} else if let Some(else_statements) = else_body {
for statement in else_statements {
self.execute_statement(statement)?;
if !matches!(self.control_flow, super::ControlFlow::None) {
break;
}
}
}
Ok(Box::new(VoidBox::new()))
}
/// ループを実行 - Loop processing: loop(condition) { body } のみ
pub(super) fn execute_loop(&mut self, condition: &Box<ASTNode>, body: &[ASTNode]) -> Result<Box<dyn NyashBox>, RuntimeError> {
loop {
// 常に条件をチェック
let condition_result = self.execute_expression(condition)?;
if let Some(bool_box) = condition_result.as_any().downcast_ref::<BoolBox>() {
if !bool_box.value {
break; // 条件がfalseの場合はループ終了
}
} else {
// 条件が真偉値でない場合は、Interpreter::is_truthy()を使用
if !self.is_truthy(&condition_result) {
break;
}
}
// ループ本体を実行
for statement in body {
self.execute_statement(statement)?;
match &self.control_flow {
super::ControlFlow::Break => {
self.control_flow = super::ControlFlow::None;
return Ok(Box::new(VoidBox::new()));
}
super::ControlFlow::Return(_) => {
// returnはループを抜けるが、上位に伝播
return Ok(Box::new(VoidBox::new()));
}
super::ControlFlow::Throw(_) => {
// 例外はループを抜けて上位に伝播
return Ok(Box::new(VoidBox::new()));
}
super::ControlFlow::None => {}
}
}
}
Ok(Box::new(VoidBox::new()))
}
/// 代入処理を実行 - Assignment processing
pub(super) fn execute_assignment(&mut self, target: &ASTNode, value: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
let val = self.execute_expression(value)?;
match target {
ASTNode::Variable { name, .. } => {
// 🌍 革命的代入local変数 → GlobalBoxフィールド
self.set_variable(name, val.clone_box())?;
Ok(val)
}
ASTNode::FieldAccess { object, field, .. } => {
// フィールドへの代入
let obj_value = self.execute_expression(object)?;
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
// 既存のフィールド値があればfini()を呼ぶ
if let Some(old_field_value) = instance.get_field(field) {
if let Some(old_instance) = old_field_value.as_any().downcast_ref::<InstanceBox>() {
let _ = old_instance.fini();
finalization::mark_as_finalized(old_instance.box_id());
}
}
instance.set_field(field, val.clone_box())
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
Ok(val)
} else {
Err(RuntimeError::TypeError {
message: format!("Cannot set field '{}' on non-instance type", field),
})
}
}
ASTNode::ThisField { field, .. } => {
// 🌍 革命的this.field代入local変数から取得
let this_value = self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' is not bound in the current context".to_string(),
})?;
if let Some(instance) = this_value.as_any().downcast_ref::<InstanceBox>() {
// 既存のthis.field値があればfini()を呼ぶ
if let Some(old_field_value) = instance.get_field(field) {
if let Some(old_instance) = old_field_value.as_any().downcast_ref::<InstanceBox>() {
let _ = old_instance.fini();
finalization::mark_as_finalized(old_instance.box_id());
}
}
instance.set_field(field, val.clone_box())
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
Ok(val)
} else {
Err(RuntimeError::TypeError {
message: "'this' is not an instance".to_string(),
})
}
}
ASTNode::MeField { field, .. } => {
// 🌍 革命的me.field代入local変数から取得
let me_value = self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' is not bound in the current context".to_string(),
})?;
if let Some(instance) = me_value.as_any().downcast_ref::<InstanceBox>() {
// 既存のme.field値があればfini()を呼ぶ
if let Some(old_field_value) = instance.get_field(field) {
if let Some(old_instance) = old_field_value.as_any().downcast_ref::<InstanceBox>() {
let _ = old_instance.fini();
finalization::mark_as_finalized(old_instance.box_id());
}
}
instance.set_field(field, val.clone_box())
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
Ok(val)
} else {
Err(RuntimeError::TypeError {
message: "'this' is not an instance".to_string(),
})
}
}
_ => Err(RuntimeError::InvalidOperation {
message: "Invalid assignment target".to_string(),
}),
}
}
/// try/catch/finally文を実行 - Exception handling
pub(super) fn execute_try_catch(&mut self, try_body: &[ASTNode], catch_clauses: &[super::CatchClause], finally_body: &Option<Vec<ASTNode>>)
-> Result<Box<dyn NyashBox>, RuntimeError> {
let mut thrown_exception: Option<Box<dyn NyashBox>> = None;
// Try block execution
let mut try_result = Ok(Box::new(VoidBox::new()));
for statement in try_body {
match self.execute_statement(statement) {
Ok(_) => {
// 制御フローをチェック
if !matches!(self.control_flow, super::ControlFlow::None) {
if let super::ControlFlow::Throw(exception) = &self.control_flow {
thrown_exception = Some(exception.clone_box());
self.control_flow = super::ControlFlow::None;
break;
} else {
break; // Return/Break等は上位に伝播
}
}
}
Err(e) => {
// RuntimeErrorを例外として扱う
thrown_exception = Some(Box::new(exception_box::ErrorBox::new(&format!("{:?}", e))));
try_result = Err(e);
break;
}
}
}
// Catch clause processing
if let Some(exception) = &thrown_exception {
for catch_clause in catch_clauses {
// 型チェック
if let Some(exception_type) = &catch_clause.exception_type {
if !exception_box::is_exception_type(exception.as_ref(), exception_type) {
continue; // 型が合わない場合は次のcatch句へ
}
}
// 🌍 革命的例外変数束縛local変数として設定
if let Some(var_name) = &catch_clause.variable_name {
self.declare_local_variable(var_name, exception.clone_box());
}
// Catch body execution
for statement in &catch_clause.body {
self.execute_statement(statement)?;
if !matches!(self.control_flow, super::ControlFlow::None) {
break;
}
}
// 🌍 革命的例外変数クリーンアップlocal変数から削除
if let Some(var_name) = &catch_clause.variable_name {
self.local_vars.remove(var_name);
}
thrown_exception = None; // 例外が処理された
break;
}
}
// Finally block execution (always executed)
if let Some(ref finally_statements) = finally_body {
for statement in finally_statements {
self.execute_statement(statement)?;
if !matches!(self.control_flow, super::ControlFlow::None) {
break;
}
}
}
// 未処理の例外があれば再スロー
if let Some(exception) = thrown_exception {
self.control_flow = super::ControlFlow::Throw(exception);
}
match try_result {
Ok(result) => Ok(result),
Err(_) => Ok(Box::new(VoidBox::new()) as Box<dyn NyashBox>),
}
}
/// throw文を実行 - Throw exception
pub(super) fn execute_throw(&mut self, expression: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
let value = self.execute_expression(expression)?;
// 値を例外として扱う
let exception = if let Some(error_box) = value.as_any().downcast_ref::<exception_box::ErrorBox>() {
Box::new(error_box.clone()) as Box<dyn NyashBox>
} else {
// 文字列や他の値はErrorBoxに変換
Box::new(exception_box::ErrorBox::new(&value.to_string_box().value))
};
self.control_flow = super::ControlFlow::Throw(exception);
Ok(Box::new(VoidBox::new()))
}
}

181
src/lib.rs Normal file
View File

@ -0,0 +1,181 @@
/*!
* Nyash Programming Language - Rust Implementation Library
*
* Everything is Box philosophy implemented in memory-safe Rust
*/
// 🌐 WebAssembly support
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
pub mod box_trait;
pub mod boxes;
// pub mod stdlib;
pub mod environment;
pub mod tokenizer;
pub mod ast; // Using old ast.rs for now
pub mod parser; // Using old parser.rs for now
pub mod interpreter;
pub mod instance;
pub mod channel_box;
pub mod finalization;
pub mod exception_box;
pub mod method_box;
pub mod type_box; // 🌟 TypeBox revolutionary system
#[cfg(target_arch = "wasm32")]
pub mod wasm_test;
// Re-export main types for easy access
pub use box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox, AddBox};
pub use environment::{Environment, PythonCompatEnvironment};
pub use tokenizer::{NyashTokenizer, TokenType, Token};
pub use type_box::{TypeBox, TypeRegistry, MethodSignature}; // 🌟 TypeBox exports
pub use ast::{ASTNode, BinaryOperator, LiteralValue};
pub use parser::{NyashParser, ParseError};
pub use interpreter::{NyashInterpreter, RuntimeError};
pub use instance::InstanceBox;
pub use channel_box::{ChannelBox, MessageBox};
pub use boxes::math_box::{MathBox, FloatBox, RangeBox};
pub use boxes::time_box::{TimeBox, DateTimeBox, TimerBox};
pub use boxes::map_box::MapBox;
pub use boxes::random_box::RandomBox;
pub use boxes::sound_box::SoundBox;
pub use boxes::debug_box::DebugBox;
pub use boxes::console_box::ConsoleBox;
pub use method_box::{MethodBox, BoxType, FunctionDefinition, EphemeralInstance};
pub use boxes::null_box::{NullBox, null};
// Direct canvas test export
#[cfg(target_arch = "wasm32")]
pub use wasm_test::wasm_test::test_direct_canvas_draw;
// 🌐 WebAssembly exports for browser usage
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub struct NyashWasm {
interpreter: NyashInterpreter,
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
impl NyashWasm {
/// Create a new Nyash interpreter instance for browser use
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
// Setup panic handling for better browser debugging
console_error_panic_hook::set_once();
// Create interpreter with browser-specific setup
let interpreter = NyashInterpreter::new();
// Register browser-specific boxes
// ConsoleBox is available as a constructor: console = new ConsoleBox()
// TODO: Also register DOMBox, CanvasBox etc.
Self { interpreter }
}
/// Evaluate Nyash code and return result as string
#[wasm_bindgen]
pub fn eval(&mut self, code: &str) -> String {
// Handle empty or whitespace-only input
let trimmed_code = code.trim();
if trimmed_code.is_empty() {
return String::new();
}
// Split multiline code into logical statements for better WASM handling
let lines: Vec<&str> = trimmed_code.lines()
.map(|line| line.trim())
.filter(|line| !line.is_empty() && !line.starts_with("//"))
.collect();
// If single line or looks like a complete static box/box definition, parse as-is
if lines.len() == 1 || trimmed_code.contains("static box") || trimmed_code.contains("box ") {
return self.eval_single_block(trimmed_code);
}
// For multiple lines, try to execute line by line
let mut results = Vec::new();
let mut accumulated_code = String::new();
for line in lines {
// Accumulate lines for block structures
accumulated_code.push_str(line);
accumulated_code.push('\n');
// Check if we have a complete statement
if self.is_complete_statement(&accumulated_code) {
let result = self.eval_single_block(accumulated_code.trim());
if result.starts_with("Parse Error:") {
return result; // Stop on parse error
}
if !result.is_empty() && result != "void" {
results.push(result);
}
accumulated_code.clear();
}
}
// Execute any remaining accumulated code
if !accumulated_code.trim().is_empty() {
let result = self.eval_single_block(accumulated_code.trim());
if !result.is_empty() && result != "void" {
results.push(result);
}
}
// Return the most relevant result
results.into_iter()
.filter(|r| !r.starts_with("Parse Error:") && !r.starts_with("Runtime Error:"))
.last()
.unwrap_or_else(|| "void".to_string())
}
/// Evaluate a single block of code
fn eval_single_block(&mut self, code: &str) -> String {
// First parse the code into an AST
let ast = match NyashParser::parse_from_string(code) {
Ok(ast) => ast,
Err(e) => return format!("Parse Error: {}", e),
};
// Then execute the AST
match self.interpreter.execute(ast) {
Ok(result_box) => {
// Format the result for browser display
let result_str = result_box.to_string_box().value;
if result_str == "void" || result_str.is_empty() {
"void".to_string()
} else {
result_str
}
}
Err(e) => format!("Runtime Error: {}", e),
}
}
/// Check if code represents a complete statement (heuristic)
fn is_complete_statement(&self, code: &str) -> bool {
let trimmed = code.trim();
// Always complete: assignments, function calls, simple expressions
if trimmed.contains('=') && !trimmed.ends_with('=') {
return true;
}
// Block structures need closing braces
let open_braces = trimmed.chars().filter(|&c| c == '{').count();
let close_braces = trimmed.chars().filter(|&c| c == '}').count();
// Complete if braces are balanced or no braces at all
open_braces == 0 || open_braces == close_braces
}
/// Get the current version info
#[wasm_bindgen]
pub fn version() -> String {
String::from("Nyash WASM v0.1.0 - Everything is Box in Browser!")
}
}

1006
src/main.rs Normal file

File diff suppressed because it is too large Load Diff

207
src/method_box.rs Normal file
View File

@ -0,0 +1,207 @@
/*!
* MethodBox - Function Pointer Implementation for Nyash
*
* イベントハンドラーやコールバックを実現するためのMethodBox実装
* ChatGPT先生のアドバイスを全面採用
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox};
use crate::ast::ASTNode;
use crate::instance::InstanceBox;
use std::fmt::{Debug, Display};
use std::any::Any;
use std::sync::{Arc, Mutex};
/// BoxType enum - ChatGPT先生の提案に従い、Box型を分類
#[derive(Debug)]
pub enum BoxType {
/// 通常のインスタンス
Instance(Box<dyn NyashBox>),
/// 関数定義(インスタンスなし)
Function(FunctionDefinition),
/// メソッド参照(インスタンス+メソッド)
Method(MethodBox),
}
/// 関数定義情報
#[derive(Debug, Clone)]
pub struct FunctionDefinition {
pub name: String,
pub params: Vec<String>,
pub body: Vec<ASTNode>,
pub is_static: bool,
}
/// MethodBox - インスタンスとメソッドの組み合わせ
#[derive(Debug, Clone)]
pub struct MethodBox {
/// メソッドを持つインスタンス
pub instance: Arc<Mutex<Box<dyn NyashBox>>>,
/// メソッド名
pub method_name: String,
/// メソッド定義(キャッシュ用)
pub method_def: Option<FunctionDefinition>,
/// ユニークID
id: u64,
}
impl MethodBox {
/// 新しいMethodBoxを作成
pub fn new(instance: Box<dyn NyashBox>, method_name: String) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
// メソッド定義をキャッシュ(可能であれば)
let method_def = if let Some(inst) = instance.as_any().downcast_ref::<InstanceBox>() {
inst.get_method(&method_name).and_then(|ast| {
if let ASTNode::FunctionDeclaration { name, params, body, is_static, .. } = ast {
Some(FunctionDefinition {
name: name.clone(),
params: params.clone(),
body: body.clone(),
is_static: *is_static,
})
} else {
None
}
})
} else {
None
};
Self {
instance: Arc::new(Mutex::new(instance)),
method_name,
method_def,
id,
}
}
/// メソッドを呼び出す
pub fn invoke(&self, _args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, String> {
// TODO: インタープリタとの統合が必要
// 現在は仮実装
Err(format!("MethodBox.invoke not yet implemented for method '{}'", self.method_name))
}
/// インスタンスを取得(内部使用)
pub fn get_instance(&self) -> Arc<Mutex<Box<dyn NyashBox>>> {
Arc::clone(&self.instance)
}
}
impl NyashBox for MethodBox {
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("<MethodBox: {}>", self.method_name))
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_method) = other.as_any().downcast_ref::<MethodBox>() {
// 同じインスタンス、同じメソッド名なら等しい
let self_inst = self.instance.lock().unwrap();
let other_inst = other_method.instance.lock().unwrap();
BoolBox::new(
self_inst.box_id() == other_inst.box_id() &&
self.method_name == other_method.method_name
)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"MethodBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for MethodBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<MethodBox: {}>", self.method_name)
}
}
/// EphemeralInstance - 一時的なスコープ(関数実行時)
#[derive(Debug)]
pub struct EphemeralInstance {
/// ローカル変数
pub locals: HashMap<String, Box<dyn NyashBox>>,
/// 親インスタンス(メソッド呼び出しの場合)
pub parent_instance: Option<Arc<Mutex<Box<dyn NyashBox>>>>,
}
use std::collections::HashMap;
impl EphemeralInstance {
/// 新しい一時スコープを作成
pub fn new() -> Self {
Self {
locals: HashMap::new(),
parent_instance: None,
}
}
/// インスタンスから一時スコープを作成
pub fn from_instance(instance: Arc<Mutex<Box<dyn NyashBox>>>) -> Self {
Self {
locals: HashMap::new(),
parent_instance: Some(instance),
}
}
/// ローカル変数を設定
pub fn set_local(&mut self, name: String, value: Box<dyn NyashBox>) {
self.locals.insert(name, value);
}
/// ローカル変数を取得
pub fn get_local(&self, name: &str) -> Option<Box<dyn NyashBox>> {
self.locals.get(name).map(|v| v.clone_box())
}
/// 変数を解決local → instance fields → global
pub fn resolve_variable(&self, name: &str) -> Option<Box<dyn NyashBox>> {
// 1. ローカル変数
if let Some(value) = self.get_local(name) {
return Some(value);
}
// 2. インスタンスフィールドparent_instanceがある場合
if let Some(parent) = &self.parent_instance {
let inst = parent.lock().unwrap();
if let Some(instance_box) = inst.as_any().downcast_ref::<InstanceBox>() {
if let Some(field_value) = instance_box.get_field(name) {
return Some(field_value);
}
}
}
// 3. グローバルTODO: インタープリタとの統合が必要)
None
}
}
/// MethodBox作成用のビルダー構文サポート
pub fn create_method_box(instance: Box<dyn NyashBox>, method_name: &str) -> Box<dyn NyashBox> {
Box::new(MethodBox::new(instance, method_name.to_string()))
}

2279
src/parser.rs.backup Normal file

File diff suppressed because it is too large Load Diff

414
src/parser/expressions.rs Normal file
View File

@ -0,0 +1,414 @@
/*!
* Nyash Parser - Expression Parsing Module
*
*Expressionの解析を担当するモジュール
* 演算子の優先順位に従った再帰下降パーサー実装
*/
use crate::tokenizer::TokenType;
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, UnaryOperator, Span};
use super::{NyashParser, ParseError};
impl NyashParser {
/// 式をパース (演算子優先順位あり)
pub(super) fn parse_expression(&mut self) -> Result<ASTNode, ParseError> {
self.parse_or()
}
/// OR演算子をパース: ||
fn parse_or(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_and()?;
while self.match_token(&TokenType::OR) {
let operator = BinaryOperator::Or;
self.advance();
let right = self.parse_and()?;
expr = ASTNode::BinaryOp {
operator,
left: Box::new(expr),
right: Box::new(right),
span: Span::unknown(),
};
}
Ok(expr)
}
/// AND演算子をパース: &&
fn parse_and(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_equality()?;
while self.match_token(&TokenType::AND) {
let operator = BinaryOperator::And;
self.advance();
let right = self.parse_equality()?;
expr = ASTNode::BinaryOp {
operator,
left: Box::new(expr),
right: Box::new(right),
span: Span::unknown(),
};
}
Ok(expr)
}
/// 等値演算子をパース: == !=
fn parse_equality(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_comparison()?;
while self.match_token(&TokenType::EQUALS) || self.match_token(&TokenType::NotEquals) {
let operator = match &self.current_token().token_type {
TokenType::EQUALS => BinaryOperator::Equal,
TokenType::NotEquals => BinaryOperator::NotEqual,
_ => unreachable!(),
};
self.advance();
let right = self.parse_comparison()?;
expr = ASTNode::BinaryOp {
operator,
left: Box::new(expr),
right: Box::new(right),
span: Span::unknown(),
};
}
Ok(expr)
}
/// 比較演算子をパース: < <= > >=
fn parse_comparison(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_term()?;
while self.match_token(&TokenType::LESS) ||
self.match_token(&TokenType::LessEquals) ||
self.match_token(&TokenType::GREATER) ||
self.match_token(&TokenType::GreaterEquals) {
let operator = match &self.current_token().token_type {
TokenType::LESS => BinaryOperator::Less,
TokenType::LessEquals => BinaryOperator::LessEqual,
TokenType::GREATER => BinaryOperator::Greater,
TokenType::GreaterEquals => BinaryOperator::GreaterEqual,
_ => unreachable!(),
};
self.advance();
let right = self.parse_term()?;
expr = ASTNode::BinaryOp {
operator,
left: Box::new(expr),
right: Box::new(right),
span: Span::unknown(),
};
}
Ok(expr)
}
/// 項をパース: + - >>
fn parse_term(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_factor()?;
while self.match_token(&TokenType::PLUS) || self.match_token(&TokenType::MINUS) || self.match_token(&TokenType::ARROW) {
if self.match_token(&TokenType::ARROW) {
// >> Arrow演算子
self.advance();
let right = self.parse_factor()?;
expr = ASTNode::Arrow {
sender: Box::new(expr),
receiver: Box::new(right),
span: Span::unknown(),
};
} else {
let operator = match &self.current_token().token_type {
TokenType::PLUS => BinaryOperator::Add,
TokenType::MINUS => BinaryOperator::Subtract,
_ => unreachable!(),
};
self.advance();
let right = self.parse_factor()?;
expr = ASTNode::BinaryOp {
operator,
left: Box::new(expr),
right: Box::new(right),
span: Span::unknown(),
};
}
}
Ok(expr)
}
/// 因子をパース: * /
fn parse_factor(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_unary()?;
while self.match_token(&TokenType::MULTIPLY) || self.match_token(&TokenType::DIVIDE) {
let operator = match &self.current_token().token_type {
TokenType::MULTIPLY => BinaryOperator::Multiply,
TokenType::DIVIDE => BinaryOperator::Divide,
_ => unreachable!(),
};
self.advance();
let right = self.parse_unary()?;
expr = ASTNode::BinaryOp {
operator,
left: Box::new(expr),
right: Box::new(right),
span: Span::unknown(),
};
}
Ok(expr)
}
/// 単項演算子をパース
fn parse_unary(&mut self) -> Result<ASTNode, ParseError> {
if self.match_token(&TokenType::MINUS) {
self.advance(); // consume '-'
let operand = self.parse_unary()?; // 再帰的に単項演算をパース
return Ok(ASTNode::UnaryOp {
operator: UnaryOperator::Minus,
operand: Box::new(operand),
span: Span::unknown(),
});
}
if self.match_token(&TokenType::NOT) {
self.advance(); // consume 'not'
let operand = self.parse_unary()?; // 再帰的に単項演算をパース
return Ok(ASTNode::UnaryOp {
operator: UnaryOperator::Not,
operand: Box::new(operand),
span: Span::unknown(),
});
}
if self.match_token(&TokenType::AWAIT) {
self.advance(); // consume 'await'
let expression = self.parse_unary()?; // 再帰的にパース
return Ok(ASTNode::AwaitExpression {
expression: Box::new(expression),
span: Span::unknown(),
});
}
self.parse_call()
}
/// 関数・メソッド呼び出しをパース
fn parse_call(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_primary()?;
loop {
if self.match_token(&TokenType::DOT) {
self.advance(); // consume '.'
if let TokenType::IDENTIFIER(method_name) = &self.current_token().token_type {
let method_name = method_name.clone();
self.advance();
if self.match_token(&TokenType::LPAREN) {
// メソッド呼び出し: obj.method(args)
self.advance(); // consume '('
let mut arguments = Vec::new();
while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() {
arguments.push(self.parse_expression()?);
if self.match_token(&TokenType::COMMA) {
self.advance();
}
}
self.consume(TokenType::RPAREN)?;
expr = ASTNode::MethodCall {
object: Box::new(expr),
method: method_name,
arguments,
span: Span::unknown(),
};
} else {
// フィールドアクセス: obj.field
expr = ASTNode::FieldAccess {
object: Box::new(expr),
field: method_name,
span: Span::unknown(),
};
}
} else {
let line = self.current_token().line;
return Err(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "identifier".to_string(),
line,
});
}
} else if self.match_token(&TokenType::LPAREN) {
// 関数呼び出し: function(args)
if let ASTNode::Variable { name, .. } = expr {
self.advance(); // consume '('
let mut arguments = Vec::new();
while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() {
arguments.push(self.parse_expression()?);
if self.match_token(&TokenType::COMMA) {
self.advance();
}
}
self.consume(TokenType::RPAREN)?;
expr = ASTNode::FunctionCall { name, arguments, span: Span::unknown() };
} else {
break;
}
} else {
break;
}
}
Ok(expr)
}
/// 基本式をパース: リテラル、変数、括弧、this、new
fn parse_primary(&mut self) -> Result<ASTNode, ParseError> {
match &self.current_token().token_type {
TokenType::STRING(s) => {
let value = s.clone();
self.advance();
Ok(ASTNode::Literal {
value: LiteralValue::String(value),
span: Span::unknown(),
})
}
TokenType::NUMBER(n) => {
let value = *n;
self.advance();
Ok(ASTNode::Literal {
value: LiteralValue::Integer(value),
span: Span::unknown(),
})
}
TokenType::FLOAT(f) => {
let value = *f;
self.advance();
Ok(ASTNode::Literal {
value: LiteralValue::Float(value),
span: Span::unknown(),
})
}
TokenType::TRUE => {
self.advance();
Ok(ASTNode::Literal {
value: LiteralValue::Bool(true),
span: Span::unknown(),
})
}
TokenType::FALSE => {
self.advance();
Ok(ASTNode::Literal {
value: LiteralValue::Bool(false),
span: Span::unknown(),
})
}
TokenType::THIS => {
self.advance();
Ok(ASTNode::This { span: Span::unknown() })
}
TokenType::ME => {
self.advance();
Ok(ASTNode::Me { span: Span::unknown() })
}
TokenType::NEW => {
self.advance();
if let TokenType::IDENTIFIER(class_name) = &self.current_token().token_type {
let class_name = class_name.clone();
self.advance();
// 🔥 ジェネリクス型引数のパース (<IntegerBox, StringBox>)
let type_arguments = if self.match_token(&TokenType::LESS) {
self.advance(); // consume '<'
let mut args = Vec::new();
loop {
if let TokenType::IDENTIFIER(type_name) = &self.current_token().token_type {
args.push(type_name.clone());
self.advance();
if self.match_token(&TokenType::COMMA) {
self.advance(); // consume ','
} else {
break;
}
} else {
let line = self.current_token().line;
return Err(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "type argument".to_string(),
line,
});
}
}
self.consume(TokenType::GREATER)?; // consume '>'
args
} else {
Vec::new()
};
self.consume(TokenType::LPAREN)?;
let mut arguments = Vec::new();
while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() {
arguments.push(self.parse_expression()?);
if self.match_token(&TokenType::COMMA) {
self.advance();
}
}
self.consume(TokenType::RPAREN)?;
Ok(ASTNode::New {
class: class_name,
arguments,
type_arguments,
span: Span::unknown(),
})
} else {
let line = self.current_token().line;
Err(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "class name".to_string(),
line,
})
}
}
TokenType::IDENTIFIER(name) => {
let name = name.clone();
self.advance();
Ok(ASTNode::Variable { name, span: Span::unknown() })
}
TokenType::LPAREN => {
self.advance(); // consume '('
let expr = self.parse_expression()?;
self.consume(TokenType::RPAREN)?;
Ok(expr)
}
_ => {
let line = self.current_token().line;
Err(ParseError::InvalidExpression { line })
}
}
}
}

1250
src/parser/mod.rs Normal file

File diff suppressed because it is too large Load Diff

409
src/parser/statements.rs Normal file
View File

@ -0,0 +1,409 @@
/*!
* Nyash Parser - Statement Parsing Module
*
*Statementの解析を担当するモジュール
* if, loop, break, return, print等の制御構文を処理
*/
use crate::tokenizer::TokenType;
use crate::ast::{ASTNode, CatchClause, Span};
use super::{NyashParser, ParseError};
impl NyashParser {
/// 文をパース
pub(super) fn parse_statement(&mut self) -> Result<ASTNode, ParseError> {
match &self.current_token().token_type {
TokenType::BOX => self.parse_box_declaration(),
TokenType::INTERFACE => self.parse_interface_box_declaration(),
TokenType::GLOBAL => self.parse_global_var(),
TokenType::FUNCTION => self.parse_function_declaration(),
TokenType::STATIC => self.parse_static_declaration(), // 🔥 静的宣言 (function/box)
TokenType::IF => self.parse_if(),
TokenType::LOOP => self.parse_loop(),
TokenType::BREAK => self.parse_break(),
TokenType::RETURN => self.parse_return(),
TokenType::PRINT => self.parse_print(),
TokenType::NOWAIT => self.parse_nowait(),
TokenType::INCLUDE => self.parse_include(),
TokenType::LOCAL => self.parse_local(),
TokenType::OUTBOX => self.parse_outbox(),
TokenType::TRY => self.parse_try_catch(),
TokenType::THROW => self.parse_throw(),
TokenType::IDENTIFIER(_) => {
// function宣言 または 代入文 または 関数呼び出し
self.parse_assignment_or_function_call()
}
TokenType::THIS | TokenType::ME => {
// this/me で始まる文も通常の代入文または関数呼び出しとして処理
self.parse_assignment_or_function_call()
}
_ => {
let line = self.current_token().line;
Err(ParseError::InvalidStatement { line })
}
}
}
/// if文をパース: if (condition) { body } else if ... else { body }
pub(super) fn parse_if(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'if'
// 条件部分を取得
let condition = Box::new(self.parse_expression()?);
// then部分を取得
self.consume(TokenType::LBRACE)?;
let mut then_body = Vec::new();
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
self.skip_newlines();
if !self.match_token(&TokenType::RBRACE) {
then_body.push(self.parse_statement()?);
}
}
self.consume(TokenType::RBRACE)?;
// else if/else部分を処理
let else_body = if self.match_token(&TokenType::ELSE) {
self.advance(); // consume 'else'
if self.match_token(&TokenType::IF) {
// else if を ネストしたifとして処理
let nested_if = self.parse_if()?;
Some(vec![nested_if])
} else {
// plain else
self.consume(TokenType::LBRACE)?;
let mut else_stmts = Vec::new();
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
self.skip_newlines();
if !self.match_token(&TokenType::RBRACE) {
else_stmts.push(self.parse_statement()?);
}
}
self.consume(TokenType::RBRACE)?;
Some(else_stmts)
}
} else {
None
};
Ok(ASTNode::If {
condition,
then_body,
else_body,
span: Span::unknown(),
})
}
/// loop文をパース
pub(super) fn parse_loop(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'loop'
// 条件部分を取得
self.consume(TokenType::LPAREN)?;
let condition = Some(Box::new(self.parse_expression()?));
self.consume(TokenType::RPAREN)?;
// body部分を取得
self.consume(TokenType::LBRACE)?;
let mut body = Vec::new();
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
self.skip_newlines();
if !self.match_token(&TokenType::RBRACE) {
body.push(self.parse_statement()?);
}
}
self.consume(TokenType::RBRACE)?;
Ok(ASTNode::Loop {
condition: condition.unwrap(),
body,
span: Span::unknown(),
})
}
/// break文をパース
pub(super) fn parse_break(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'break'
Ok(ASTNode::Break { span: Span::unknown() })
}
/// return文をパース
pub(super) fn parse_return(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'return'
// returnの後に式があるかチェック
let value = if self.is_at_end() || self.match_token(&TokenType::NEWLINE) {
// return単体の場合はvoidを返す
None
} else {
// 式をパースして返す
Some(Box::new(self.parse_expression()?))
};
Ok(ASTNode::Return { value, span: Span::unknown() })
}
/// print文をパース
pub(super) fn parse_print(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'print'
self.consume(TokenType::LPAREN)?;
let value = Box::new(self.parse_expression()?);
self.consume(TokenType::RPAREN)?;
Ok(ASTNode::Print { expression: value, span: Span::unknown() })
}
/// nowait文をパース: nowait variable = expression
pub(super) fn parse_nowait(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'nowait'
// 変数名を取得
let variable = if let TokenType::IDENTIFIER(name) = &self.current_token().token_type {
let name = name.clone();
self.advance();
name
} else {
let line = self.current_token().line;
return Err(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "variable name".to_string(),
line,
});
};
self.consume(TokenType::ASSIGN)?;
let expression = Box::new(self.parse_expression()?);
Ok(ASTNode::Nowait {
variable,
expression,
span: Span::unknown(),
})
}
/// include文をパース
pub(super) fn parse_include(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'include'
let path = if let TokenType::STRING(path) = &self.current_token().token_type {
let path = path.clone();
self.advance();
path
} else {
let line = self.current_token().line;
return Err(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "string literal".to_string(),
line,
});
};
Ok(ASTNode::Include { filename: path, span: Span::unknown() })
}
/// local変数宣言をパース: local var1, var2, var3 または local x = 10
pub(super) fn parse_local(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'local'
let mut names = Vec::new();
let mut initial_values = Vec::new();
// 最初の変数名を取得
if let TokenType::IDENTIFIER(name) = &self.current_token().token_type {
names.push(name.clone());
self.advance();
// = があれば初期値を設定
if self.match_token(&TokenType::ASSIGN) {
self.advance(); // consume '='
initial_values.push(Some(Box::new(self.parse_expression()?)));
// 初期化付きlocalは単一変数のみカンマ区切り不可
Ok(ASTNode::Local {
variables: names,
initial_values,
span: Span::unknown(),
})
} else {
// 初期化なしの場合はカンマ区切りで複数変数可能
initial_values.push(None);
// カンマ区切りで追加の変数名を取得
while self.match_token(&TokenType::COMMA) {
self.advance(); // consume ','
if let TokenType::IDENTIFIER(name) = &self.current_token().token_type {
names.push(name.clone());
initial_values.push(None);
self.advance();
} else {
let line = self.current_token().line;
return Err(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "identifier".to_string(),
line,
});
}
}
Ok(ASTNode::Local {
variables: names,
initial_values,
span: Span::unknown(),
})
}
} else {
let line = self.current_token().line;
Err(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "identifier".to_string(),
line,
})
}
}
/// outbox変数宣言をパース: outbox var1, var2, var3
pub(super) fn parse_outbox(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'outbox'
let mut names = Vec::new();
// 最初の変数名を取得
if let TokenType::IDENTIFIER(name) = &self.current_token().token_type {
names.push(name.clone());
self.advance();
// カンマ区切りで追加の変数名を取得
while self.match_token(&TokenType::COMMA) {
self.advance(); // consume ','
if let TokenType::IDENTIFIER(name) = &self.current_token().token_type {
names.push(name.clone());
self.advance();
} else {
let line = self.current_token().line;
return Err(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "identifier".to_string(),
line,
});
}
}
let num_vars = names.len();
Ok(ASTNode::Outbox {
variables: names,
initial_values: vec![None; num_vars],
span: Span::unknown(),
})
} else {
let line = self.current_token().line;
Err(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "identifier".to_string(),
line,
})
}
}
/// try-catch文をパース
pub(super) fn parse_try_catch(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'try'
self.consume(TokenType::LBRACE)?;
let mut try_body = Vec::new();
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
self.skip_newlines();
if !self.match_token(&TokenType::RBRACE) {
try_body.push(self.parse_statement()?);
}
}
self.consume(TokenType::RBRACE)?;
let mut catch_clauses = Vec::new();
// catch節をパース
while self.match_token(&TokenType::CATCH) {
self.advance(); // consume 'catch'
self.consume(TokenType::LPAREN)?;
// 例外型 (オプション)
let exception_type = if let TokenType::IDENTIFIER(type_name) = &self.current_token().token_type {
let type_name = type_name.clone();
self.advance();
Some(type_name)
} else {
None
};
// 例外変数名
let exception_var = if let TokenType::IDENTIFIER(var_name) = &self.current_token().token_type {
let var_name = var_name.clone();
self.advance();
var_name
} else {
let line = self.current_token().line;
return Err(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: "exception variable name".to_string(),
line,
});
};
self.consume(TokenType::RPAREN)?;
self.consume(TokenType::LBRACE)?;
let mut catch_body = Vec::new();
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
self.skip_newlines();
if !self.match_token(&TokenType::RBRACE) {
catch_body.push(self.parse_statement()?);
}
}
self.consume(TokenType::RBRACE)?;
catch_clauses.push(CatchClause {
exception_type,
variable_name: Some(exception_var),
body: catch_body,
span: Span::unknown(),
});
}
// finally節をパース (オプション)
let finally_body = if self.match_token(&TokenType::FINALLY) {
self.advance(); // consume 'finally'
self.consume(TokenType::LBRACE)?;
let mut body = Vec::new();
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
self.skip_newlines();
if !self.match_token(&TokenType::RBRACE) {
body.push(self.parse_statement()?);
}
}
self.consume(TokenType::RBRACE)?;
Some(body)
} else {
None
};
Ok(ASTNode::TryCatch {
try_body,
catch_clauses,
finally_body,
span: Span::unknown(),
})
}
/// throw文をパース
pub(super) fn parse_throw(&mut self) -> Result<ASTNode, ParseError> {
self.advance(); // consume 'throw'
let value = Box::new(self.parse_expression()?);
Ok(ASTNode::Throw { expression: value, span: Span::unknown() })
}
}

612
src/tokenizer.rs Normal file
View File

@ -0,0 +1,612 @@
/*!
* Nyash Tokenizer - .nyashソースコードをトークン列に変換
*
* Python版nyashc_v4.pyのNyashTokenizerをRustで完全再実装
* 正規表現ベース → 高速なcharレベル処理に最適化
*/
use thiserror::Error;
/// トークンの種類を表すenum
#[derive(Debug, Clone, PartialEq)]
pub enum TokenType {
// リテラル
STRING(String),
NUMBER(i64),
FLOAT(f64), // 浮動小数点数サポート追加
TRUE,
FALSE,
// キーワード
BOX,
GLOBAL,
SINGLETON,
NEW,
IF,
ELSE,
LOOP,
BREAK,
RETURN,
FUNCTION,
PRINT,
THIS,
ME,
INIT, // init (初期化ブロック)
NOWAIT, // nowait
AWAIT, // await
INTERFACE, // interface
FROM, // from (継承用)
INCLUDE, // include (ファイル読み込み)
TRY, // try
CATCH, // catch
FINALLY, // finally
THROW, // throw
LOCAL, // local (一時変数宣言)
STATIC, // static (静的メソッド)
OUTBOX, // outbox (所有権移転変数)
NOT, // not (否定演算子)
// 演算子 (長いものから先に定義)
ARROW, // >>
EQUALS, // ==
NotEquals, // !=
LessEquals, // <=
GreaterEquals, // >=
AND, // && または and
OR, // || または or
LESS, // <
GREATER, // >
ASSIGN, // =
PLUS, // +
MINUS, // -
MULTIPLY, // *
DIVIDE, // /
// 記号
DOT, // .
LPAREN, // (
RPAREN, // )
LBRACE, // {
RBRACE, // }
COMMA, // ,
NEWLINE, // \n
// 識別子
IDENTIFIER(String),
// 特殊
EOF,
}
/// トークンの位置情報を含む構造体
#[derive(Debug, Clone)]
pub struct Token {
pub token_type: TokenType,
pub line: usize,
pub column: usize,
}
impl Token {
pub fn new(token_type: TokenType, line: usize, column: usize) -> Self {
Self { token_type, line, column }
}
}
/// トークナイズエラー
#[derive(Error, Debug)]
pub enum TokenizeError {
#[error("Unexpected character '{char}' at line {line}, column {column}")]
UnexpectedCharacter { char: char, line: usize, column: usize },
#[error("Unterminated string literal at line {line}")]
UnterminatedString { line: usize },
#[error("Invalid number format at line {line}")]
InvalidNumber { line: usize },
#[error("Comment not closed at line {line}")]
UnterminatedComment { line: usize },
}
/// Nyashトークナイザー
pub struct NyashTokenizer {
input: Vec<char>,
position: usize,
line: usize,
column: usize,
}
impl NyashTokenizer {
/// 新しいトークナイザーを作成
pub fn new(input: impl Into<String>) -> Self {
let input_string = input.into();
Self {
input: input_string.chars().collect(),
position: 0,
line: 1,
column: 1,
}
}
/// 完全なトークナイズを実行
pub fn tokenize(&mut self) -> Result<Vec<Token>, TokenizeError> {
let mut tokens = Vec::new();
while !self.is_at_end() {
// 空白をスキップ
self.skip_whitespace();
if self.is_at_end() {
break;
}
// 次のトークンを読み取り
let token = self.tokenize_next()?;
tokens.push(token);
}
// EOF トークンを追加
tokens.push(Token::new(TokenType::EOF, self.line, self.column));
Ok(tokens)
}
/// 次の一つのトークンを読み取り
fn tokenize_next(&mut self) -> Result<Token, TokenizeError> {
let start_line = self.line;
let start_column = self.column;
match self.current_char() {
Some('"') => {
let string_value = self.read_string()?;
Ok(Token::new(TokenType::STRING(string_value), start_line, start_column))
}
Some(c) if c.is_ascii_digit() => {
let token_type = self.read_numeric_literal()?;
Ok(Token::new(token_type, start_line, start_column))
}
Some(c) if c.is_alphabetic() || c == '_' => {
let token_type = self.read_keyword_or_identifier();
Ok(Token::new(token_type, start_line, start_column))
}
Some('/') if self.peek_char() == Some('/') => {
self.skip_line_comment();
self.skip_whitespace(); // コメント後の空白もスキップ
return self.tokenize_next();
}
Some('#') => {
self.skip_line_comment();
self.skip_whitespace(); // コメント後の空白もスキップ
return self.tokenize_next();
}
Some('>') if self.peek_char() == Some('>') => {
self.advance();
self.advance();
Ok(Token::new(TokenType::ARROW, start_line, start_column))
}
Some('=') if self.peek_char() == Some('=') => {
self.advance();
self.advance();
Ok(Token::new(TokenType::EQUALS, start_line, start_column))
}
Some('!') if self.peek_char() == Some('=') => {
self.advance();
self.advance();
Ok(Token::new(TokenType::NotEquals, start_line, start_column))
}
Some('<') if self.peek_char() == Some('=') => {
self.advance();
self.advance();
Ok(Token::new(TokenType::LessEquals, start_line, start_column))
}
Some('>') if self.peek_char() == Some('=') => {
self.advance();
self.advance();
Ok(Token::new(TokenType::GreaterEquals, start_line, start_column))
}
Some('&') if self.peek_char() == Some('&') => {
self.advance();
self.advance();
Ok(Token::new(TokenType::AND, start_line, start_column))
}
Some('|') if self.peek_char() == Some('|') => {
self.advance();
self.advance();
Ok(Token::new(TokenType::OR, start_line, start_column))
}
Some('<') => {
self.advance();
Ok(Token::new(TokenType::LESS, start_line, start_column))
}
Some('>') => {
self.advance();
Ok(Token::new(TokenType::GREATER, start_line, start_column))
}
Some('=') => {
self.advance();
Ok(Token::new(TokenType::ASSIGN, start_line, start_column))
}
Some('+') => {
self.advance();
Ok(Token::new(TokenType::PLUS, start_line, start_column))
}
Some('-') => {
self.advance();
Ok(Token::new(TokenType::MINUS, start_line, start_column))
}
Some('*') => {
self.advance();
Ok(Token::new(TokenType::MULTIPLY, start_line, start_column))
}
Some('/') => {
self.advance();
Ok(Token::new(TokenType::DIVIDE, start_line, start_column))
}
Some('.') => {
self.advance();
Ok(Token::new(TokenType::DOT, start_line, start_column))
}
Some('(') => {
self.advance();
Ok(Token::new(TokenType::LPAREN, start_line, start_column))
}
Some(')') => {
self.advance();
Ok(Token::new(TokenType::RPAREN, start_line, start_column))
}
Some('{') => {
self.advance();
Ok(Token::new(TokenType::LBRACE, start_line, start_column))
}
Some('}') => {
self.advance();
Ok(Token::new(TokenType::RBRACE, start_line, start_column))
}
Some(',') => {
self.advance();
Ok(Token::new(TokenType::COMMA, start_line, start_column))
}
Some('\n') => {
self.advance();
Ok(Token::new(TokenType::NEWLINE, start_line, start_column))
}
Some(c) => {
Err(TokenizeError::UnexpectedCharacter {
char: c,
line: self.line,
column: self.column,
})
}
None => {
Ok(Token::new(TokenType::EOF, self.line, self.column))
}
}
}
/// 文字列リテラルを読み取り
fn read_string(&mut self) -> Result<String, TokenizeError> {
let start_line = self.line;
self.advance(); // 開始の '"' をスキップ
let mut string_value = String::new();
while let Some(c) = self.current_char() {
if c == '"' {
self.advance(); // 終了の '"' をスキップ
return Ok(string_value);
}
// エスケープ文字の処理
if c == '\\' {
self.advance();
match self.current_char() {
Some('n') => string_value.push('\n'),
Some('t') => string_value.push('\t'),
Some('r') => string_value.push('\r'),
Some('\\') => string_value.push('\\'),
Some('"') => string_value.push('"'),
Some(c) => {
string_value.push('\\');
string_value.push(c);
}
None => break,
}
} else {
string_value.push(c);
}
self.advance();
}
Err(TokenizeError::UnterminatedString { line: start_line })
}
/// 数値リテラル(整数または浮動小数点数)を読み取り
fn read_numeric_literal(&mut self) -> Result<TokenType, TokenizeError> {
let start_line = self.line;
let mut number_str = String::new();
let mut has_dot = false;
// 整数部分を読み取り
while let Some(c) = self.current_char() {
if c.is_ascii_digit() {
number_str.push(c);
self.advance();
} else if c == '.' && !has_dot && self.peek_char().map_or(false, |ch| ch.is_ascii_digit()) {
// 小数点の後に数字が続く場合のみ受け入れる
has_dot = true;
number_str.push(c);
self.advance();
} else {
break;
}
}
if has_dot {
// 浮動小数点数として解析
number_str.parse::<f64>()
.map(TokenType::FLOAT)
.map_err(|_| TokenizeError::InvalidNumber { line: start_line })
} else {
// 整数として解析
number_str.parse::<i64>()
.map(TokenType::NUMBER)
.map_err(|_| TokenizeError::InvalidNumber { line: start_line })
}
}
/// キーワードまたは識別子を読み取り
fn read_keyword_or_identifier(&mut self) -> TokenType {
let mut identifier = String::new();
while let Some(c) = self.current_char() {
if c.is_alphanumeric() || c == '_' {
identifier.push(c);
self.advance();
} else {
break;
}
}
// キーワードチェック
match identifier.as_str() {
"box" => TokenType::BOX,
"global" => TokenType::GLOBAL,
"singleton" => TokenType::SINGLETON,
"new" => TokenType::NEW,
"if" => TokenType::IF,
"else" => TokenType::ELSE,
"loop" => TokenType::LOOP,
"break" => TokenType::BREAK,
"return" => TokenType::RETURN,
"function" => TokenType::FUNCTION,
"print" => TokenType::PRINT,
"this" => TokenType::THIS,
"me" => TokenType::ME,
"init" => TokenType::INIT,
"nowait" => TokenType::NOWAIT,
"await" => TokenType::AWAIT,
"interface" => TokenType::INTERFACE,
"from" => TokenType::FROM,
"include" => TokenType::INCLUDE,
"try" => TokenType::TRY,
"catch" => TokenType::CATCH,
"finally" => TokenType::FINALLY,
"throw" => TokenType::THROW,
"local" => TokenType::LOCAL,
"static" => TokenType::STATIC,
"outbox" => TokenType::OUTBOX,
"not" => TokenType::NOT,
"and" => TokenType::AND,
"or" => TokenType::OR,
"true" => TokenType::TRUE,
"false" => TokenType::FALSE,
_ => TokenType::IDENTIFIER(identifier),
}
}
/// 行コメントをスキップ
fn skip_line_comment(&mut self) {
while let Some(c) = self.current_char() {
if c == '\n' {
break; // 改行文字は消費せずに残す
}
self.advance();
}
}
/// 空白文字をスキップ(改行は除く)
fn skip_whitespace(&mut self) {
while let Some(c) = self.current_char() {
if c.is_whitespace() && c != '\n' {
self.advance();
} else {
break;
}
}
}
/// 現在の文字を取得
fn current_char(&self) -> Option<char> {
self.input.get(self.position).copied()
}
/// 次の文字を先読み
fn peek_char(&self) -> Option<char> {
self.input.get(self.position + 1).copied()
}
/// 位置を1つ進める
fn advance(&mut self) {
if let Some(c) = self.current_char() {
if c == '\n' {
self.line += 1;
self.column = 1;
} else {
self.column += 1;
}
self.position += 1;
}
}
/// 入力の終端に達したかチェック
fn is_at_end(&self) -> bool {
self.position >= self.input.len()
}
}
// ===== Tests =====
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_tokens() {
let mut tokenizer = NyashTokenizer::new("box new = + - *");
let tokens = tokenizer.tokenize().unwrap();
assert_eq!(tokens.len(), 7); // 6 tokens + EOF
assert_eq!(tokens[0].token_type, TokenType::BOX);
assert_eq!(tokens[1].token_type, TokenType::NEW);
assert_eq!(tokens[2].token_type, TokenType::ASSIGN);
assert_eq!(tokens[3].token_type, TokenType::PLUS);
assert_eq!(tokens[4].token_type, TokenType::MINUS);
assert_eq!(tokens[5].token_type, TokenType::MULTIPLY);
assert_eq!(tokens[6].token_type, TokenType::EOF);
}
#[test]
fn test_string_literal() {
let mut tokenizer = NyashTokenizer::new(r#""Hello, World!""#);
let tokens = tokenizer.tokenize().unwrap();
assert_eq!(tokens.len(), 2); // STRING + EOF
match &tokens[0].token_type {
TokenType::STRING(s) => assert_eq!(s, "Hello, World!"),
_ => panic!("Expected STRING token"),
}
}
#[test]
fn test_number_literal() {
let mut tokenizer = NyashTokenizer::new("42 123 0");
let tokens = tokenizer.tokenize().unwrap();
assert_eq!(tokens.len(), 4); // 3 numbers + EOF
match &tokens[0].token_type {
TokenType::NUMBER(n) => assert_eq!(*n, 42),
_ => panic!("Expected NUMBER token"),
}
match &tokens[1].token_type {
TokenType::NUMBER(n) => assert_eq!(*n, 123),
_ => panic!("Expected NUMBER token"),
}
match &tokens[2].token_type {
TokenType::NUMBER(n) => assert_eq!(*n, 0),
_ => panic!("Expected NUMBER token"),
}
}
#[test]
fn test_identifier() {
let mut tokenizer = NyashTokenizer::new("test_var myBox getValue");
let tokens = tokenizer.tokenize().unwrap();
assert_eq!(tokens.len(), 4); // 3 identifiers + EOF
match &tokens[0].token_type {
TokenType::IDENTIFIER(s) => assert_eq!(s, "test_var"),
_ => panic!("Expected IDENTIFIER token"),
}
match &tokens[1].token_type {
TokenType::IDENTIFIER(s) => assert_eq!(s, "myBox"),
_ => panic!("Expected IDENTIFIER token"),
}
match &tokens[2].token_type {
TokenType::IDENTIFIER(s) => assert_eq!(s, "getValue"),
_ => panic!("Expected IDENTIFIER token"),
}
}
#[test]
fn test_operators() {
let mut tokenizer = NyashTokenizer::new(">> == != <= >= < >");
let tokens = tokenizer.tokenize().unwrap();
assert_eq!(tokens[0].token_type, TokenType::ARROW);
assert_eq!(tokens[1].token_type, TokenType::EQUALS);
assert_eq!(tokens[2].token_type, TokenType::NotEquals);
assert_eq!(tokens[3].token_type, TokenType::LessEquals);
assert_eq!(tokens[4].token_type, TokenType::GreaterEquals);
assert_eq!(tokens[5].token_type, TokenType::LESS);
assert_eq!(tokens[6].token_type, TokenType::GREATER);
}
#[test]
fn test_complex_code() {
let code = r#"
box TestBox {
value
getValue() {
return this.value
}
}
obj = new TestBox()
obj.value = "test123"
"#;
let mut tokenizer = NyashTokenizer::new(code);
let tokens = tokenizer.tokenize().unwrap();
// 基本的なトークンがある事を確認
let token_types: Vec<_> = tokens.iter().map(|t| &t.token_type).collect();
assert!(token_types.contains(&&TokenType::BOX));
assert!(token_types.contains(&&TokenType::NEW));
assert!(token_types.contains(&&TokenType::THIS));
assert!(token_types.contains(&&TokenType::RETURN));
assert!(token_types.contains(&&TokenType::DOT));
}
#[test]
fn test_line_numbers() {
let code = "box\ntest\nvalue";
let mut tokenizer = NyashTokenizer::new(code);
let tokens = tokenizer.tokenize().unwrap();
assert_eq!(tokens[0].line, 1); // box
assert_eq!(tokens[1].line, 2); // test
assert_eq!(tokens[2].line, 3); // value
}
#[test]
fn test_comments() {
let code = r#"box Test // this is a comment
# this is also a comment
value"#;
let mut tokenizer = NyashTokenizer::new(code);
let tokens = tokenizer.tokenize().unwrap();
// コメントは除外されている
let token_types: Vec<_> = tokens.iter().map(|t| &t.token_type).collect();
assert_eq!(token_types.len(), 4); // box, Test, value, EOF
}
#[test]
fn test_error_handling() {
let mut tokenizer = NyashTokenizer::new("@#$%");
let result = tokenizer.tokenize();
assert!(result.is_err());
match result {
Err(TokenizeError::UnexpectedCharacter { char, line, column }) => {
assert_eq!(char, '@');
assert_eq!(line, 1);
assert_eq!(column, 1);
}
_ => panic!("Expected UnexpectedCharacter error"),
}
}
}

433
src/type_box.rs Normal file
View File

@ -0,0 +1,433 @@
/*!
* TypeBox - Everything is Box極限実現
*
* 型情報もBoxとして表現し、実行時型チェック、メタプログラミング、
* ジェネリクス基盤を提供する革命的システム
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox};
use std::collections::HashMap;
use std::sync::Arc;
use std::fmt::{Debug, Display};
use std::any::Any;
/// メソッドシグニチャ情報
#[derive(Debug, Clone)]
pub struct MethodSignature {
pub name: String,
pub parameters: Vec<String>,
pub parameter_types: Vec<Arc<TypeBox>>,
pub return_type: Arc<TypeBox>,
pub is_static: bool,
}
impl MethodSignature {
pub fn new(name: String, parameters: Vec<String>) -> Self {
Self {
name,
parameters,
parameter_types: Vec::new(),
return_type: Arc::new(TypeBox::void_type()),
is_static: false,
}
}
pub fn with_types(
name: String,
parameters: Vec<String>,
parameter_types: Vec<Arc<TypeBox>>,
return_type: Arc<TypeBox>
) -> Self {
Self {
name,
parameters,
parameter_types,
return_type,
is_static: false,
}
}
}
/// 🔥 TypeBox - 型情報をBoxとして表現
#[derive(Debug, Clone)]
pub struct TypeBox {
/// 型名
pub name: String,
/// フィールド情報 (field_name -> field_type)
pub fields: HashMap<String, Arc<TypeBox>>,
/// メソッドシグニチャ情報
pub methods: HashMap<String, MethodSignature>,
/// 親型(継承)
pub parent_type: Option<Arc<TypeBox>>,
/// ジェネリクス型パラメータ
pub type_parameters: Vec<String>,
/// インスタンス化された具体型(ジェネリクス用)
pub concrete_types: HashMap<String, Arc<TypeBox>>,
/// ビルトイン型かどうか
pub is_builtin: bool,
/// ユニークID
id: u64,
}
impl TypeBox {
/// 新しいTypeBoxを作成
pub fn new(name: &str) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self {
name: name.to_string(),
fields: HashMap::new(),
methods: HashMap::new(),
parent_type: None,
type_parameters: Vec::new(),
concrete_types: HashMap::new(),
is_builtin: false,
id,
}
}
/// ビルトイン型を作成
pub fn builtin(name: &str) -> Self {
let mut type_box = Self::new(name);
type_box.is_builtin = true;
type_box
}
/// フィールドを追加
pub fn add_field(&mut self, name: &str, field_type: Arc<TypeBox>) {
self.fields.insert(name.to_string(), field_type);
}
/// メソッドを追加
pub fn add_method(&mut self, method: MethodSignature) {
self.methods.insert(method.name.clone(), method);
}
/// 親型を設定
pub fn set_parent(&mut self, parent: Arc<TypeBox>) {
self.parent_type = Some(parent);
}
/// 型パラメータを追加
pub fn add_type_parameter(&mut self, param: String) {
self.type_parameters.push(param);
}
/// 具体型を設定(ジェネリクス用)
pub fn set_concrete_type(&mut self, param: &str, concrete_type: Arc<TypeBox>) {
self.concrete_types.insert(param.to_string(), concrete_type);
}
/// フィールドの型を取得
pub fn get_field_type(&self, field_name: &str) -> Option<Arc<TypeBox>> {
// 自分のフィールドをチェック
if let Some(field_type) = self.fields.get(field_name) {
return Some(Arc::clone(field_type));
}
// 親型のフィールドをチェック(継承)
if let Some(parent) = &self.parent_type {
return parent.get_field_type(field_name);
}
None
}
/// メソッドシグニチャを取得
pub fn get_method_signature(&self, method_name: &str) -> Option<&MethodSignature> {
// 自分のメソッドをチェック
if let Some(method) = self.methods.get(method_name) {
return Some(method);
}
// 親型のメソッドをチェック(継承)
if let Some(parent) = &self.parent_type {
return parent.get_method_signature(method_name);
}
None
}
/// 型互換性チェック
pub fn is_compatible_with(&self, other: &TypeBox) -> bool {
// 同じ型
if self.name == other.name {
return true;
}
// 継承チェック
if let Some(parent) = &self.parent_type {
if parent.is_compatible_with(other) {
return true;
}
}
false
}
/// ジェネリクス型かどうか
pub fn is_generic(&self) -> bool {
!self.type_parameters.is_empty()
}
/// 具体化されたジェネリクス型かどうか
pub fn is_concrete_generic(&self) -> bool {
!self.concrete_types.is_empty()
}
/// 型名を完全表示(ジェネリクス対応)
pub fn full_name(&self) -> String {
if self.concrete_types.is_empty() {
self.name.clone()
} else {
let mut result = self.name.clone();
result.push('<');
let concrete_names: Vec<String> = self.type_parameters.iter()
.map(|param| {
self.concrete_types.get(param)
.map(|t| t.name.clone())
.unwrap_or_else(|| param.clone())
})
.collect();
result.push_str(&concrete_names.join(", "));
result.push('>');
result
}
}
/// 基本型の定数
pub fn void_type() -> TypeBox {
TypeBox::builtin("Void")
}
pub fn string_type() -> TypeBox {
TypeBox::builtin("StringBox")
}
pub fn integer_type() -> TypeBox {
TypeBox::builtin("IntegerBox")
}
pub fn bool_type() -> TypeBox {
TypeBox::builtin("BoolBox")
}
pub fn array_type() -> TypeBox {
let mut type_box = TypeBox::builtin("ArrayBox");
type_box.add_type_parameter("T".to_string());
type_box
}
pub fn method_box_type() -> TypeBox {
let mut type_box = TypeBox::builtin("MethodBox");
type_box.add_type_parameter("T".to_string());
type_box
}
}
/// TypeBoxをNyashBoxとして実装
impl NyashBox for TypeBox {
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("<TypeBox: {}>", self.full_name()))
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_type) = other.as_any().downcast_ref::<TypeBox>() {
BoolBox::new(self.name == other_type.name)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"TypeBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for TypeBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<TypeBox: {}>", self.full_name())
}
}
/// TypeBoxレジストリ - グローバル型管理
#[derive(Debug)]
pub struct TypeRegistry {
/// 登録済み型
types: HashMap<String, Arc<TypeBox>>,
/// 継承チェーン情報(高速化用)
inheritance_chains: HashMap<String, Vec<String>>,
/// メソッドキャッシュ(将来の最適化用)
#[allow(dead_code)]
method_cache: HashMap<(String, String), MethodSignature>,
}
impl TypeRegistry {
/// 新しいTypeRegistryを作成
pub fn new() -> Self {
let mut registry = Self {
types: HashMap::new(),
inheritance_chains: HashMap::new(),
method_cache: HashMap::new(),
};
// ビルトイン型を登録
registry.register_builtin_types();
registry
}
/// ビルトイン型を登録
fn register_builtin_types(&mut self) {
self.register_type(Arc::new(TypeBox::void_type()));
self.register_type(Arc::new(TypeBox::string_type()));
self.register_type(Arc::new(TypeBox::integer_type()));
self.register_type(Arc::new(TypeBox::bool_type()));
self.register_type(Arc::new(TypeBox::array_type()));
self.register_type(Arc::new(TypeBox::method_box_type()));
}
/// 型を登録
pub fn register_type(&mut self, type_box: Arc<TypeBox>) {
let name = type_box.name.clone();
// 継承チェーンを構築
let mut chain = vec![name.clone()];
let mut current = &type_box.parent_type;
while let Some(parent) = current {
chain.push(parent.name.clone());
current = &parent.parent_type;
}
self.inheritance_chains.insert(name.clone(), chain);
self.types.insert(name, type_box);
}
/// 型を取得
pub fn get_type(&self, name: &str) -> Option<Arc<TypeBox>> {
self.types.get(name).map(Arc::clone)
}
/// 型互換性チェック
pub fn is_compatible(&self, from_type: &str, to_type: &str) -> bool {
if from_type == to_type {
return true;
}
if let Some(chain) = self.inheritance_chains.get(from_type) {
chain.contains(&to_type.to_string())
} else {
false
}
}
/// すべての型名を取得
pub fn get_all_type_names(&self) -> Vec<String> {
self.types.keys().cloned().collect()
}
/// ジェネリクス型をインスタンス化
pub fn instantiate_generic(&mut self, base_type: &str, concrete_types: &[&str]) -> Result<Arc<TypeBox>, String> {
let base = self.get_type(base_type)
.ok_or_else(|| format!("Base type '{}' not found", base_type))?;
if !base.is_generic() {
return Err(format!("Type '{}' is not generic", base_type));
}
if base.type_parameters.len() != concrete_types.len() {
return Err(format!(
"Generic type '{}' expects {} type parameters, got {}",
base_type, base.type_parameters.len(), concrete_types.len()
));
}
// 新しい具体化型を作成
let mut concrete_type = (*base).clone();
concrete_type.name = format!("{}_{}", base_type, concrete_types.join("_"));
concrete_type.concrete_types.clear();
// 具体型を設定
for (i, param) in base.type_parameters.iter().enumerate() {
let concrete = self.get_type(concrete_types[i])
.ok_or_else(|| format!("Concrete type '{}' not found", concrete_types[i]))?;
concrete_type.set_concrete_type(param, concrete);
}
let result = Arc::new(concrete_type);
// レジストリに登録
self.register_type(Arc::clone(&result));
Ok(result)
}
}
/// TypeBoxビルダー - 便利な構築関数
pub struct TypeBoxBuilder {
type_box: TypeBox,
}
impl TypeBoxBuilder {
/// 新しいビルダーを作成
pub fn new(name: &str) -> Self {
Self {
type_box: TypeBox::new(name),
}
}
/// フィールドを追加
pub fn field(mut self, name: &str, field_type: Arc<TypeBox>) -> Self {
self.type_box.add_field(name, field_type);
self
}
/// メソッドを追加
pub fn method(mut self, method: MethodSignature) -> Self {
self.type_box.add_method(method);
self
}
/// 親型を設定
pub fn parent(mut self, parent: Arc<TypeBox>) -> Self {
self.type_box.set_parent(parent);
self
}
/// 型パラメータを追加
pub fn type_param(mut self, param: &str) -> Self {
self.type_box.add_type_parameter(param.to_string());
self
}
/// TypeBoxを完成
pub fn build(self) -> TypeBox {
self.type_box
}
}

49
src/wasm_test.rs Normal file
View File

@ -0,0 +1,49 @@
#[cfg(target_arch = "wasm32")]
pub mod wasm_test {
use wasm_bindgen::prelude::*;
use web_sys::{window, HtmlCanvasElement, CanvasRenderingContext2d};
#[wasm_bindgen]
pub fn test_direct_canvas_draw() -> Result<(), JsValue> {
// Get window and document
let window = window().ok_or("no window")?;
let document = window.document().ok_or("no document")?;
// Get canvas element
let canvas = document
.get_element_by_id("test-canvas")
.ok_or("canvas not found")?
.dyn_into::<HtmlCanvasElement>()?;
// Set canvas size
canvas.set_width(400);
canvas.set_height(300);
// Get 2D context
let context = canvas
.get_context("2d")?
.ok_or("no 2d context")?
.dyn_into::<CanvasRenderingContext2d>()?;
// Draw black background
context.set_fill_style(&JsValue::from_str("black"));
context.fill_rect(0.0, 0.0, 400.0, 300.0);
// Draw red rectangle
context.set_fill_style(&JsValue::from_str("red"));
context.fill_rect(50.0, 50.0, 100.0, 80.0);
// Draw blue circle
context.set_fill_style(&JsValue::from_str("blue"));
context.begin_path();
context.arc(250.0, 100.0, 40.0, 0.0, 2.0 * std::f64::consts::PI)?;
context.fill();
// Draw text
context.set_font("20px Arial");
context.set_fill_style(&JsValue::from_str("white"));
context.fill_text("Hello Direct Canvas!", 100.0, 200.0)?;
Ok(())
}
}