feat: 改行処理Phase 1 TokenCursor基本実装完了
- src/parser/cursor.rs: TokenCursor本体実装(230行) - NewlineMode(Stmt/Expr)による文脈認識改行処理 - ブレース/パーレン/ブラケット深度の自動追跡 - 行継続判定(演算子・カンマ等) - with_expr_mode/with_stmt_mode によるモード切替 - src/parser/expr_cursor.rs: TokenCursor版式パーサー(250行) - 実験的実装として式パーサーを TokenCursor対応 - 二項演算子・比較・プライマリ式・オブジェクトリテラル対応 - ビルド成功(warning のみ、エラーなし) - CLAUDE.md更新: Phase 1実装内容を記載 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
25
CLAUDE.md
25
CLAUDE.md
@ -397,20 +397,25 @@ jq '.functions[0].blocks' mir.json # ブロック構造確認
|
|||||||
- 🗃️ **アーカイブ整理**: 古いphaseファイル群をarchiveに移動、導線クリーンアップ完了
|
- 🗃️ **アーカイブ整理**: 古いphaseファイル群をarchiveに移動、導線クリーンアップ完了
|
||||||
- 📋 詳細: [Property System仕様](docs/proposals/unified-members.md) | [Python統合計画](docs/development/roadmap/phases/phase-10.7/)
|
- 📋 詳細: [Property System仕様](docs/proposals/unified-members.md) | [Python統合計画](docs/development/roadmap/phases/phase-10.7/)
|
||||||
|
|
||||||
## 📝 Update (2025-09-24) ✅ 改行処理Phase 0 Quick Fix完了!
|
## 📝 Update (2025-09-24) ✅ 改行処理Phase 0 & Phase 1基本実装完了!
|
||||||
- ✅ **複数行match式パース成功!** たった5箇所の修正で複数行オブジェクトリテラル完全動作
|
- ✅ **Phase 0 Quick Fix完了!** たった5箇所の修正で複数行オブジェクトリテラル完全動作
|
||||||
- **修正箇所**:
|
- `primary.rs`: COLON前後とCOMMA判定前にskip_newlines()追加(3箇所)
|
||||||
1. `primary.rs`: COLON前後とCOMMA判定前にskip_newlines()追加(3箇所)
|
- `match_expr.rs`: is_object_literal()関数を改行対応(lookahead改良)
|
||||||
2. `match_expr.rs`: is_object_literal()関数を改行対応(lookahead改良)
|
|
||||||
- **必須環境変数**: `NYASH_SYNTAX_SUGAR_LEVEL=full`(IDENTIFIERキー使用のため)
|
- **必須環境変数**: `NYASH_SYNTAX_SUGAR_LEVEL=full`(IDENTIFIERキー使用のため)
|
||||||
- **テスト結果**: MapBox正常出力 `{'__box__': 'MapBox', '__map': {'value': 42, 'name': 'answer'}}`
|
- **テスト結果**: MapBox正常出力 `{'__box__': 'MapBox', '__map': {'value': 42, 'name': 'answer'}}`
|
||||||
|
- 🎯 **Phase 1 TokenCursor基本実装完了!** 改行処理を一元管理する仕組み構築
|
||||||
|
- **新規実装ファイル**:
|
||||||
|
1. `src/parser/cursor.rs`: TokenCursor本体(230行)- モード制御・深度追跡・自動改行処理
|
||||||
|
2. `src/parser/expr_cursor.rs`: TokenCursor版式パーサー(250行)- 実験的実装
|
||||||
|
- **主要機能**:
|
||||||
|
- NewlineMode(Stmt/Expr)による文脈認識改行処理
|
||||||
|
- ブレース/パーレン/ブラケット深度の自動追跡
|
||||||
|
- 行継続判定(演算子・カンマ等)
|
||||||
|
- with_expr_mode/with_stmt_mode によるモード一時切り替え
|
||||||
|
- **ビルド成功**: warning のみでエラーなし
|
||||||
- 🎯 **セミコロンモード確認完了!** `NYASH_PARSER_ALLOW_SEMICOLON=1`で動作確認
|
- 🎯 **セミコロンモード確認完了!** `NYASH_PARSER_ALLOW_SEMICOLON=1`で動作確認
|
||||||
- セミコロンと改行を自由に混在可能
|
|
||||||
- JavaScript/Go風の書き方も完全サポート
|
|
||||||
- 📚 **改行処理戦略ドキュメント完成**: [newline-handling-strategy.md](docs/development/strategies/newline-handling-strategy.md)
|
- 📚 **改行処理戦略ドキュメント完成**: [newline-handling-strategy.md](docs/development/strategies/newline-handling-strategy.md)
|
||||||
- 3段階実装計画(Phase 0: Quick Fix ✅, Phase 1: TokenCursor, Phase 2: LASI)
|
- 🚀 **次の実装**: Phase 2 LASI前処理でトークン正規化(Phase 15後)
|
||||||
- ChatGPT Pro提案のTokenCursorサンプルコード含む
|
|
||||||
- 🚀 **次の実装**: Phase 1 TokenCursor導入で改行処理を根本解決へ
|
|
||||||
|
|
||||||
## 📝 Update (2025-09-23) ✅ フェーズS実装完了!break制御フロー根治開始
|
## 📝 Update (2025-09-23) ✅ フェーズS実装完了!break制御フロー根治開始
|
||||||
- ✅ **フェーズS完了!** PHI incoming修正+終端ガード徹底→重複処理4箇所統一
|
- ✅ **フェーズS完了!** PHI incoming修正+終端ガード徹底→重複処理4箇所統一
|
||||||
|
|||||||
297
src/parser/cursor.rs
Normal file
297
src/parser/cursor.rs
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
use crate::tokenizer::{Token, TokenType};
|
||||||
|
|
||||||
|
/// トークンカーソル - 改行処理を一元管理
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TokenCursor<'a> {
|
||||||
|
tokens: &'a [Token],
|
||||||
|
idx: usize,
|
||||||
|
mode: NewlineMode,
|
||||||
|
paren_depth: usize, // ()
|
||||||
|
brace_depth: usize, // {}
|
||||||
|
bracket_depth: usize, // []
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 改行処理モード
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum NewlineMode {
|
||||||
|
/// 文モード:改行は文の区切り
|
||||||
|
Stmt,
|
||||||
|
/// 式モード:改行を自動スキップ
|
||||||
|
Expr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TokenCursor<'a> {
|
||||||
|
/// 新しいTokenCursorを作成
|
||||||
|
pub fn new(tokens: &'a [Token]) -> Self {
|
||||||
|
Self {
|
||||||
|
tokens,
|
||||||
|
idx: 0,
|
||||||
|
mode: NewlineMode::Stmt,
|
||||||
|
paren_depth: 0,
|
||||||
|
brace_depth: 0,
|
||||||
|
bracket_depth: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 現在のトークンを取得
|
||||||
|
pub fn current(&self) -> &Token {
|
||||||
|
self.tokens.get(self.idx).unwrap_or(&Token {
|
||||||
|
token_type: TokenType::EOF,
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 次のトークンをピーク
|
||||||
|
pub fn peek(&self) -> &Token {
|
||||||
|
self.tokens.get(self.idx + 1).unwrap_or(&Token {
|
||||||
|
token_type: TokenType::EOF,
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// N番目のトークンをピーク
|
||||||
|
pub fn peek_nth(&self, n: usize) -> &Token {
|
||||||
|
self.tokens.get(self.idx + n).unwrap_or(&Token {
|
||||||
|
token_type: TokenType::EOF,
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 次のトークンに進む(改行を考慮)
|
||||||
|
pub fn advance(&mut self) {
|
||||||
|
if self.idx < self.tokens.len() {
|
||||||
|
// 深度を更新
|
||||||
|
match &self.tokens[self.idx].token_type {
|
||||||
|
TokenType::LPAREN => self.paren_depth += 1,
|
||||||
|
TokenType::RPAREN => self.paren_depth = self.paren_depth.saturating_sub(1),
|
||||||
|
TokenType::LBRACE => self.brace_depth += 1,
|
||||||
|
TokenType::RBRACE => self.brace_depth = self.brace_depth.saturating_sub(1),
|
||||||
|
TokenType::LBRACK => self.bracket_depth += 1,
|
||||||
|
TokenType::RBRACK => self.bracket_depth = self.bracket_depth.saturating_sub(1),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.idx += 1;
|
||||||
|
|
||||||
|
// 改行を自動的にスキップするかチェック
|
||||||
|
while self.should_skip_newline() && self.idx < self.tokens.len() {
|
||||||
|
if matches!(self.tokens[self.idx].token_type, TokenType::NEWLINE) {
|
||||||
|
self.idx += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 明示的に改行をスキップ
|
||||||
|
pub fn skip_newlines(&mut self) {
|
||||||
|
while self.idx < self.tokens.len()
|
||||||
|
&& matches!(self.tokens[self.idx].token_type, TokenType::NEWLINE) {
|
||||||
|
self.idx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// トークンが期待した型かチェック
|
||||||
|
pub fn match_token(&self, token_type: &TokenType) -> bool {
|
||||||
|
std::mem::discriminant(&self.current().token_type) == std::mem::discriminant(token_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 期待したトークンを消費
|
||||||
|
pub fn consume(&mut self, expected: TokenType) -> Result<(), crate::parser::ParseError> {
|
||||||
|
if self.match_token(&expected) {
|
||||||
|
self.advance();
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(crate::parser::ParseError::UnexpectedToken {
|
||||||
|
found: self.current().token_type.clone(),
|
||||||
|
expected: format!("{:?}", expected),
|
||||||
|
line: self.current().line,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ファイル終端かチェック
|
||||||
|
pub fn is_at_end(&self) -> bool {
|
||||||
|
matches!(self.current().token_type, TokenType::EOF)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 式モードで一時的に実行
|
||||||
|
pub fn with_expr_mode<F, T>(&mut self, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> T,
|
||||||
|
{
|
||||||
|
let old_mode = self.mode;
|
||||||
|
self.mode = NewlineMode::Expr;
|
||||||
|
let result = f(self);
|
||||||
|
self.mode = old_mode;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 文モードで一時的に実行
|
||||||
|
pub fn with_stmt_mode<F, T>(&mut self, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> T,
|
||||||
|
{
|
||||||
|
let old_mode = self.mode;
|
||||||
|
self.mode = NewlineMode::Stmt;
|
||||||
|
let result = f(self);
|
||||||
|
self.mode = old_mode;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 改行をスキップすべきか判定
|
||||||
|
fn should_skip_newline(&self) -> bool {
|
||||||
|
// ブレース/パーレン/ブラケット内では常にスキップ
|
||||||
|
if self.brace_depth > 0 || self.paren_depth > 0 || self.bracket_depth > 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 式モードでは改行をスキップ
|
||||||
|
if self.mode == NewlineMode::Expr {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 行継続判定(直前のトークンを見る)
|
||||||
|
if self.prev_is_line_continuation() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 直前のトークンが行継続を示すか判定
|
||||||
|
fn prev_is_line_continuation(&self) -> bool {
|
||||||
|
if self.idx == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &self.tokens[self.idx - 1].token_type {
|
||||||
|
// 二項演算子
|
||||||
|
TokenType::PLUS | TokenType::MINUS | TokenType::MULTIPLY | TokenType::DIVIDE |
|
||||||
|
TokenType::MODULO | TokenType::AND | TokenType::OR |
|
||||||
|
TokenType::BitOr | TokenType::BitAnd | TokenType::BitXor |
|
||||||
|
// メンバアクセス
|
||||||
|
TokenType::DOT | TokenType::DoubleColon |
|
||||||
|
// Optional系
|
||||||
|
TokenType::QUESTION |
|
||||||
|
// Arrow
|
||||||
|
TokenType::FatArrow |
|
||||||
|
// カンマ
|
||||||
|
TokenType::COMMA => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 現在の位置を取得
|
||||||
|
pub fn position(&self) -> usize {
|
||||||
|
self.idx
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 位置を設定(バックトラック用)
|
||||||
|
pub fn set_position(&mut self, pos: usize) {
|
||||||
|
if pos <= self.tokens.len() {
|
||||||
|
self.idx = pos;
|
||||||
|
// 深度を再計算
|
||||||
|
self.recalculate_depths();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 深度を再計算
|
||||||
|
fn recalculate_depths(&mut self) {
|
||||||
|
self.paren_depth = 0;
|
||||||
|
self.brace_depth = 0;
|
||||||
|
self.bracket_depth = 0;
|
||||||
|
|
||||||
|
for i in 0..self.idx {
|
||||||
|
match &self.tokens[i].token_type {
|
||||||
|
TokenType::LPAREN => self.paren_depth += 1,
|
||||||
|
TokenType::RPAREN => self.paren_depth = self.paren_depth.saturating_sub(1),
|
||||||
|
TokenType::LBRACE => self.brace_depth += 1,
|
||||||
|
TokenType::RBRACE => self.brace_depth = self.brace_depth.saturating_sub(1),
|
||||||
|
TokenType::LBRACK => self.bracket_depth += 1,
|
||||||
|
TokenType::RBRACK => self.bracket_depth = self.bracket_depth.saturating_sub(1),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// モードを取得
|
||||||
|
pub fn get_mode(&self) -> NewlineMode {
|
||||||
|
self.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
/// モードを設定
|
||||||
|
pub fn set_mode(&mut self, mode: NewlineMode) {
|
||||||
|
self.mode = mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_basic_cursor_operations() {
|
||||||
|
let tokens = vec![
|
||||||
|
Token { token_type: TokenType::LOCAL, line: 1, column: 1 },
|
||||||
|
Token { token_type: TokenType::IDENTIFIER("x".to_string()), line: 1, column: 7 },
|
||||||
|
Token { token_type: TokenType::ASSIGN, line: 1, column: 9 },
|
||||||
|
Token { token_type: TokenType::NUMBER(42), line: 1, column: 11 },
|
||||||
|
Token { token_type: TokenType::EOF, line: 1, column: 13 },
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut cursor = TokenCursor::new(&tokens);
|
||||||
|
|
||||||
|
assert!(cursor.match_token(&TokenType::LOCAL));
|
||||||
|
cursor.advance();
|
||||||
|
|
||||||
|
assert!(matches!(cursor.current().token_type, TokenType::IDENTIFIER(_)));
|
||||||
|
cursor.advance();
|
||||||
|
|
||||||
|
assert!(cursor.match_token(&TokenType::ASSIGN));
|
||||||
|
cursor.advance();
|
||||||
|
|
||||||
|
assert!(matches!(cursor.current().token_type, TokenType::NUMBER(42)));
|
||||||
|
cursor.advance();
|
||||||
|
|
||||||
|
assert!(cursor.is_at_end());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_newline_skipping_in_braces() {
|
||||||
|
let tokens = vec![
|
||||||
|
Token { token_type: TokenType::LBRACE, line: 1, column: 1 },
|
||||||
|
Token { token_type: TokenType::NEWLINE, line: 1, column: 2 },
|
||||||
|
Token { token_type: TokenType::IDENTIFIER("x".to_string()), line: 2, column: 1 },
|
||||||
|
Token { token_type: TokenType::RBRACE, line: 2, column: 2 },
|
||||||
|
Token { token_type: TokenType::EOF, line: 2, column: 3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut cursor = TokenCursor::new(&tokens);
|
||||||
|
|
||||||
|
cursor.advance(); // consume LBRACE, should skip NEWLINE
|
||||||
|
assert!(matches!(cursor.current().token_type, TokenType::IDENTIFIER(_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expr_mode() {
|
||||||
|
let tokens = vec![
|
||||||
|
Token { token_type: TokenType::IDENTIFIER("x".to_string()), line: 1, column: 1 },
|
||||||
|
Token { token_type: TokenType::NEWLINE, line: 1, column: 2 },
|
||||||
|
Token { token_type: TokenType::PLUS, line: 2, column: 1 },
|
||||||
|
Token { token_type: TokenType::NUMBER(1), line: 2, column: 3 },
|
||||||
|
Token { token_type: TokenType::EOF, line: 2, column: 4 },
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut cursor = TokenCursor::new(&tokens);
|
||||||
|
|
||||||
|
cursor.with_expr_mode(|c| {
|
||||||
|
c.advance(); // consume IDENTIFIER, should skip NEWLINE in expr mode
|
||||||
|
assert!(c.match_token(&TokenType::PLUS));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
253
src/parser/expr_cursor.rs
Normal file
253
src/parser/expr_cursor.rs
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
|
||||||
|
use crate::parser::cursor::TokenCursor;
|
||||||
|
use crate::parser::ParseError;
|
||||||
|
use crate::tokenizer::TokenType;
|
||||||
|
|
||||||
|
/// TokenCursorを使用した式パーサー(実験的実装)
|
||||||
|
pub struct ExprParserWithCursor;
|
||||||
|
|
||||||
|
impl ExprParserWithCursor {
|
||||||
|
/// 式をパース(TokenCursor版)
|
||||||
|
pub fn parse_expression(cursor: &mut TokenCursor) -> Result<ASTNode, ParseError> {
|
||||||
|
// 式モードで実行(改行を自動的にスキップ)
|
||||||
|
cursor.with_expr_mode(|c| {
|
||||||
|
Self::parse_or_expr(c)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// OR式をパース
|
||||||
|
fn parse_or_expr(cursor: &mut TokenCursor) -> Result<ASTNode, ParseError> {
|
||||||
|
let mut left = Self::parse_and_expr(cursor)?;
|
||||||
|
|
||||||
|
while cursor.match_token(&TokenType::OR) {
|
||||||
|
let op_line = cursor.current().line;
|
||||||
|
cursor.advance();
|
||||||
|
let right = Self::parse_and_expr(cursor)?;
|
||||||
|
left = ASTNode::BinaryOp {
|
||||||
|
operator: BinaryOperator::Or,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(right),
|
||||||
|
span: Span::new(op_line, 0, op_line, 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AND式をパース
|
||||||
|
fn parse_and_expr(cursor: &mut TokenCursor) -> Result<ASTNode, ParseError> {
|
||||||
|
let mut left = Self::parse_comparison_expr(cursor)?;
|
||||||
|
|
||||||
|
while cursor.match_token(&TokenType::AND) {
|
||||||
|
let op_line = cursor.current().line;
|
||||||
|
cursor.advance();
|
||||||
|
let right = Self::parse_comparison_expr(cursor)?;
|
||||||
|
left = ASTNode::BinaryOp {
|
||||||
|
operator: BinaryOperator::And,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(right),
|
||||||
|
span: Span::new(op_line, 0, op_line, 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 比較式をパース
|
||||||
|
fn parse_comparison_expr(cursor: &mut TokenCursor) -> Result<ASTNode, ParseError> {
|
||||||
|
let mut left = Self::parse_additive_expr(cursor)?;
|
||||||
|
|
||||||
|
while let Some(op) = Self::match_comparison_op(cursor) {
|
||||||
|
let op_line = cursor.current().line;
|
||||||
|
cursor.advance();
|
||||||
|
let right = Self::parse_additive_expr(cursor)?;
|
||||||
|
left = ASTNode::BinaryOp {
|
||||||
|
operator: op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(right),
|
||||||
|
span: Span::new(op_line, 0, op_line, 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 比較演算子をチェック
|
||||||
|
fn match_comparison_op(cursor: &TokenCursor) -> Option<BinaryOperator> {
|
||||||
|
match &cursor.current().token_type {
|
||||||
|
TokenType::EQUALS => Some(BinaryOperator::Equal),
|
||||||
|
TokenType::NotEquals => Some(BinaryOperator::NotEqual),
|
||||||
|
TokenType::LESS => Some(BinaryOperator::Less),
|
||||||
|
TokenType::LessEquals => Some(BinaryOperator::LessEqual),
|
||||||
|
TokenType::GREATER => Some(BinaryOperator::Greater),
|
||||||
|
TokenType::GreaterEquals => Some(BinaryOperator::GreaterEqual),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 加算式をパース
|
||||||
|
fn parse_additive_expr(cursor: &mut TokenCursor) -> Result<ASTNode, ParseError> {
|
||||||
|
let mut left = Self::parse_multiplicative_expr(cursor)?;
|
||||||
|
|
||||||
|
while let Some(op) = Self::match_additive_op(cursor) {
|
||||||
|
let op_line = cursor.current().line;
|
||||||
|
cursor.advance();
|
||||||
|
let right = Self::parse_multiplicative_expr(cursor)?;
|
||||||
|
left = ASTNode::BinaryOp {
|
||||||
|
operator: op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(right),
|
||||||
|
span: Span::new(op_line, 0, op_line, 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 加算演算子をチェック
|
||||||
|
fn match_additive_op(cursor: &TokenCursor) -> Option<BinaryOperator> {
|
||||||
|
match &cursor.current().token_type {
|
||||||
|
TokenType::PLUS => Some(BinaryOperator::Add),
|
||||||
|
TokenType::MINUS => Some(BinaryOperator::Subtract),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 乗算式をパース
|
||||||
|
fn parse_multiplicative_expr(cursor: &mut TokenCursor) -> Result<ASTNode, ParseError> {
|
||||||
|
let mut left = Self::parse_primary_expr(cursor)?;
|
||||||
|
|
||||||
|
while let Some(op) = Self::match_multiplicative_op(cursor) {
|
||||||
|
let op_line = cursor.current().line;
|
||||||
|
cursor.advance();
|
||||||
|
let right = Self::parse_primary_expr(cursor)?;
|
||||||
|
left = ASTNode::BinaryOp {
|
||||||
|
operator: op,
|
||||||
|
left: Box::new(left),
|
||||||
|
right: Box::new(right),
|
||||||
|
span: Span::new(op_line, 0, op_line, 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 乗算演算子をチェック
|
||||||
|
fn match_multiplicative_op(cursor: &TokenCursor) -> Option<BinaryOperator> {
|
||||||
|
match &cursor.current().token_type {
|
||||||
|
TokenType::MULTIPLY => Some(BinaryOperator::Multiply),
|
||||||
|
TokenType::DIVIDE => Some(BinaryOperator::Divide),
|
||||||
|
TokenType::MODULO => Some(BinaryOperator::Modulo),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// プライマリ式をパース
|
||||||
|
fn parse_primary_expr(cursor: &mut TokenCursor) -> Result<ASTNode, ParseError> {
|
||||||
|
match &cursor.current().token_type.clone() {
|
||||||
|
TokenType::NUMBER(n) => {
|
||||||
|
let value = *n;
|
||||||
|
cursor.advance();
|
||||||
|
Ok(ASTNode::Literal {
|
||||||
|
value: LiteralValue::Integer(value),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenType::STRING(s) => {
|
||||||
|
let value = s.clone();
|
||||||
|
cursor.advance();
|
||||||
|
Ok(ASTNode::Literal {
|
||||||
|
value: LiteralValue::String(value),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenType::TRUE => {
|
||||||
|
cursor.advance();
|
||||||
|
Ok(ASTNode::Literal {
|
||||||
|
value: LiteralValue::Bool(true),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenType::FALSE => {
|
||||||
|
cursor.advance();
|
||||||
|
Ok(ASTNode::Literal {
|
||||||
|
value: LiteralValue::Bool(false),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenType::NULL => {
|
||||||
|
cursor.advance();
|
||||||
|
Ok(ASTNode::Literal {
|
||||||
|
value: LiteralValue::Null,
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenType::IDENTIFIER(name) => {
|
||||||
|
let name = name.clone();
|
||||||
|
cursor.advance();
|
||||||
|
Ok(ASTNode::Variable {
|
||||||
|
name,
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenType::LPAREN => {
|
||||||
|
cursor.advance();
|
||||||
|
let expr = Self::parse_expression(cursor)?;
|
||||||
|
cursor.consume(TokenType::RPAREN)?;
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
TokenType::LBRACE => {
|
||||||
|
// オブジェクトリテラル(改行対応済み)
|
||||||
|
Self::parse_object_literal(cursor)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let line = cursor.current().line;
|
||||||
|
Err(ParseError::InvalidExpression { line })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// オブジェクトリテラルをパース(TokenCursor版)
|
||||||
|
fn parse_object_literal(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() {
|
||||||
|
// キーをパース(STRING or IDENTIFIER)
|
||||||
|
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_expression(cursor)?;
|
||||||
|
entries.push((key, value));
|
||||||
|
|
||||||
|
if cursor.match_token(&TokenType::COMMA) {
|
||||||
|
cursor.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.consume(TokenType::RBRACE)?;
|
||||||
|
Ok(ASTNode::MapLiteral {
|
||||||
|
entries,
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,9 +18,11 @@
|
|||||||
|
|
||||||
// サブモジュール宣言
|
// サブモジュール宣言
|
||||||
mod common;
|
mod common;
|
||||||
|
mod cursor; // TokenCursor: 改行処理を一元管理
|
||||||
mod declarations;
|
mod declarations;
|
||||||
pub mod entry_sugar; // helper to parse with sugar level
|
pub mod entry_sugar; // helper to parse with sugar level
|
||||||
mod expr;
|
mod expr;
|
||||||
|
mod expr_cursor; // TokenCursorを使用した式パーサー(実験的)
|
||||||
mod expressions;
|
mod expressions;
|
||||||
mod items;
|
mod items;
|
||||||
mod statements;
|
mod statements;
|
||||||
|
|||||||
Reference in New Issue
Block a user