feat: using ブレース均等解明完了&環境変数簡略化戦略策定
🎯 using ブレース均等の正体完全解明: - SeamInspector.report()のprelude_brace_delta計算を解析 - "static box Main {" より前のブレース{/}バランス検証 - usingシステムでファイル結合時の整合性チェック機能と判明 📝 環境変数地獄(8変数)の簡略化戦略策定: - Phase 1: NYASH_ENABLE_USING, NYASH_RESOLVE_FIX_BRACESデフォルト化 - Phase 2: PyVM/ny_plugins安定化後の段階的デフォルト化 - Phase 3: デバッグ変数のCLIフラグ化(--debug-pyvm等) - 理想形: 8変数→0変数(./target/release/nyash program.nyash) 🔧 skip_newlines()削除革命継続: - TokenCursor v2パーサー実装(nyash_parser_v2.rs新規) - 既存パーサー拡張版(parser_enhanced.rs) - Smart advance()とTokenCursorの協調実装 📚 次の課題: - 環境変数デフォルト化の段階的実装 - using systemの完全安定化 - codex協働でのデバッグ効率化 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
25
CLAUDE.md
25
CLAUDE.md
@ -205,6 +205,31 @@ NYASH_DISABLE_PLUGINS=1 NYASH_ENABLE_USING=1 NYASH_VM_USE_PY=1 ./target/release/
|
|||||||
|
|
||||||
**💡 覚え方**:迷ったら`NYASH_DISABLE_PLUGINS=1`から試す!
|
**💡 覚え方**:迷ったら`NYASH_DISABLE_PLUGINS=1`から試す!
|
||||||
|
|
||||||
|
### ⚠️ **using system環境変数地獄(要整理)**
|
||||||
|
|
||||||
|
**現状**: using関連テストに**8個**の環境変数が必要で複雑すぎる状況
|
||||||
|
```bash
|
||||||
|
# using混在スモークテスト用の環境変数地獄
|
||||||
|
NYASH_ENABLE_USING=1 # using構文有効化
|
||||||
|
NYASH_VM_USE_PY=1 # PyVM使用
|
||||||
|
NYASH_LOAD_NY_PLUGINS=1 # Nyプラグイン読み込み
|
||||||
|
NYASH_RESOLVE_FIX_BRACES=1 # ブレース修正
|
||||||
|
NYASH_PARSER_STATIC_INIT_STRICT=1 # パーサー厳格モード
|
||||||
|
NYASH_PYVM_DUMP_CODE=1 # PyVMコードダンプ
|
||||||
|
NYASH_RESOLVE_SEAM_DEBUG=1 # seam結合デバッグ
|
||||||
|
NYASH_RESOLVE_DEDUP_BOX=1 # 重複Box削除
|
||||||
|
```
|
||||||
|
|
||||||
|
**問題**:
|
||||||
|
- 🔥 認知負荷高すぎ(8個は覚えられない)
|
||||||
|
- 🔥 相互依存性不明(どれが必須?)
|
||||||
|
- 🔥 組み合わせ爆発(2^8 = 256通り)
|
||||||
|
- 🔥 デバッグ困難(どれが原因?)
|
||||||
|
|
||||||
|
**将来の簡略化案**:
|
||||||
|
- `NYASH_USING_PROFILE=dev|smoke|debug` でプロファイル化
|
||||||
|
- または `--using-mode=dev` CLIフラグで統合
|
||||||
|
|
||||||
## 🚀 よく使う実行コマンド(忘れやすい)
|
## 🚀 よく使う実行コマンド(忘れやすい)
|
||||||
|
|
||||||
### 🎯 基本実行方法
|
### 🎯 基本実行方法
|
||||||
|
|||||||
@ -34,7 +34,6 @@ pub(crate) fn parse_header(
|
|||||||
p.advance();
|
p.advance();
|
||||||
if p.match_token(&TokenType::COMMA) {
|
if p.match_token(&TokenType::COMMA) {
|
||||||
p.advance();
|
p.advance();
|
||||||
p.skip_newlines();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::UnexpectedToken {
|
return Err(ParseError::UnexpectedToken {
|
||||||
@ -66,7 +65,6 @@ pub(crate) fn parse_header(
|
|||||||
}
|
}
|
||||||
while p.match_token(&TokenType::COMMA) {
|
while p.match_token(&TokenType::COMMA) {
|
||||||
p.advance(); // consume ','
|
p.advance(); // consume ','
|
||||||
p.skip_newlines();
|
|
||||||
if let TokenType::IDENTIFIER(parent) = &p.current_token().token_type {
|
if let TokenType::IDENTIFIER(parent) = &p.current_token().token_type {
|
||||||
parents.push(parent.clone());
|
parents.push(parent.clone());
|
||||||
p.advance();
|
p.advance();
|
||||||
|
|||||||
@ -25,12 +25,10 @@ pub(crate) fn parse_interface_box(p: &mut NyashParser) -> Result<ASTNode, ParseE
|
|||||||
};
|
};
|
||||||
|
|
||||||
p.consume(TokenType::LBRACE)?;
|
p.consume(TokenType::LBRACE)?;
|
||||||
p.skip_newlines(); // ブレース後の改行をスキップ
|
|
||||||
|
|
||||||
let mut methods = HashMap::new();
|
let mut methods = HashMap::new();
|
||||||
|
|
||||||
while !p.match_token(&TokenType::RBRACE) && !p.is_at_end() {
|
while !p.match_token(&TokenType::RBRACE) && !p.is_at_end() {
|
||||||
p.skip_newlines(); // ループ開始時に改行をスキップ
|
|
||||||
if let TokenType::IDENTIFIER(method_name) = &p.current_token().token_type {
|
if let TokenType::IDENTIFIER(method_name) = &p.current_token().token_type {
|
||||||
let method_name = method_name.clone();
|
let method_name = method_name.clone();
|
||||||
p.advance();
|
p.advance();
|
||||||
@ -64,9 +62,6 @@ pub(crate) fn parse_interface_box(p: &mut NyashParser) -> Result<ASTNode, ParseE
|
|||||||
};
|
};
|
||||||
|
|
||||||
methods.insert(method_name, method_decl);
|
methods.insert(method_name, method_decl);
|
||||||
|
|
||||||
// メソッド宣言後の改行をスキップ
|
|
||||||
p.skip_newlines();
|
|
||||||
} else {
|
} else {
|
||||||
let line = p.current_token().line;
|
let line = p.current_token().line;
|
||||||
return Err(ParseError::UnexpectedToken {
|
return Err(ParseError::UnexpectedToken {
|
||||||
|
|||||||
@ -36,7 +36,6 @@ pub(crate) fn try_parse_constructor(
|
|||||||
}
|
}
|
||||||
p.consume(TokenType::RPAREN)?;
|
p.consume(TokenType::RPAREN)?;
|
||||||
let mut body = p.parse_block_statements()?;
|
let mut body = p.parse_block_statements()?;
|
||||||
p.skip_newlines();
|
|
||||||
// Optional postfix catch/cleanup (method-level gate)
|
// Optional postfix catch/cleanup (method-level gate)
|
||||||
if p.match_token(&TokenType::CATCH) || p.match_token(&TokenType::CLEANUP) {
|
if p.match_token(&TokenType::CATCH) || p.match_token(&TokenType::CLEANUP) {
|
||||||
let mut catch_clauses: Vec<crate::ast::CatchClause> = Vec::new();
|
let mut catch_clauses: Vec<crate::ast::CatchClause> = Vec::new();
|
||||||
@ -52,7 +51,6 @@ pub(crate) fn try_parse_constructor(
|
|||||||
body: catch_body,
|
body: catch_body,
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
});
|
});
|
||||||
p.skip_newlines();
|
|
||||||
if p.match_token(&TokenType::CATCH) {
|
if p.match_token(&TokenType::CATCH) {
|
||||||
let line = p.current_token().line;
|
let line = p.current_token().line;
|
||||||
return Err(ParseError::UnexpectedToken {
|
return Err(ParseError::UnexpectedToken {
|
||||||
|
|||||||
@ -30,7 +30,6 @@ pub(crate) fn wrap_with_optional_postfix(
|
|||||||
body: catch_body,
|
body: catch_body,
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
});
|
});
|
||||||
p.skip_newlines();
|
|
||||||
if p.match_token(&TokenType::CATCH) {
|
if p.match_token(&TokenType::CATCH) {
|
||||||
let line = p.current_token().line;
|
let line = p.current_token().line;
|
||||||
return Err(ParseError::UnexpectedToken {
|
return Err(ParseError::UnexpectedToken {
|
||||||
@ -78,7 +77,6 @@ pub(crate) fn try_parse_method_postfix_after_last_method(
|
|||||||
body: catch_body,
|
body: catch_body,
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
});
|
});
|
||||||
p.skip_newlines();
|
|
||||||
if p.match_token(&TokenType::CATCH) {
|
if p.match_token(&TokenType::CATCH) {
|
||||||
let line = p.current_token().line;
|
let line = p.current_token().line;
|
||||||
return Err(ParseError::UnexpectedToken {
|
return Err(ParseError::UnexpectedToken {
|
||||||
|
|||||||
@ -129,7 +129,6 @@ pub(crate) fn try_parse_block_first_property(
|
|||||||
}
|
}
|
||||||
// 1) Parse block body first
|
// 1) Parse block body first
|
||||||
let mut final_body = p.parse_block_statements()?;
|
let mut final_body = p.parse_block_statements()?;
|
||||||
p.skip_newlines();
|
|
||||||
|
|
||||||
// 2) Expect 'as'
|
// 2) Expect 'as'
|
||||||
if let TokenType::IDENTIFIER(kw) = &p.current_token().token_type {
|
if let TokenType::IDENTIFIER(kw) = &p.current_token().token_type {
|
||||||
|
|||||||
@ -297,7 +297,6 @@ impl NyashParser {
|
|||||||
self.consume(TokenType::LBRACE)?;
|
self.consume(TokenType::LBRACE)?;
|
||||||
let mut body: Vec<ASTNode> = Vec::new();
|
let mut body: Vec<ASTNode> = Vec::new();
|
||||||
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
|
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
|
||||||
self.skip_newlines();
|
|
||||||
if !self.match_token(&TokenType::RBRACE) {
|
if !self.match_token(&TokenType::RBRACE) {
|
||||||
body.push(self.parse_statement()?);
|
body.push(self.parse_statement()?);
|
||||||
}
|
}
|
||||||
|
|||||||
244
src/parser/nyash_parser_v2.rs
Normal file
244
src/parser/nyash_parser_v2.rs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
/*!
|
||||||
|
* NyashParser v2 - TokenCursorベースの新パーサー
|
||||||
|
*
|
||||||
|
* 改行処理を完全自動化した次世代パーサー
|
||||||
|
* skip_newlines()の手動呼び出しを完全排除
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::ast::{ASTNode, Span};
|
||||||
|
use crate::parser::cursor::{TokenCursor, NewlineMode};
|
||||||
|
use crate::parser::ParseError;
|
||||||
|
use crate::tokenizer::{Token, TokenType};
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
/// TokenCursorベースの新パーサー
|
||||||
|
pub struct NyashParserV2<'a> {
|
||||||
|
cursor: TokenCursor<'a>,
|
||||||
|
static_box_dependencies: HashMap<String, HashSet<String>>,
|
||||||
|
debug_fuel: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NyashParserV2<'a> {
|
||||||
|
/// 新しいパーサーを作成
|
||||||
|
pub fn new(tokens: &'a [Token]) -> Self {
|
||||||
|
Self {
|
||||||
|
cursor: TokenCursor::new(tokens),
|
||||||
|
static_box_dependencies: HashMap::new(),
|
||||||
|
debug_fuel: Some(100_000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// プログラムをパース(エントリーポイント)
|
||||||
|
pub fn parse_program(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
let mut statements = Vec::new();
|
||||||
|
|
||||||
|
// 文モードでパース(改行が文の区切り)
|
||||||
|
while !self.cursor.is_at_end() {
|
||||||
|
statements.push(self.parse_statement()?);
|
||||||
|
|
||||||
|
// 文の区切り(改行やセミコロン)は自動処理
|
||||||
|
while self.cursor.match_token(&TokenType::NEWLINE)
|
||||||
|
|| self.cursor.match_token(&TokenType::SEMICOLON) {
|
||||||
|
self.cursor.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ASTNode::Program {
|
||||||
|
statements,
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 文をパース
|
||||||
|
pub fn parse_statement(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
// 文モードで実行(改行を文の区切りとして扱う)
|
||||||
|
match &self.cursor.current().token_type {
|
||||||
|
TokenType::LOCAL => self.parse_local_declaration(),
|
||||||
|
TokenType::IF => self.parse_if_statement(),
|
||||||
|
TokenType::LOOP => self.parse_loop_statement(),
|
||||||
|
TokenType::RETURN => self.parse_return_statement(),
|
||||||
|
TokenType::BREAK => self.parse_break_statement(),
|
||||||
|
TokenType::CONTINUE => self.parse_continue_statement(),
|
||||||
|
_ => {
|
||||||
|
// 式文(代入や関数呼び出しなど)
|
||||||
|
self.parse_expression_statement()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 式をパース
|
||||||
|
pub fn parse_expression(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
// 式モードで実行(改行を自動的にスキップ)
|
||||||
|
self.cursor.with_expr_mode(|c| {
|
||||||
|
Self::parse_or_expression_internal(c)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// OR式をパース(内部実装)
|
||||||
|
fn parse_or_expression_internal(cursor: &mut TokenCursor) -> Result<ASTNode, ParseError> {
|
||||||
|
let mut left = Self::parse_and_expression_internal(cursor)?;
|
||||||
|
|
||||||
|
while cursor.match_token(&TokenType::OR) {
|
||||||
|
cursor.advance();
|
||||||
|
let right = Self::parse_and_expression_internal(cursor)?;
|
||||||
|
left = ASTNode::BinaryOp {
|
||||||
|
operator: crate::ast::BinaryOperator::Or,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(right),
|
||||||
|
span: Span::unknown(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AND式をパース(内部実装)
|
||||||
|
fn parse_and_expression_internal(cursor: &mut TokenCursor) -> Result<ASTNode, ParseError> {
|
||||||
|
let mut left = Self::parse_primary_expression_internal(cursor)?;
|
||||||
|
|
||||||
|
while cursor.match_token(&TokenType::AND) {
|
||||||
|
cursor.advance();
|
||||||
|
let right = Self::parse_primary_expression_internal(cursor)?;
|
||||||
|
left = ASTNode::BinaryOp {
|
||||||
|
operator: crate::ast::BinaryOperator::And,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(right),
|
||||||
|
span: Span::unknown(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// プライマリ式をパース(内部実装)
|
||||||
|
fn parse_primary_expression_internal(cursor: &mut TokenCursor) -> Result<ASTNode, ParseError> {
|
||||||
|
match &cursor.current().token_type.clone() {
|
||||||
|
TokenType::NUMBER(n) => {
|
||||||
|
let value = *n;
|
||||||
|
cursor.advance();
|
||||||
|
Ok(ASTNode::Literal {
|
||||||
|
value: crate::ast::LiteralValue::Integer(value),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenType::STRING(s) => {
|
||||||
|
let value = s.clone();
|
||||||
|
cursor.advance();
|
||||||
|
Ok(ASTNode::Literal {
|
||||||
|
value: crate::ast::LiteralValue::String(value),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenType::TRUE => {
|
||||||
|
cursor.advance();
|
||||||
|
Ok(ASTNode::Literal {
|
||||||
|
value: crate::ast::LiteralValue::Bool(true),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenType::FALSE => {
|
||||||
|
cursor.advance();
|
||||||
|
Ok(ASTNode::Literal {
|
||||||
|
value: crate::ast::LiteralValue::Bool(false),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenType::IDENTIFIER(name) => {
|
||||||
|
let name = name.clone();
|
||||||
|
cursor.advance();
|
||||||
|
Ok(ASTNode::Variable {
|
||||||
|
name,
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenType::LBRACE => {
|
||||||
|
// オブジェクトリテラル(改行は自動処理)
|
||||||
|
Self::parse_object_literal_internal(cursor)
|
||||||
|
}
|
||||||
|
TokenType::LPAREN => {
|
||||||
|
cursor.advance();
|
||||||
|
let expr = Self::parse_or_expression_internal(cursor)?;
|
||||||
|
cursor.consume(TokenType::RPAREN)?;
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let line = cursor.current().line;
|
||||||
|
Err(ParseError::InvalidExpression { line })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// オブジェクトリテラルをパース(改行完全自動化)
|
||||||
|
fn parse_object_literal_internal(cursor: &mut TokenCursor) -> Result<ASTNode, ParseError> {
|
||||||
|
cursor.consume(TokenType::LBRACE)?;
|
||||||
|
let mut entries = Vec::new();
|
||||||
|
|
||||||
|
// ブレース内は改行が自動的にスキップされる!
|
||||||
|
while !cursor.match_token(&TokenType::RBRACE) && !cursor.is_at_end() {
|
||||||
|
// キーをパース
|
||||||
|
let key = match &cursor.current().token_type {
|
||||||
|
TokenType::STRING(s) => {
|
||||||
|
let k = s.clone();
|
||||||
|
cursor.advance();
|
||||||
|
k
|
||||||
|
}
|
||||||
|
TokenType::IDENTIFIER(id) => {
|
||||||
|
let k = id.clone();
|
||||||
|
cursor.advance();
|
||||||
|
k
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let line = cursor.current().line;
|
||||||
|
return Err(ParseError::UnexpectedToken {
|
||||||
|
found: cursor.current().token_type.clone(),
|
||||||
|
expected: "string or identifier key".to_string(),
|
||||||
|
line,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cursor.consume(TokenType::COLON)?;
|
||||||
|
let value = Self::parse_or_expression_internal(cursor)?;
|
||||||
|
entries.push((key, value));
|
||||||
|
|
||||||
|
if cursor.match_token(&TokenType::COMMA) {
|
||||||
|
cursor.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.consume(TokenType::RBRACE)?;
|
||||||
|
Ok(ASTNode::MapLiteral {
|
||||||
|
entries,
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以下、各種文のパースメソッド(スタブ)
|
||||||
|
fn parse_local_declaration(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
todo!("local宣言のパース実装")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_if_statement(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
todo!("if文のパース実装")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_loop_statement(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
todo!("loop文のパース実装")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_return_statement(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
todo!("return文のパース実装")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_break_statement(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
todo!("break文のパース実装")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_continue_statement(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
todo!("continue文のパース実装")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expression_statement(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
self.parse_expression()
|
||||||
|
}
|
||||||
|
}
|
||||||
200
src/parser/parser_enhanced.rs
Normal file
200
src/parser/parser_enhanced.rs
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*!
|
||||||
|
* Parser Enhanced - 既存パーサーの改行処理自動化
|
||||||
|
*
|
||||||
|
* 既存のNyashParserを拡張し、advance()で自動的に改行をスキップ
|
||||||
|
* skip_newlines()の明示的呼び出しを不要にする
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::tokenizer::{Token, TokenType};
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
/// パーサーコンテキスト(改行処理のモード管理)
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum ParserContext {
|
||||||
|
/// 文コンテキスト(改行は文の区切り)
|
||||||
|
Statement,
|
||||||
|
/// 式コンテキスト(改行を自動スキップ)
|
||||||
|
Expression,
|
||||||
|
/// ブロック内(改行を自動スキップ)
|
||||||
|
Block,
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
/// 現在のパーサーコンテキスト
|
||||||
|
static PARSER_CONTEXT: Cell<ParserContext> = Cell::new(ParserContext::Statement);
|
||||||
|
|
||||||
|
/// 括弧の深度(自動改行スキップの判定用)
|
||||||
|
static PAREN_DEPTH: Cell<usize> = Cell::new(0);
|
||||||
|
static BRACE_DEPTH: Cell<usize> = Cell::new(0);
|
||||||
|
static BRACKET_DEPTH: Cell<usize> = Cell::new(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// コンテキストガード(RAIIパターンで自動復元)
|
||||||
|
pub struct ContextGuard {
|
||||||
|
prev_context: ParserContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ContextGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
PARSER_CONTEXT.with(|c| c.set(self.prev_context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 式コンテキストで実行
|
||||||
|
pub fn with_expr_context<F, T>(f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce() -> T,
|
||||||
|
{
|
||||||
|
let prev = PARSER_CONTEXT.with(|c| c.get());
|
||||||
|
PARSER_CONTEXT.with(|c| c.set(ParserContext::Expression));
|
||||||
|
let result = f();
|
||||||
|
PARSER_CONTEXT.with(|c| c.set(prev));
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ブロックコンテキストで実行
|
||||||
|
pub fn with_block_context<F, T>(f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce() -> T,
|
||||||
|
{
|
||||||
|
let prev = PARSER_CONTEXT.with(|c| c.get());
|
||||||
|
PARSER_CONTEXT.with(|c| c.set(ParserContext::Block));
|
||||||
|
let result = f();
|
||||||
|
PARSER_CONTEXT.with(|c| c.set(prev));
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 改行をスキップすべきか判定
|
||||||
|
pub fn should_skip_newlines() -> bool {
|
||||||
|
// 括弧内では常にスキップ
|
||||||
|
if PAREN_DEPTH.with(|d| d.get()) > 0
|
||||||
|
|| BRACE_DEPTH.with(|d| d.get()) > 0
|
||||||
|
|| BRACKET_DEPTH.with(|d| d.get()) > 0
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// コンテキストによる判定
|
||||||
|
match PARSER_CONTEXT.with(|c| c.get()) {
|
||||||
|
ParserContext::Expression | ParserContext::Block => true,
|
||||||
|
ParserContext::Statement => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// トークンタイプによる深度更新
|
||||||
|
pub fn update_depth(token_type: &TokenType, advancing: bool) {
|
||||||
|
match token_type {
|
||||||
|
TokenType::LPAREN => {
|
||||||
|
if advancing {
|
||||||
|
PAREN_DEPTH.with(|d| d.set(d.get() + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenType::RPAREN => {
|
||||||
|
if !advancing {
|
||||||
|
PAREN_DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenType::LBRACE => {
|
||||||
|
if advancing {
|
||||||
|
BRACE_DEPTH.with(|d| d.set(d.get() + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenType::RBRACE => {
|
||||||
|
if !advancing {
|
||||||
|
BRACE_DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenType::LBRACK => {
|
||||||
|
if advancing {
|
||||||
|
BRACKET_DEPTH.with(|d| d.set(d.get() + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenType::RBRACK => {
|
||||||
|
if !advancing {
|
||||||
|
BRACKET_DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 改良されたadvance実装(自動改行スキップ付き)
|
||||||
|
pub fn smart_advance(
|
||||||
|
tokens: &[Token],
|
||||||
|
current: &mut usize,
|
||||||
|
prev_token: Option<&TokenType>,
|
||||||
|
) {
|
||||||
|
if *current >= tokens.len() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 現在のトークンで深度を更新
|
||||||
|
let current_token = &tokens[*current].token_type;
|
||||||
|
update_depth(current_token, true);
|
||||||
|
|
||||||
|
// 位置を進める
|
||||||
|
*current += 1;
|
||||||
|
|
||||||
|
// 改行を自動的にスキップ
|
||||||
|
while *current < tokens.len() {
|
||||||
|
let token_type = &tokens[*current].token_type;
|
||||||
|
|
||||||
|
// 改行判定
|
||||||
|
if matches!(token_type, TokenType::NEWLINE) {
|
||||||
|
// スキップすべきか判定
|
||||||
|
if should_skip_newlines() || is_line_continuation(prev_token) {
|
||||||
|
*current += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// セミコロンも同様に処理
|
||||||
|
if matches!(token_type, TokenType::SEMICOLON) {
|
||||||
|
if std::env::var("NYASH_PARSER_ALLOW_SEMICOLON").ok().as_deref() == Some("1") {
|
||||||
|
if should_skip_newlines() {
|
||||||
|
*current += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 行継続判定(直前のトークンから判断)
|
||||||
|
fn is_line_continuation(prev_token: Option<&TokenType>) -> bool {
|
||||||
|
match prev_token {
|
||||||
|
Some(token) => matches!(
|
||||||
|
token,
|
||||||
|
TokenType::PLUS
|
||||||
|
| TokenType::MINUS
|
||||||
|
| TokenType::MULTIPLY
|
||||||
|
| TokenType::DIVIDE
|
||||||
|
| TokenType::MODULO
|
||||||
|
| TokenType::AND
|
||||||
|
| TokenType::OR
|
||||||
|
| TokenType::DOT
|
||||||
|
| TokenType::DoubleColon
|
||||||
|
| TokenType::COMMA
|
||||||
|
| TokenType::FatArrow
|
||||||
|
),
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 既存のParserUtilsトレイトを拡張
|
||||||
|
pub trait EnhancedParserUtils {
|
||||||
|
/// 改良版advance(改行自動処理)
|
||||||
|
fn advance_smart(&mut self);
|
||||||
|
|
||||||
|
/// 式コンテキストでパース
|
||||||
|
fn parse_in_expr_context<F, T>(&mut self, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> T;
|
||||||
|
|
||||||
|
/// ブロックコンテキストでパース
|
||||||
|
fn parse_in_block_context<F, T>(&mut self, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> T;
|
||||||
|
}
|
||||||
@ -17,9 +17,6 @@ impl NyashParser {
|
|||||||
// Parse the block body first
|
// Parse the block body first
|
||||||
let try_body = self.parse_block_statements()?;
|
let try_body = self.parse_block_statements()?;
|
||||||
|
|
||||||
// Allow whitespace/newlines between block and postfix keywords
|
|
||||||
self.skip_newlines();
|
|
||||||
|
|
||||||
if crate::config::env::block_postfix_catch()
|
if crate::config::env::block_postfix_catch()
|
||||||
&& (self.match_token(&TokenType::CATCH) || self.match_token(&TokenType::CLEANUP))
|
&& (self.match_token(&TokenType::CATCH) || self.match_token(&TokenType::CLEANUP))
|
||||||
{
|
{
|
||||||
@ -39,7 +36,6 @@ impl NyashParser {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Single‑catch policy (MVP): disallow multiple catch in postfix form
|
// Single‑catch policy (MVP): disallow multiple catch in postfix form
|
||||||
self.skip_newlines();
|
|
||||||
if self.match_token(&TokenType::CATCH) {
|
if self.match_token(&TokenType::CATCH) {
|
||||||
let line = self.current_token().line;
|
let line = self.current_token().line;
|
||||||
return Err(ParseError::UnexpectedToken {
|
return Err(ParseError::UnexpectedToken {
|
||||||
@ -88,7 +84,6 @@ impl NyashParser {
|
|||||||
self.consume(TokenType::LBRACE)?;
|
self.consume(TokenType::LBRACE)?;
|
||||||
let mut body = Vec::new();
|
let mut body = Vec::new();
|
||||||
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
|
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
|
||||||
self.skip_newlines();
|
|
||||||
if !self.match_token(&TokenType::RBRACE) {
|
if !self.match_token(&TokenType::RBRACE) {
|
||||||
body.push(self.parse_statement()?);
|
body.push(self.parse_statement()?);
|
||||||
}
|
}
|
||||||
@ -473,8 +468,6 @@ impl NyashParser {
|
|||||||
/// return文をパース
|
/// return文をパース
|
||||||
pub(super) fn parse_return(&mut self) -> Result<ASTNode, ParseError> {
|
pub(super) fn parse_return(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
self.advance(); // consume 'return'
|
self.advance(); // consume 'return'
|
||||||
// 許容: 改行をスキップしてから式有無を判定
|
|
||||||
self.skip_newlines();
|
|
||||||
// returnの後に式があるかチェック(RBRACE/EOFなら値なし)
|
// returnの後に式があるかチェック(RBRACE/EOFなら値なし)
|
||||||
let value = if self.is_at_end() || self.match_token(&TokenType::RBRACE) {
|
let value = if self.is_at_end() || self.match_token(&TokenType::RBRACE) {
|
||||||
None
|
None
|
||||||
|
|||||||
Reference in New Issue
Block a user