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:
Moe Charm
2025-08-21 03:08:13 +09:00
parent 2200312f09
commit 55777a0735
12 changed files with 208 additions and 201 deletions

View File

@ -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(),
})
}
}
}

View File

@ -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(),
})
}
}
}