feat: Implement field visibility (public/private) system
## Major Features Added
### Field Visibility System
- Added `private { ... }` and `public { ... }` blocks in box declarations
- Default visibility is now handled explicitly (fields must be in either block)
- Visibility checks enforced at both interpreter and VM levels
### Parser Enhancements
- Extended AST with public_fields and private_fields vectors
- Added parsing for visibility blocks in box definitions
- Maintained backward compatibility with existing `init { ... }` syntax
### Interpreter Implementation
- Added visibility checks in field access (get_field/set_field)
- External access to private fields now throws appropriate errors
- Internal access (within methods) always allowed
### VM Implementation
- Extended VM with object_class tracking for visibility checks
- RefGet/RefSet instructions now enforce field visibility
- Fixed nested box declaration collection (boxes defined inside methods)
### Test Examples Added
- docs/examples/visibility_ok.nyash - demonstrates correct usage
- docs/examples/visibility_error.nyash - tests private field access errors
## Technical Details
### Error Messages
- Interpreter: "Field 'X' is private in Y"
- VM: Same error message for consistency
### Current Limitations
- All RefGet/RefSet treated as external access in VM (internal flag future work)
- Legacy `init { ... }` fields treated as having unspecified visibility
## Test Results
✅ Interpreter: Both test cases pass correctly
✅ VM: Both test cases pass correctly after nested declaration fix
This implements the foundation for proper encapsulation in Nyash,
following the "explicit is better than implicit" philosophy.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -34,6 +34,13 @@ impl NyashInterpreter {
|
||||
}
|
||||
|
||||
|
||||
// 外からのフィールドアクセスか(me/this以外)を判定
|
||||
let is_internal_access = match object {
|
||||
ASTNode::This { .. } | ASTNode::Me { .. } => true,
|
||||
ASTNode::Variable { name, .. } if name == "me" => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// オブジェクトを評価(通常のフィールドアクセス)
|
||||
let obj_value = self.execute_expression(object);
|
||||
|
||||
@ -41,6 +48,20 @@ impl NyashInterpreter {
|
||||
|
||||
// InstanceBoxにキャスト
|
||||
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
|
||||
// 可視性チェック(互換性: public/privateのどちらかが定義されていれば強制)
|
||||
if !is_internal_access {
|
||||
let box_decls = self.shared.box_declarations.read().unwrap();
|
||||
if let Some(box_decl) = box_decls.get(&instance.class_name) {
|
||||
let has_visibility = !box_decl.public_fields.is_empty() || !box_decl.private_fields.is_empty();
|
||||
if has_visibility {
|
||||
if !box_decl.public_fields.contains(&field.to_string()) {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: format!("Field '{}' is private in {}", field, instance.class_name),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 🔥 finiは何回呼ばれてもエラーにしない(ユーザー要求)
|
||||
// is_finalized()チェックを削除
|
||||
|
||||
@ -145,4 +166,4 @@ impl NyashInterpreter {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user