Files
hakorune/src/parser/common.rs

207 lines
7.0 KiB
Rust
Raw Normal View History

/*!
* Parser Common Utilities
*
*
* Extracted from parser/mod.rs as part of modularization
*/
use super::ParseError;
use crate::ast::Span;
use crate::tokenizer::{Token, TokenType};
/// Parser utility methods
pub trait ParserUtils {
fn tokens(&self) -> &Vec<Token>;
fn current(&self) -> usize;
fn current_mut(&mut self) -> &mut usize;
/// 現在のトークンを取得
fn current_token(&self) -> &Token {
self.tokens().get(self.current()).unwrap_or(&Token {
token_type: TokenType::EOF,
line: 0,
column: 0,
})
}
/// 次のトークンを先読み(位置を進めない)
fn peek_token(&self) -> &TokenType {
if self.current() + 1 < self.tokens().len() {
&self.tokens()[self.current() + 1].token_type
} else {
&TokenType::EOF
}
}
/// N個先のトークンを先読み
#[allow(dead_code)]
fn peek_nth_token(&self, n: usize) -> &TokenType {
if self.current() + n < self.tokens().len() {
&self.tokens()[self.current() + n].token_type
} else {
&TokenType::EOF
}
}
/// 位置を1つ進める改行自動スキップ対応
fn advance(&mut self) {
if !self.is_at_end() {
// 現在のトークンで深度を更新(進める前)
self.update_depth_before_advance();
*self.current_mut() += 1;
// 新しいトークンで深度を更新(進めた後)
self.update_depth_after_advance();
// Phase 1: Smart advance - コンテキストに応じて改行を自動スキップ
if self.should_auto_skip_newlines() {
self.skip_newlines_internal();
}
}
}
/// advance前の深度更新閉じ括弧の処理
fn update_depth_before_advance(&mut self) {
// デフォルト実装は何もしないNyashParserでオーバーライド
}
/// advance後の深度更新開き括弧の処理
fn update_depth_after_advance(&mut self) {
// デフォルト実装は何もしないNyashParserでオーバーライド
}
/// 改行を自動スキップすべきか判定
fn should_auto_skip_newlines(&self) -> bool {
// 環境変数でSmart advanceを有効化
if std::env::var("NYASH_SMART_ADVANCE").ok().as_deref() != Some("1") {
return false;
}
// 現在のトークンがブレースやパーレンの後の場合
if self.current() > 0 {
let prev_token = &self.tokens()[self.current() - 1].token_type;
match prev_token {
TokenType::LBRACE | TokenType::LPAREN | TokenType::LBRACK => return true,
// 演算子の後(行継続)
TokenType::PLUS | TokenType::MINUS | TokenType::MULTIPLY |
TokenType::DIVIDE | TokenType::MODULO |
TokenType::AND | TokenType::OR |
TokenType::DOT | TokenType::DoubleColon |
TokenType::COMMA | TokenType::FatArrow => return true,
_ => {}
}
}
false
}
/// 内部用改行スキップ(再帰防止)
fn skip_newlines_internal(&mut self) {
let allow_sc = std::env::var("NYASH_PARSER_ALLOW_SEMICOLON").ok().map(|v| {
let lv = v.to_ascii_lowercase();
lv == "1" || lv == "true" || lv == "on"
}).unwrap_or(false);
while !self.is_at_end() {
let is_nl = matches!(self.current_token().token_type, TokenType::NEWLINE);
let is_sc = allow_sc && matches!(self.current_token().token_type, TokenType::SEMICOLON);
if is_nl || is_sc {
*self.current_mut() += 1; // advance()を使わず直接更新(再帰防止)
} else {
break;
}
}
}
/// NEWLINEトークンをスキップ
fn skip_newlines(&mut self) {
let allow_sc = std::env::var("NYASH_PARSER_ALLOW_SEMICOLON").ok().map(|v| {
let lv = v.to_ascii_lowercase();
lv == "1" || lv == "true" || lv == "on"
}).unwrap_or(false);
loop {
let is_nl = matches!(self.current_token().token_type, TokenType::NEWLINE);
let is_sc = allow_sc && matches!(self.current_token().token_type, TokenType::SEMICOLON);
if (is_nl || is_sc) && !self.is_at_end() {
self.advance();
continue;
}
break;
}
}
/// 指定されたトークンタイプを消費 (期待通りでなければエラー)
fn consume(&mut self, expected: TokenType) -> Result<Token, ParseError> {
if std::mem::discriminant(&self.current_token().token_type)
== std::mem::discriminant(&expected)
{
let token = self.current_token().clone();
self.advance();
Ok(token)
} else {
let line = self.current_token().line;
Err(ParseError::UnexpectedToken {
found: self.current_token().token_type.clone(),
expected: format!("{:?}", expected),
line,
})
}
}
/// 現在のトークンが指定されたタイプかチェック
fn match_token(&self, token_type: &TokenType) -> bool {
std::mem::discriminant(&self.current_token().token_type)
== std::mem::discriminant(token_type)
}
/// 複数のトークンタイプのいずれかにマッチするかチェック
#[allow(dead_code)]
fn match_any_token(&self, token_types: &[TokenType]) -> bool {
let current_discriminant = std::mem::discriminant(&self.current_token().token_type);
token_types
.iter()
.any(|tt| std::mem::discriminant(tt) == current_discriminant)
}
/// 終端に達したかチェック
fn is_at_end(&self) -> bool {
self.current() >= self.tokens().len()
|| matches!(self.current_token().token_type, TokenType::EOF)
}
/// 現在のトークンが行の終わりNEWLINE or EOFかチェック
#[allow(dead_code)]
fn is_line_end(&self) -> bool {
matches!(
self.current_token().token_type,
TokenType::NEWLINE | TokenType::EOF
)
}
/// エラー報告用の現在位置情報を取得
#[allow(dead_code)]
fn current_position(&self) -> (usize, usize) {
let token = self.current_token();
(token.line, token.column)
}
/// 現在のトークンからSpanを作成
#[allow(dead_code)]
fn current_span(&self) -> Span {
let token = self.current_token();
Span {
start: 0, // Token doesn't have byte offset, so using 0
end: 0,
line: token.line,
column: token.column,
}
}
}
/// Helper function to create unknown span
#[allow(dead_code)]
pub fn unknown_span() -> Span {
Span::unknown()
}