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:
@ -137,6 +137,8 @@ impl NyashParser {
|
||||
|
||||
let mut fields = Vec::new();
|
||||
let mut methods = HashMap::new();
|
||||
let mut public_fields: Vec<String> = Vec::new();
|
||||
let mut private_fields: Vec<String> = Vec::new();
|
||||
let mut constructors = HashMap::new();
|
||||
let mut init_fields = Vec::new();
|
||||
let mut weak_fields = Vec::new(); // 🔗 Track weak fields
|
||||
@ -404,6 +406,35 @@ impl NyashParser {
|
||||
if let TokenType::IDENTIFIER(field_or_method) = &self.current_token().token_type {
|
||||
let field_or_method = field_or_method.clone();
|
||||
self.advance();
|
||||
|
||||
// 可視性ブロック: public { ... } / private { ... }
|
||||
if field_or_method == "public" || field_or_method == "private" {
|
||||
self.consume(TokenType::LBRACE)?;
|
||||
self.skip_newlines();
|
||||
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
|
||||
if let TokenType::IDENTIFIER(fname) = &self.current_token().token_type {
|
||||
let fname = fname.clone();
|
||||
// ブロックに追加
|
||||
if field_or_method == "public" { public_fields.push(fname.clone()); } else { private_fields.push(fname.clone()); }
|
||||
// 互換性のため、全体fieldsにも追加
|
||||
fields.push(fname);
|
||||
self.advance();
|
||||
// カンマ/改行をスキップ
|
||||
if self.match_token(&TokenType::COMMA) { self.advance(); }
|
||||
self.skip_newlines();
|
||||
continue;
|
||||
}
|
||||
// 予期しないトークン
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
expected: "identifier in visibility block".to_string(),
|
||||
found: self.current_token().token_type.clone(),
|
||||
line: self.current_token().line,
|
||||
});
|
||||
}
|
||||
self.consume(TokenType::RBRACE)?;
|
||||
self.skip_newlines();
|
||||
continue;
|
||||
}
|
||||
|
||||
// メソッドかフィールドかを判定
|
||||
if self.match_token(&TokenType::LPAREN) {
|
||||
@ -473,6 +504,8 @@ impl NyashParser {
|
||||
Ok(ASTNode::BoxDeclaration {
|
||||
name,
|
||||
fields,
|
||||
public_fields,
|
||||
private_fields,
|
||||
methods,
|
||||
constructors,
|
||||
init_fields,
|
||||
@ -571,6 +604,8 @@ impl NyashParser {
|
||||
Ok(ASTNode::BoxDeclaration {
|
||||
name,
|
||||
fields: vec![], // インターフェースはフィールドなし
|
||||
public_fields: vec![],
|
||||
private_fields: vec![],
|
||||
methods,
|
||||
constructors: HashMap::new(), // インターフェースにコンストラクタなし
|
||||
init_fields: vec![], // インターフェースにinitブロックなし
|
||||
@ -584,4 +619,4 @@ impl NyashParser {
|
||||
span: Span::unknown(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,6 +273,8 @@ impl NyashParser {
|
||||
Ok(ASTNode::BoxDeclaration {
|
||||
name,
|
||||
fields,
|
||||
public_fields: vec![],
|
||||
private_fields: vec![],
|
||||
methods,
|
||||
constructors,
|
||||
init_fields,
|
||||
@ -286,4 +288,4 @@ impl NyashParser {
|
||||
span: Span::unknown(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user