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`から試す!
|
||||
|
||||
### ⚠️ **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();
|
||||
if p.match_token(&TokenType::COMMA) {
|
||||
p.advance();
|
||||
p.skip_newlines();
|
||||
}
|
||||
} else {
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
@ -66,7 +65,6 @@ pub(crate) fn parse_header(
|
||||
}
|
||||
while p.match_token(&TokenType::COMMA) {
|
||||
p.advance(); // consume ','
|
||||
p.skip_newlines();
|
||||
if let TokenType::IDENTIFIER(parent) = &p.current_token().token_type {
|
||||
parents.push(parent.clone());
|
||||
p.advance();
|
||||
|
||||
@ -25,12 +25,10 @@ pub(crate) fn parse_interface_box(p: &mut NyashParser) -> Result<ASTNode, ParseE
|
||||
};
|
||||
|
||||
p.consume(TokenType::LBRACE)?;
|
||||
p.skip_newlines(); // ブレース後の改行をスキップ
|
||||
|
||||
let mut methods = HashMap::new();
|
||||
|
||||
while !p.match_token(&TokenType::RBRACE) && !p.is_at_end() {
|
||||
p.skip_newlines(); // ループ開始時に改行をスキップ
|
||||
if let TokenType::IDENTIFIER(method_name) = &p.current_token().token_type {
|
||||
let method_name = method_name.clone();
|
||||
p.advance();
|
||||
@ -64,9 +62,6 @@ pub(crate) fn parse_interface_box(p: &mut NyashParser) -> Result<ASTNode, ParseE
|
||||
};
|
||||
|
||||
methods.insert(method_name, method_decl);
|
||||
|
||||
// メソッド宣言後の改行をスキップ
|
||||
p.skip_newlines();
|
||||
} else {
|
||||
let line = p.current_token().line;
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
|
||||
@ -36,7 +36,6 @@ pub(crate) fn try_parse_constructor(
|
||||
}
|
||||
p.consume(TokenType::RPAREN)?;
|
||||
let mut body = p.parse_block_statements()?;
|
||||
p.skip_newlines();
|
||||
// Optional postfix catch/cleanup (method-level gate)
|
||||
if p.match_token(&TokenType::CATCH) || p.match_token(&TokenType::CLEANUP) {
|
||||
let mut catch_clauses: Vec<crate::ast::CatchClause> = Vec::new();
|
||||
@ -52,7 +51,6 @@ pub(crate) fn try_parse_constructor(
|
||||
body: catch_body,
|
||||
span: Span::unknown(),
|
||||
});
|
||||
p.skip_newlines();
|
||||
if p.match_token(&TokenType::CATCH) {
|
||||
let line = p.current_token().line;
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
|
||||
@ -30,7 +30,6 @@ pub(crate) fn wrap_with_optional_postfix(
|
||||
body: catch_body,
|
||||
span: Span::unknown(),
|
||||
});
|
||||
p.skip_newlines();
|
||||
if p.match_token(&TokenType::CATCH) {
|
||||
let line = p.current_token().line;
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
@ -78,7 +77,6 @@ pub(crate) fn try_parse_method_postfix_after_last_method(
|
||||
body: catch_body,
|
||||
span: Span::unknown(),
|
||||
});
|
||||
p.skip_newlines();
|
||||
if p.match_token(&TokenType::CATCH) {
|
||||
let line = p.current_token().line;
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
|
||||
@ -129,7 +129,6 @@ pub(crate) fn try_parse_block_first_property(
|
||||
}
|
||||
// 1) Parse block body first
|
||||
let mut final_body = p.parse_block_statements()?;
|
||||
p.skip_newlines();
|
||||
|
||||
// 2) Expect 'as'
|
||||
if let TokenType::IDENTIFIER(kw) = &p.current_token().token_type {
|
||||
|
||||
@ -297,7 +297,6 @@ impl NyashParser {
|
||||
self.consume(TokenType::LBRACE)?;
|
||||
let mut body: Vec<ASTNode> = 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()?);
|
||||
}
|
||||
|
||||
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
|
||||
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()
|
||||
&& (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
|
||||
self.skip_newlines();
|
||||
if self.match_token(&TokenType::CATCH) {
|
||||
let line = self.current_token().line;
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
@ -88,7 +84,6 @@ impl NyashParser {
|
||||
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()?);
|
||||
}
|
||||
@ -473,8 +468,6 @@ impl NyashParser {
|
||||
/// return文をパース
|
||||
pub(super) fn parse_return(&mut self) -> Result<ASTNode, ParseError> {
|
||||
self.advance(); // consume 'return'
|
||||
// 許容: 改行をスキップしてから式有無を判定
|
||||
self.skip_newlines();
|
||||
// returnの後に式があるかチェック(RBRACE/EOFなら値なし)
|
||||
let value = if self.is_at_end() || self.match_token(&TokenType::RBRACE) {
|
||||
None
|
||||
|
||||
Reference in New Issue
Block a user