From 6d79d7d3acacde9a249929a5794f92f474775b51 Mon Sep 17 00:00:00 2001 From: Moe Charm Date: Wed, 3 Sep 2025 15:26:15 +0900 Subject: [PATCH] grammar(P0): add peek expression, continue statement, and field type annotations acceptance; add sample apps and interpreter path\n\n- tokenizer: add keywords (peek, continue), tokens (=> as FatArrow, :: as DoubleColon), keep >> as Arrow\n- parser: implement peek as expression (literal patterns only, else required), add continue; accept field 'name: Type' (discard type P0)\n- interpreter: evaluate PeekExpr; add Continue control flow handling\n- apps: add peek-demo, loop-continue-demo, adjust field-decl demo; use ConsoleBox instead of env.console for interpreter backend\n- docs: CURRENT_TASK updated earlier for Phase 12.7 P0\n\nNOTE: peek arms currently single-expression (no block expr yet); VM/MIR path does not lower PeekExpr yet; use --backend interpreter for demos --- apps/box-field-decl-demo/main.nyash | 18 ++ apps/loop-continue-demo/main.nyash | 14 + apps/peek-demo/main.nyash | 9 + docs/development/current/CURRENT_TASK.md | 48 +++ .../essential-features-consultation.txt | 96 ++++++ .../grammar-reform-final-decision.txt | 231 ++++++++++++++ .../phase-12.7/grammar-technical-spec.txt | 267 ++++++++++++++++ .../implementation-final-checklist.txt | 259 +++++++++++++++ .../powerful-syntax-sugar-proposals.txt | 297 ++++++++++++++++++ .../when-naming-fun-consultation.txt | 65 ++++ .../when-syntax-deep-consultation.txt | 109 +++++++ src/ast.rs | 30 ++ src/interpreter/eval.rs | 3 + src/interpreter/expressions/mod.rs | 18 ++ src/interpreter/mod.rs | 1 + src/interpreter/statements.rs | 8 + src/parser/declarations/box_definition.rs | 12 +- src/parser/expressions.rs | 71 +++++ src/parser/statements.rs | 9 + src/tokenizer.rs | 20 +- 20 files changed, 1581 insertions(+), 4 deletions(-) create mode 100644 apps/box-field-decl-demo/main.nyash create mode 100644 apps/loop-continue-demo/main.nyash create mode 100644 apps/peek-demo/main.nyash create mode 100644 docs/development/roadmap/phases/phase-12.7/essential-features-consultation.txt create mode 100644 docs/development/roadmap/phases/phase-12.7/grammar-reform-final-decision.txt create mode 100644 docs/development/roadmap/phases/phase-12.7/grammar-technical-spec.txt create mode 100644 docs/development/roadmap/phases/phase-12.7/implementation-final-checklist.txt create mode 100644 docs/development/roadmap/phases/phase-12.7/powerful-syntax-sugar-proposals.txt create mode 100644 docs/development/roadmap/phases/phase-12.7/when-naming-fun-consultation.txt create mode 100644 docs/development/roadmap/phases/phase-12.7/when-syntax-deep-consultation.txt diff --git a/apps/box-field-decl-demo/main.nyash b/apps/box-field-decl-demo/main.nyash new file mode 100644 index 00000000..e58724c4 --- /dev/null +++ b/apps/box-field-decl-demo/main.nyash @@ -0,0 +1,18 @@ +// box field declaration demo (P0) +box Counter { + count: IntegerBox + public { name } + + birth(name) { + me.name = name + me.count = 0 + } + + inc() { me.count = me.count + 1 } + get() { return me.count } +} + +local c = new Counter("nyash") +c.inc() +local con = new ConsoleBox() +con.println(c.get()) diff --git a/apps/loop-continue-demo/main.nyash b/apps/loop-continue-demo/main.nyash new file mode 100644 index 00000000..58fb3e62 --- /dev/null +++ b/apps/loop-continue-demo/main.nyash @@ -0,0 +1,14 @@ +// continue demo: sum of 1..5 skipping 3 +local i = 0 +local sum = 0 + +loop (i < 5) { + i = i + 1 + if i == 3 { + continue + } + sum = sum + i +} + +local con = new ConsoleBox() +con.println("sum=" + sum) diff --git a/apps/peek-demo/main.nyash b/apps/peek-demo/main.nyash new file mode 100644 index 00000000..99ef1925 --- /dev/null +++ b/apps/peek-demo/main.nyash @@ -0,0 +1,9 @@ +// peek expression demo (P0: arms are single expressions) +local animal = "cat" +local sound = peek animal { + "dog" => "bark" + "cat" => "meow" + else => "silent" +} +local con = new ConsoleBox() +con.println(sound) diff --git a/docs/development/current/CURRENT_TASK.md b/docs/development/current/CURRENT_TASK.md index 986dded1..12c69758 100644 --- a/docs/development/current/CURRENT_TASK.md +++ b/docs/development/current/CURRENT_TASK.md @@ -156,6 +156,54 @@ NYASH_DISABLE_TYPEBOX=1 cargo test --lib typebox_tlv_diff -- --nocapture --- +**Phase 12.7: 文法改革(P0 即実装スコープ)** + +- 決定仕様: docs/development/roadmap/phases/phase-12.7/grammar-reform-final-decision.txt +- レガシー互換: 不要(開発中言語のためブレイク可) + +【目的】 +- P0 の文法変更(peek/continue/フィールド宣言/birth統一)を最小差分で実装し、apps の Nyash サンプルで動作確認する。 + +【実装項目(P0)】 +- Tokenizer: + - 予約語追加: `peek`, `continue`, `public` + - 記号トークン追加: `=>`(FAT_ARROW), `::`(DOUBLE_COLON:P1用、定義のみ) + - 既存の `>>`→ARROW は廃止(未使用)。 +- Parser: + - peek式: `peek { => ... else => }` + - else必須。は式 or ブロック(最後の式が値。空はvoid) or 関数Box + - AST: `PeekExpr { scrutinee, arms: [(PatternLiteral, Expr)], else_expr }` + - P0: パターンはリテラルのみ(文字列/数値/bool/null) + - continue文: `continue`(loop内で ControlFlow::Continue) + - フィールド宣言: box先頭で `name: Type`(`public` 修飾子対応)。P0 は「型注釈の受理のみ」。 + - birth統一: 現状維持(Box名コンストラクタ禁止)。 +- VM/MIR: + - peekは if-else 連鎖へデシュガ(文字列はequals、数値は==)。 + - continue は既存の ControlFlow::Continue に接続。 +- ResultBox 方針(実装最小): + - まずは現状APIで利用(is_ok/is_err/get_value/get_error)。? 演算子はP1で導入予定。 + +【アプリ更新(ゴール)】 +- apps/ 配下に P0 文法を使った最小デモを追加・更新して動作確認(VM): + - `apps/peek-demo/main.nyash`: peek の式/ブロック/関数Box 3種の例 + - `apps/loop-continue-demo/main.nyash`: continue の確認(カウントスキップ) + - `apps/box-field-decl-demo/main.nyash`: `box` 内フィールド `name: Type` と `public` の受理(birth内で代入) + +【テスト更新】 +- 既存 test を壊さない範囲で追加(Rust 側の parser 単体テスト): + - `src/tests/parser_peek_test.rs`: peek の構文/AST/評価(if-else相当と一致) + - `src/tests/parser_continue_test.rs`: continue の挙動確認 + - `src/tests/parser_field_decl_test.rs`: `name: Type` の受理(型名は文字列一致でOK) + +【DoD】 +- apps の上記3デモが VM で実行成功(peek分岐が正しく値を返す/continue がスキップ動作/field 宣言受理)。 +- 追加の parser テストが Green(最低1本/項目)。 +- 既存サンプルの動作に回帰なし(ビルド/VM実行)。 + +【補足】 +- P1 以降:Parent::method の `::` 記法、fn{} クロージャ完全化、? 演算子導入、Resultユーティリティ(andThen/map 等)、フィールドのデフォルト初期化を予定。 + + # 🎯 CURRENT TASK - 2025-08-30 Restart Snapshot(Plugin-First / Core最小化) このスナップショットは最新の到達点へ更新済み。再起動時はここから辿る。 diff --git a/docs/development/roadmap/phases/phase-12.7/essential-features-consultation.txt b/docs/development/roadmap/phases/phase-12.7/essential-features-consultation.txt new file mode 100644 index 00000000..a88c2936 --- /dev/null +++ b/docs/development/roadmap/phases/phase-12.7/essential-features-consultation.txt @@ -0,0 +1,96 @@ +Nyash言語の必須機能について深い相談です(文法改革の続き) + +【前回の合意事項】 +- 予約語10個に削減 +- セミコロン不要(改行区切り) +- フィールド宣言は name: Type 形式 +- publicは先頭配置 + +【重要:忘れていた必須機能】 + +1. switch/case文(またはwhen文) + - if文地獄を避けるために絶対必要 + - 以前Claude提案のwhen文も検討 + - パターンマッチング機能は必要? + +【例:現在のif文地獄】 +if type == "dog" { + bark() +} else if type == "cat" { + meow() +} else if type == "bird" { + chirp() +} else { + silent() +} + +【理想的な形は?】 +when type { + "dog" => bark() + "cat" => meow() + "bird" => chirp() + else => silent() +} + +または + +switch type { + case "dog": + bark() + case "cat": + meow() + default: + silent() +} + +【その他の必須機能候補】 + +1. continue文 + - ループの次の反復へスキップ + - breakはあるのにcontinueがない + +2. null/nil/void値 + - 現在はVoidBoxだけ? + - もっと簡潔な表現が必要? + +3. 型チェック機能 + - value is StringBox のような? + - typeof演算子? + +4. エラーハンドリング + - try/catch/finallyは予約語リストにあるが、本当に必要? + - Result型だけで十分? + +5. モジュール/名前空間 + - importはあるが、exportは? + - 名前空間の分離は? + +6. その他検討事項 + - yield(ジェネレータ) + - with文(リソース管理) + - defer文(遅延実行) + - assert文(デバッグ) + +【質問】 + +1. switch/case vs when文、どちらがNyashに適している? + - 予約語数への影響は? + - Everything is Box哲学との整合性は? + +2. 上記の機能候補の中で、本当に必須なものは? + - 予約語10個制限を守りつつ実現可能? + - 代替案はある? + +3. Box型システムで解決できる機能は? + - 例:ResultBoxでエラーハンドリング + - 例:OptionBoxでnull安全性 + +4. 文法のシンプルさを保ちながら実用性を確保する方法は? + +【理想】 +- 予約語は最小限(できれば10個維持) +- Everything is Box哲学に忠実 +- 実用的でif文地獄を避けられる +- 初学者にも分かりやすい + +プログラミング言語設計の観点から、必須機能の優先順位と実装方法を提案してください。 \ No newline at end of file diff --git a/docs/development/roadmap/phases/phase-12.7/grammar-reform-final-decision.txt b/docs/development/roadmap/phases/phase-12.7/grammar-reform-final-decision.txt new file mode 100644 index 00000000..75742dbb --- /dev/null +++ b/docs/development/roadmap/phases/phase-12.7/grammar-reform-final-decision.txt @@ -0,0 +1,231 @@ +================================================================================ +Nyash文法改革 - 最終決定事項(実装向け) +2025-09-03 +================================================================================ + +【Phase 12.7: 文法改革の完全仕様】 + +================================================================================ +1. 予約語リスト(最終版) +================================================================================ + +合計15個の予約語: +1. box - Box定義 +2. new - インスタンス生成 +3. me - 自己参照 +4. public - 公開指定(デフォルトは非公開) +5. if - 条件分岐 +6. else - else節 +7. loop - ループ(唯一の形式) +8. break - ループ脱出 +9. continue - 次の反復へスキップ +10. peek - 分岐構文(旧when)※※※新決定!※※※ +11. return - 関数リターン +12. import - モジュール読み込み +13. from - デリゲーション/親メソッド呼び出し +14. birth - コンストラクタ(Everything is Box哲学) +15. fn - 関数定義/関数Box生成 + +================================================================================ +2. peek構文(分岐構文) +================================================================================ + +【基本構文】 +peek { + => + => + else => // else必須 +} + +【3つの形式】 +// 単一式 +peek animal { + "dog" => bark() + "cat" => meow() + else => silent() +} + +// ブロック式(最後の式が値) +peek type { + "error" => { + log("Error occurred") + local message = getErrorMessage() + notifyAdmin(message) + message // これがpeekの値 + } + else => "ok" +} + +// 関数Box(新しいスコープ) +peek operation { + "factorial" => fn(n) { + if n <= 1 { return 1 } + return n * me(n - 1) // meは関数Box自身 + }(5) + else => fn() { return 1 }() +} + +【重要な仕様】 +- peekは「式」(値を返す) +- else節は必須(非網羅エラーを防ぐ) +- returnはpeekではなく関数から脱出 +- =>の右側は式、ブロック、関数Boxのいずれか +- パターンは当初「リテラルの等値比較」のみ + +================================================================================ +3. フィールド宣言(Box内) +================================================================================ + +【基本形式】 +box ClassName { + // フィールド宣言(最上部に配置) + fieldName: TypeBox // デフォルト非公開 + public fieldName: TypeBox // 公開フィールド + + // コンストラクタ + birth(params) { + // 初期化処理 + } + + // メソッド + methodName(params) { + // 処理 + } +} + +【具体例】 +box Counter { + count: IntegerBox + public name: StringBox + cache: MapBox = new MapBox() // デフォルト値も可能 + + birth(name) { + me.name = name + me.count = 0 + // cacheは既に初期化済み + } +} + +================================================================================ +4. デリゲーション構文 +================================================================================ + +box Child from Parent { + additionalField: StringBox + + birth(name, extra) { + from Parent.birth(name) // 親のコンストラクタ呼び出し + me.additionalField = extra + } + + // メソッドオーバーライド(@overrideは将来検討) + process() { + Parent::process() // 親メソッド呼び出し(::記法) + // または + from Parent.process() // 従来記法も可 + } +} + +================================================================================ +5. fn(関数Box)の拡張 +================================================================================ + +【用途】 +1. トップレベル関数定義 +2. インライン関数Box作成(クロージャ) + +【例】 +// 通常の関数定義 +fn add(a, b) { + return a + b +} + +// インライン関数Box +local counter = fn() { + local count = 0 + return { + increment: fn() { count = count + 1 }, + get: fn() { return count } + } +}() + +// peek内での使用 +peek operation { + "make_adder" => fn(x) { + return fn(y) { return x + y } // クロージャ + } + else => fn() { return null } +} + +【重要】 +- fn{} は新しいスコープ(関数Box)を作る +- {} だけは単なるブロック(スコープ共有) +- meの意味が変わる(関数Box内では関数自身) + +================================================================================ +6. その他の重要事項 +================================================================================ + +【セミコロン】 +- 基本的に不要(改行が文の区切り) +- 1行に複数文を書く場合のみ使用可 + +【変数宣言】 +- local x = 42 // ローカル変数 +- 未宣言変数への代入はエラー + +【論理演算子】 +- not, and, or を使用(!, &&, || は非推奨) + +【型チェック】 +- typeof()関数 + peek構文で実現 +- 例: peek typeof(value) { "StringBox" => ... } + +================================================================================ +7. 実装優先順位 +================================================================================ + +P0(即実装): +1. パーサーにpeek構文追加 +2. continue追加 +3. フィールド宣言の name: Type 形式 +4. birth統一 + +P1(次フェーズ): +1. Parent::method() 記法 +2. fn{} クロージャ完全実装 +3. OptionBox/ResultBox標準化 + +P2(将来検討): +1. パターンマッチング拡張 +2. @override等の属性 +3. 構文糖衣(nyan等のエイリアス) + +================================================================================ +8. パーサー実装への注意点 +================================================================================ + +- peek { の識別 +- => トークンの追加 +- else必須チェック +- ブロックと関数Boxの区別(fnキーワードの有無) +- returnのスコープ(最も内側の関数から脱出) + +================================================================================ +9. MIR/VM/LLVM実装指針 +================================================================================ + +【MIR】 +- PeekExpr { scrutinee, arms: [(Pattern, Expr)], else_expr } +- Pattern は当初 Literal のみ +- 将来的に Pattern を拡張可能な設計に + +【VM】 +- 小規模: if-else連鎖 +- 大規模: ジャンプテーブル最適化 + +【LLVM】 +- 整数: switch命令 +- 文字列: ハッシュテーブル or 二分探索 + +================================================================================ \ No newline at end of file diff --git a/docs/development/roadmap/phases/phase-12.7/grammar-technical-spec.txt b/docs/development/roadmap/phases/phase-12.7/grammar-technical-spec.txt new file mode 100644 index 00000000..e5f06981 --- /dev/null +++ b/docs/development/roadmap/phases/phase-12.7/grammar-technical-spec.txt @@ -0,0 +1,267 @@ +================================================================================ +Nyash文法改革 - 技術仕様書(パーサー実装向け) +2025-09-03 +================================================================================ + +1. トークン定義 +================================================================================ + +【予約語(Keywords)】15個 +- box, new, me, public, if, else, loop, break, continue +- peek, return, import, from, birth, fn + +【演算子・記号(Operators/Symbols)】 +- 算術: + - * / % +- 比較: == != < > <= >= +- 論理: and or not +- 代入: = +- アクセス: . (ドット) [ ] (インデックス) +- 関数: ( ) { } +- 区切り: , ; +- 型注釈: : +- peekアーム: => +- 親メソッド: :: +- コメント: // /* */ + +【リテラル(Literals)】 +- 整数: /[0-9]+/ +- 浮動小数: /[0-9]+\.[0-9]+/ +- 文字列: /"([^"\\]|\\.)*"/ +- 真偽値: true false +- null値: null + +【識別子(Identifier)】 +- /[a-zA-Z_][a-zA-Z0-9_]*/ +- 予約語と衝突しない + +【空白・改行】 +- 改行は文の区切り(セミコロン自動挿入規則適用) +- インデント: 意味を持たない(Python風ではない) +- 行継続: \ で明示的に次行へ続く + +================================================================================ +2. 式構文(Expression Grammar) +================================================================================ + +【優先順位(高→低)】 +1. リテラル、識別子、(式) +2. 関数呼び出し: expr(args) +3. メンバアクセス: expr.member +4. インデックス: expr[index] +5. 単項演算: not expr, -expr +6. 乗除: * / % +7. 加減: + - +8. 比較: < > <= >= == != +9. 論理積: and +10. 論理和: or +11. peek式: peek expr { ... } + +【結合規則】 +- 算術演算子: 左結合 +- 比較演算子: 非結合(a < b < c は不可) +- 論理演算子: 左結合 + +【式の種類】 +``` +Expression ::= + | Literal + | Identifier + | "me" + | Expression "." Identifier // メンバアクセス + | Expression "[" Expression "]" // インデックス + | Expression "(" ExprList? ")" // 関数呼び出し + | "new" Identifier "(" ExprList? ")" // インスタンス生成 + | "fn" "(" ParamList? ")" Block // 関数Box + | PeekExpression + | BinaryOp + | UnaryOp + | "(" Expression ")" +``` + +【peek式】 +``` +PeekExpression ::= + "peek" Expression "{" + PeekArm* + "else" "=>" (Expression | Block) + "}" + +PeekArm ::= + Pattern "=>" (Expression | Block) + +Pattern ::= + | Literal // 現在はリテラルのみ + | Pattern "|" Pattern // 将来: 複数パターン +``` + +================================================================================ +3. 文構文(Statement Grammar) +================================================================================ + +``` +Statement ::= + | Expression // 式文 + | "local" IdentList ("=" ExprList)? // 変数宣言 + | Identifier "=" Expression // 代入 + | "if" Expression Block ("else" (Block | Statement))? + | "loop" "(" Expression ")" Block + | "break" + | "continue" + | "return" Expression? + | BoxDeclaration + | FunctionDeclaration + | "import" StringLiteral ("as" Identifier)? + +Block ::= "{" Statement* "}" + +BoxDeclaration ::= + "box" Identifier ("from" IdentList)? "{" + FieldDeclaration* + MethodDeclaration* + "}" + +FieldDeclaration ::= + ("public")? Identifier ":" Identifier ("=" Expression)? + +MethodDeclaration ::= + ("public")? (Identifier | "birth") "(" ParamList? ")" Block +``` + +================================================================================ +4. 名前解決規則 +================================================================================ + +【スコープ】 +1. グローバルスコープ(box、関数定義) +2. Box内スコープ(フィールド、メソッド) +3. 関数スコープ(引数、local変数) +4. ブロックスコープ({}内のlocal) + +【シャドーイング】 +- 内側のスコープが外側を隠蔽 +- 同一スコープ内での再定義は不可 + +【meの解決】 +- Box内: 現在のBoxインスタンス +- fn{}内: 関数Box自身 +- それ以外: エラー + +【from Parent.method()の解決】 +- 現在のBoxがParentにデリゲートしているか確認 +- Parent::method()も同様 + +================================================================================ +5. 曖昧性と解決策 +================================================================================ + +【左再帰なし】 +- 全て右再帰または反復で記述 + +【最長一致】 +- トークナイザーレベルで最長一致 +- 例: ">=" は2文字演算子として認識 + +【バックトラック不要】 +- LL(1)またはLL(2)で解析可能 +- peekトークンで次の構文要素を判別 + +【曖昧箇所】 +1. 関数呼び出し vs 変数 + - 解決: "("の有無で判別 + +2. {} がブロックか関数Boxか + - 解決: 直前のfnキーワードで判別 + +3. セミコロン自動挿入 + - 改行時に次が中置演算子でない場合挿入 + - 例外: 行末が不完全な式の場合は挿入しない + +================================================================================ +6. エラー処理 +================================================================================ + +【期待トークンエラー】 +``` +Expected 'token' but found 'actual' + at line X, column Y +``` + +【回復戦略】 +1. 文レベル: 次の文開始トークンまでスキップ +2. ブロックレベル: 対応する}までスキップ +3. Box/関数レベル: 次のbox/fnまでスキップ + +【行・列の追跡】 +- 各トークンに位置情報を付与 +- エラー時は該当箇所を表示 + +【エラーメッセージ例】 +``` +Error: Missing 'else' in peek expression + --> program.nyash:10:5 + | +10 | peek value { + | ^^^^ peek expression requires 'else' branch +``` + +================================================================================ +7. 将来拡張の余地 +================================================================================ + +【予約済み位置】 +- @ 記号: 将来の属性用 +- # 記号: 将来のマクロ用 +- ` 記号: 将来のテンプレート用 + +【拡張可能な構文】 +- Pattern: 現在はリテラルのみ、将来は構造体パターン等 +- 型注釈: 現在は : Type のみ、将来はジェネリクス等 + +【前方互換性】 +- 未知の @ で始まる行は読み飛ばし +- box内の未知セクションは警告のみ + +================================================================================ +8. 既存実装への差分最小化 +================================================================================ + +【tokenizer.rs への変更】 +1. 予約語リストにpeek, continue, birthを追加 +2. => を2文字演算子として追加 +3. :: を2文字演算子として追加 + +【parser.rs への変更】 +1. parse_when() → parse_peek() に改名 +2. parse_field_declaration() を追加(name: Type形式) +3. parse_fn_literal() を追加(fn式のため) +4. continue文の処理追加 +5. birthキーワードの特殊処理 + +【AST変更】 +```rust +// 追加 +enum Expr { + // ... + Peek { + scrutinee: Box, + arms: Vec<(Pattern, BlockOrExpr)>, + else_arm: Box, + }, + FnLiteral { + params: Vec, + body: Block, + }, +} + +enum Stmt { + // ... + Continue, +} +``` + +【セマンティクス】 +- peekは式として値を返す +- else必須のバリデーション追加 +- returnは最内関数スコープから脱出 + +================================================================================ \ No newline at end of file diff --git a/docs/development/roadmap/phases/phase-12.7/implementation-final-checklist.txt b/docs/development/roadmap/phases/phase-12.7/implementation-final-checklist.txt new file mode 100644 index 00000000..a5bc7f5c --- /dev/null +++ b/docs/development/roadmap/phases/phase-12.7/implementation-final-checklist.txt @@ -0,0 +1,259 @@ +================================================================================ +Phase 12.7 文法改革 - 実装前最終チェックリスト +2025-09-03 +================================================================================ + +【ChatGPT5さんからの重要指摘への対応】 + +================================================================================ +1. トークナイザー実装の注意点 +================================================================================ + +【既存の問題】 +- 現在のARROWトークンが '>>' になっている → これを修正! + +【対応】 +```rust +// tokenizer.rs での修正 +// 削除または未使用化 +// ARROW => ">>" // これは間違い! + +// 新規追加 +FAT_ARROW => "=>" // peek構文用 +DOUBLE_COLON => "::" // Parent::method用(P1だがトークンは今追加) +``` + +【追加する予約語(P0)】 +- peek +- continue +- birth +(publicは後述の特殊対応) + +================================================================================ +2. 値の扱いの明確化 +================================================================================ + +【空ブロックの値】 +- 空ブロック {} の値は **VoidBox** とする +- 最後の式がない場合もVoidBox + +【peek式の値規約】 +```nyash +// 単一式の値 +peek x { + 1 => "one" // StringBoxを返す + else => "other" +} + +// ブロックの値(最後の式) +peek x { + 1 => { + print("got one") + "one" // これが値 + } + else => { + // 空ブロックはVoidBox + } +} + +// 関数Boxの値(P0では単純に関数オブジェクト) +peek op { + "add" => fn(a, b) { return a + b } // 関数Boxを返す + else => fn() { return 0 } +} +``` + +================================================================================ +3. 等値比較の詳細規約 +================================================================================ + +【peek内のパターンマッチング(P0)】 +- StringBox: 完全一致、大文字小文字を区別 +- IntegerBox: == による数値比較 +- BoolBox: true/false の完全一致 +- VoidBox/null: null との一致 + +【typeof との組み合わせ】 +```nyash +peek typeof(value) { + "StringBox" => processString(value) + "IntegerBox" => processInt(value) + else => processOther(value) +} +``` +※ typeof は既存実装の型名文字列をそのまま返す + +================================================================================ +4. publicキーワードの扱い +================================================================================ + +【現状との調整】 +- 既存: public { field1, field2 } ブロック形式 +- 新規: public field: Type 個別指定形式 + +【P0での対応】 +```rust +// 両方をサポート(移行期間) +box Example { + // 新形式 + public name: StringBox + count: IntegerBox + + // 旧形式(レガシー、警告付きでサポート) + public { oldField1, oldField2 } +} +``` + +【実装方針】 +- publicを「文脈依存キーワード」として扱う +- Box内でのみ特別な意味を持つ +- それ以外では識別子として使える(後方互換性) + +================================================================================ +5. フィールド宣言の段階実装 +================================================================================ + +【P0(今回)】 +- パースのみ: name: Type +- publicプレフィックス対応 +- デフォルト値なし(= expr はP1へ) + +【P1(次回)】 +- デフォルト値: name: Type = expr +- birth内での自動初期化 + +【現在の回避策】 +```nyash +box Counter { + count: IntegerBox + cache: MapBox // デフォルト値はP1まで待つ + + birth() { + me.count = 0 + me.cache = new MapBox() // birth内で明示的に初期化 + } +} +``` + +================================================================================ +6. デシュガー戦略(P0) +================================================================================ + +【peek → if-else連鎖】 +```nyash +// Nyashコード +peek animal { + "dog" => bark() + "cat" => meow() + else => silent() +} + +// デシュガー後(概念的) +if animal == "dog" { + bark() +} else if animal == "cat" { + meow() +} else { + silent() +} +``` + +【実装の簡単さ優先】 +- VM/MIRは既存のif-else処理をそのまま利用 +- 最適化(ジャンプテーブル等)はP1以降 + +================================================================================ +7. 最小限のテストケース(P0) +================================================================================ + +```nyash +// test_peek_basic.nyash +local animal = "cat" +local sound = peek animal { + "dog" => "woof" + "cat" => "meow" + else => "..." +} +print(sound) // "meow" + +// test_peek_block.nyash +local result = peek x { + 1 => { + local temp = "one" + temp // 値 + } + else => "other" +} + +// test_continue.nyash +local i = 0 +local sum = 0 +loop(i < 5) { + i = i + 1 + if i == 3 { + continue + } + sum = sum + i +} +print(sum) // 12 (1+2+4+5, 3はスキップ) + +// test_field_declaration.nyash +box Point { + public x: IntegerBox + public y: IntegerBox + private z: IntegerBox + + birth(x, y) { + me.x = x + me.y = y + me.z = 0 + } +} +``` + +================================================================================ +8. 実装順序(推奨) +================================================================================ + +1. tokenizer.rs + - FAT_ARROW, DOUBLE_COLON追加 + - peek, continue, birth を予約語追加 + +2. parser/expressions.rs + - parse_peek_expr() 実装 + - else必須チェック + +3. parser/statements.rs + - Continue文追加 + - フィールド宣言パース追加 + +4. ast.rs + - PeekExpr, ContinueStmt追加 + - Field構造体に型情報追加 + +5. interpreter/evaluator.rs + - peek式の評価(if-elseとして) + - continue処理(既存のControlFlow利用) + +================================================================================ +9. P1に回すもの(今回は実装しない) +================================================================================ + +- import文の完全実装 +- Parent::method() 記法(トークンのみ追加) +- fn{} クロージャの完全実装(環境キャプチャ等) +- フィールドのデフォルト値 +- @override等の属性 + +================================================================================ +10. 合意事項の最終確認 +================================================================================ + +✓ peekは「式」として値を返す +✓ else節は必須(コンパイルエラー) +✓ 空ブロックの値はVoidBox +✓ publicは文脈依存キーワード(互換性維持) +✓ フィールドのデフォルト値はP1送り +✓ デシュガーでシンプル実装(最適化は後回し) + +================================================================================ \ No newline at end of file diff --git a/docs/development/roadmap/phases/phase-12.7/powerful-syntax-sugar-proposals.txt b/docs/development/roadmap/phases/phase-12.7/powerful-syntax-sugar-proposals.txt new file mode 100644 index 00000000..094becfa --- /dev/null +++ b/docs/development/roadmap/phases/phase-12.7/powerful-syntax-sugar-proposals.txt @@ -0,0 +1,297 @@ +================================================================================ +Nyash つよつよ糖衣構文提案 - 自己ホスティングに向けて +2025-09-03 +================================================================================ + +【背景】 +自己ホスティング(NyashでNyashコンパイラを書く)を実現するため、 +コード量を劇的に削減できる強力な糖衣構文を検討する。 + +================================================================================ +1. パイプライン演算子 |> (優先度:最高) +================================================================================ + +【現在の問題】 +// ネストが深くて読みにくい +local result = trim(uppercase(replace(input, "cat", "nyan"))) + +【提案構文】 +local result = input + |> replace($, "cat", "nyan") + |> uppercase($) + |> trim($) + +// $ は前の結果を表す特殊変数 +// または第一引数に自動挿入も可 +local result = input + |> replace("cat", "nyan") // 第一引数に自動挿入 + |> uppercase() + |> trim() + +【効果】 +- AST変換処理が非常に読みやすくなる +- 関数合成が直感的 +- デバッグ時に中間結果を確認しやすい + +================================================================================ +2. 分割代入(Destructuring)(優先度:高) +================================================================================ + +【基本形】 +// タプル分割 +local (x, y, z) = getCoordinates() + +// Box分割 +local { name, age, email } = getUserInfo() + +// 配列分割 +local [first, second, ...rest] = items + +【peek内での構造体パターン】 +peek ast { + BinaryOp(left, op, right) => { + // left, op, right が自動的に変数として使える + compile(left) + compile(op) + compile(right) + } + UnaryOp(op, expr) => { + compile(op) + compile(expr) + } + Literal(value) => value.toString() +} + +【効果】 +- パーサー実装で威力発揮 +- ASTノードの処理が簡潔に +- ボイラープレートコード削減 + +================================================================================ +3. null安全演算子 ?. と ?? (優先度:高) +================================================================================ + +【?.(null安全アクセス)】 +// 現在 +local name +if user != null { + if user.profile != null { + if user.profile.name != null { + name = user.profile.name + } + } +} + +// 提案 +local name = user?.profile?.name + +【??(null合体演算子)】 +local displayName = user?.name ?? "Anonymous" +local port = config?.server?.port ?? 8080 + +【組み合わせ】 +local result = getData()?.process()?.format() ?? "default" + +【効果】 +- null/undefinedエラーの防止 +- 防御的プログラミングが簡潔に +- 設定値の読み込みで特に有用 + +================================================================================ +4. 文字列テンプレート強化(優先度:高) +================================================================================ + +【基本テンプレート】 +// 現在 +local msg = "Error at line " + line + ", column " + col + ": " + error + +// 提案 +local msg = `Error at line ${line}, column ${col}: ${error}` + +【複数行テンプレート】 +local code = ``` +fn ${name}(${params.join(", ")}) { + ${body.indent(4)} +} +``` + +【タグ付きテンプレート(将来)】 +local sql = SQL`SELECT * FROM users WHERE id = ${userId}` +local regex = RE`\d{3}-\d{4}` // エスケープ不要 + +【効果】 +- コード生成が劇的に簡単に +- SQLやHTML生成で安全性向上 +- 可読性の大幅改善 + +================================================================================ +5. リスト内包表記(優先度:中) +================================================================================ + +【基本形】 +// 現在 +local result = new ArrayBox() +for item in list { + if item.isValid() { + result.push(item.process()) + } +} + +// 提案 +local result = [item.process() for item in list if item.isValid()] + +【ネスト】 +local pairs = [(x, y) for x in xs for y in ys if x != y] + +【辞書内包】 +local map = {key: value.process() for (key, value) in entries} + +【効果】 +- 変換処理が1行で書ける +- 関数型プログラミングスタイル +- メモリ効率的な実装も可能 + +================================================================================ +6. for式(値を返すfor)(優先度:中) +================================================================================ + +【基本形】 +local tokens = for token in input yield parseToken(token) + +【フィルタ付き】 +local validTokens = for token in tokens if token.isValid() yield token + +【変換付き】 +local ast = for line in lines { + local trimmed = line.trim() + if trimmed.length() > 0 { + yield parseLine(trimmed) + } +} + +【効果】 +- リスト内包より複雑な処理に対応 +- 中間変数を使える +- yieldで明示的 + +================================================================================ +7. スプレッド演算子 ... (優先度:中) +================================================================================ + +【配列展開】 +local combined = [...array1, ...array2, newItem] +local clone = [...original] + +【Box展開】 +local updated = { ...oldConfig, debug: true, port: 8080 } + +【関数引数展開】 +processItems(...args) +local max = Math.max(...numbers) + +【効果】 +- 配列・オブジェクト操作が簡潔 +- イミュータブルな更新が簡単 +- 可変長引数の処理 + +================================================================================ +8. with文(リソース管理)(優先度:低) +================================================================================ + +【基本形】 +with file = openFile("data.txt") { + local content = file.read() + process(content) +} // 自動的にfile.close() + +【複数リソース】 +with db = connectDB(), file = openLog() { + db.query("SELECT...") + file.write(result) +} // 両方自動クローズ + +【効果】 +- リソースリークの防止 +- finally不要 +- RAII的な管理 + +================================================================================ +9. 関数合成演算子(優先度:低) +================================================================================ + +【合成 >>】 +local processLine = trim >> lowercase >> tokenize +local result = processLine(input) + +【部分適用】 +local add5 = add(5, _) +local result = add5(10) // 15 + +【カリー化】 +local multiply = fn(a)(b) => a * b +local double = multiply(2) + +【効果】 +- 関数型プログラミング支援 +- 高階関数の活用 +- コードの再利用性向上 + +================================================================================ +10. 強力なpeekパターン拡張(優先度:中) +================================================================================ + +【ガード】 +peek value { + n if n > 0 => "positive" + n if n < 0 => "negative" + 0 => "zero" +} + +【範囲】 +peek score { + 0..60 => "F" + 60..70 => "D" + 70..80 => "C" + 80..90 => "B" + 90..100 => "A" +} + +【深いパターン】 +peek ast { + If({ condition: BinaryOp(_, "==", _), then, else }) => { + optimizeEquality(condition, then, else) + } +} + +【効果】 +- より表現力豊かな分岐 +- コンパイラ実装で必須 +- コードの意図が明確 + +================================================================================ +実装優先順位まとめ +================================================================================ + +【Phase 13(次フェーズ)】 +1. パイプライン演算子 |> +2. 分割代入(基本形のみ) +3. null安全演算子 ?. と ?? +4. 文字列テンプレート `${}` + +【Phase 14】 +5. リスト内包表記 +6. for式 +7. peekパターン拡張(ガード) + +【Phase 15以降】 +8. スプレッド演算子 +9. with文 +10. 関数合成演算子 + +================================================================================ +実装時の注意点 +================================================================================ + +1. 各糖衣構文は既存構文へのデシュガーとして実装 +2. エラーメッセージは元の構文で表示 +3. デバッガーでのステップ実行に配慮 +4. 段階的に導入(基本形→拡張形) + +================================================================================ \ No newline at end of file diff --git a/docs/development/roadmap/phases/phase-12.7/when-naming-fun-consultation.txt b/docs/development/roadmap/phases/phase-12.7/when-naming-fun-consultation.txt new file mode 100644 index 00000000..6fd6b872 --- /dev/null +++ b/docs/development/roadmap/phases/phase-12.7/when-naming-fun-consultation.txt @@ -0,0 +1,65 @@ +Nyash言語の分岐構文の名前について、楽しくユニークな相談です! + +【背景】 +when構文でほぼ決定していますが、これは「ニャーの言語」なので、もっと楽しい名前も考えてみたい! + +【現在の候補】 +1. when(現在の最有力候補) +2. match(パターンマッチング的) +3. pick(選ぶ) +4. given(与えられたとき) +5. with(〜で) +6. upon(〜に基づいて) +7. take(取る) +8. test(テスト) +9. by(〜によって) +10. for(〜について) + +【使用例】 +```nyash +// 現在のwhen +when animal { + "dog" => bark() + "cat" => meow() + else => silent() +} + +// もし別の名前なら? +??? animal { + "dog" => bark() + "cat" => meow() + else => silent() +} +``` + +【質問】 +1. もっとユニークで楽しい名前の提案はありますか? + - Nyash(猫言語)らしさ + - Everything is Box哲学 + - 短くて覚えやすい + - 楽しくてワクワクする + +2. 各言語の面白い分岐構文名の例は? + - 変わった名前 + - 独特な哲学を持つもの + - 歴史的に面白いもの + +3. 名前から連想される「使い心地」は? + - whenだと「〜の時」 + - pickだと「選ぶ」 + - 他の名前だと? + +4. 猫っぽい名前はある? + - paw(肉球) + - meow(鳴き声) + - hunt(狩り) + - など... + +【理想】 +- 楽しくて使うたびにワクワクする +- でも実用的で分かりやすい +- 短い(できれば3-4文字) +- 特に良い案がなければwhenでOK! + +気楽に、楽しく、ユニークな提案をお聞かせください! +「こんなの思いついたにゃ〜」という感じで! \ No newline at end of file diff --git a/docs/development/roadmap/phases/phase-12.7/when-syntax-deep-consultation.txt b/docs/development/roadmap/phases/phase-12.7/when-syntax-deep-consultation.txt new file mode 100644 index 00000000..9d2f93d1 --- /dev/null +++ b/docs/development/roadmap/phases/phase-12.7/when-syntax-deep-consultation.txt @@ -0,0 +1,109 @@ +Nyash言語のwhen構文と関数Box設計について深い相談です + +【前回の議論からの進展】 + +1. when文採用でほぼ決定 + - switch/caseより予約語が少ない + - 式としても使える + - パターンマッチングへの拡張性 + +2. => 構文の導入 + - 現代的で見やすい + - 他言語(Rust, Kotlin, JS)でも採用 + - 単一式とブロック両対応を検討中 + +3. returnキーワードは必須! + - 早期リターンに必要 + - 明示性のため重要 + - 式指向だけでは複雑になりすぎる + +【新しい設計提案:fnによる関数Box】 + +通常のブロックと関数Boxを明示的に区別する案: + +```nyash +// 通常のブロック(外側のスコープを共有) +when animal { + "dog" => { + me.count = me.count + 1 // 外側のBoxのme + local sound = "woof" + playSound(sound) + return sound // whenの値として返る + } + "cat" => meow() // 単一式もOK + else => silent() +} + +// 関数Box(新しいスコープ、再帰可能) +when operation { + "factorial" => fn(n) { + if n <= 1 { return 1 } + return n * me(n - 1) // meは新しい関数Box自身! + }(5) // 即座に呼び出し + + "counter" => fn() { + local count = 0 + return { + increment: fn() { count = count + 1 }, + get: fn() { return count } + } + }() +} +``` + +【質問事項】 + +1. when vs match + - whenという名前で良いか? + - matchの方が良い? + - 他の候補:check, on, select + +2. => 構文の詳細設計 + - 単一式:`"dog" => bark()` + - ブロック:`"dog" => { ... }` + - 関数Box:`"dog" => fn(args) { ... }` + - この3パターンで十分か? + +3. fnキーワードの役割拡張 + - 現在:関数定義のみ + - 提案:インライン関数Box作成にも使用 + - 一貫性は保たれるか? + +4. Everything is Box哲学との整合性 + - {} だけでは通常のブロック(Boxではない) + - fn{} で関数Box + - この区別は哲学に反しないか? + +5. 実装の観点 + - MIR/VM/LLVMでの実装難易度 + - 最適化の可能性 + - デバッグのしやすさ + +【設計原則の確認】 + +- 明示性:何が起きているか一目瞭然 +- シンプルさ:初学者にも分かりやすい +- 表現力:実用的なプログラムが書ける +- 一貫性:言語全体で統一感がある + +【予約語リスト(案)】 +必須機能に必要な最小限: +1. box +2. new +3. me +4. public +5. if +6. else +7. loop +8. break +9. continue +10. when (またはmatch) +11. return +12. import +13. from +14. birth (コンストラクタ) +15. fn + +予約語10個厳守ではなく、必要なものは追加する方針です。 + +プログラミング言語設計の観点から、この設計の妥当性と改善案をお聞かせください。 \ No newline at end of file diff --git a/src/ast.rs b/src/ast.rs index 088fe969..945b4832 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -194,6 +194,13 @@ pub enum ExpressionNode { MeExpression { span: Span, }, + /// peek式: peek { lit => expr, ... else => expr } + PeekExpr { + scrutinee: Box, + arms: Vec<(LiteralValue, ASTNode)>, + else_expr: Box, + span: Span, + }, } /// 文ノード - 実行可能なアクション @@ -215,6 +222,9 @@ pub enum StatementNode { Break { span: Span, }, + Continue { + span: Span, + }, Include { filename: String, span: Span, @@ -413,6 +423,10 @@ pub enum ASTNode { Break { span: Span, }, + /// continue文 + Continue { + span: Span, + }, /// using文: using namespace_name UsingStatement { @@ -433,6 +447,14 @@ pub enum ASTNode { span: Span, }, + /// peek式: peek { lit => expr, ... else => expr } + PeekExpr { + scrutinee: Box, + arms: Vec<(LiteralValue, ASTNode)>, + else_expr: Box, + span: Span, + }, + /// arrow文: (sender >> receiver).method(args) Arrow { sender: Box, @@ -619,6 +641,7 @@ impl ASTNode { ASTNode::Loop { .. } => "Loop", ASTNode::Return { .. } => "Return", ASTNode::Break { .. } => "Break", + ASTNode::Continue { .. } => "Continue", ASTNode::UsingStatement { .. } => "UsingStatement", ASTNode::BoxDeclaration { .. } => "BoxDeclaration", ASTNode::FunctionDeclaration { .. } => "FunctionDeclaration", @@ -644,6 +667,7 @@ impl ASTNode { ASTNode::TryCatch { .. } => "TryCatch", ASTNode::Throw { .. } => "Throw", ASTNode::AwaitExpression { .. } => "AwaitExpression", + ASTNode::PeekExpr { .. } => "PeekExpr", } } @@ -672,6 +696,7 @@ impl ASTNode { ASTNode::FromCall { .. } => ASTNodeType::Expression, ASTNode::ThisField { .. } => ASTNodeType::Expression, ASTNode::MeField { .. } => ASTNodeType::Expression, + ASTNode::PeekExpr { .. } => ASTNodeType::Expression, // Statement nodes - 実行可能なアクション ASTNode::Program { .. } => ASTNodeType::Statement, // プログラム全体 @@ -679,6 +704,7 @@ impl ASTNode { ASTNode::Print { .. } => ASTNodeType::Statement, ASTNode::Return { .. } => ASTNodeType::Statement, ASTNode::Break { .. } => ASTNodeType::Statement, + ASTNode::Continue { .. } => ASTNodeType::Statement, ASTNode::UsingStatement { .. } => ASTNodeType::Statement, ASTNode::GlobalVar { .. } => ASTNodeType::Statement, ASTNode::Include { .. } => ASTNodeType::Statement, @@ -728,6 +754,7 @@ impl ASTNode { } } ASTNode::Break { .. } => "Break".to_string(), + ASTNode::Continue { .. } => "Continue".to_string(), ASTNode::UsingStatement { namespace_name, .. } => { format!("UsingStatement({})", namespace_name) } @@ -823,6 +850,7 @@ impl ASTNode { ASTNode::AwaitExpression { expression, .. } => { format!("Await({:?})", expression) } + ASTNode::PeekExpr { .. } => "PeekExpr".to_string(), } } @@ -836,6 +864,7 @@ impl ASTNode { ASTNode::Loop { span, .. } => *span, ASTNode::Return { span, .. } => *span, ASTNode::Break { span, .. } => *span, + ASTNode::Continue { span, .. } => *span, ASTNode::UsingStatement { span, .. } => *span, ASTNode::Nowait { span, .. } => *span, ASTNode::Arrow { span, .. } => *span, @@ -861,6 +890,7 @@ impl ASTNode { ASTNode::Outbox { span, .. } => *span, ASTNode::FunctionCall { span, .. } => *span, ASTNode::AwaitExpression { span, .. } => *span, + ASTNode::PeekExpr { span, .. } => *span, } } } diff --git a/src/interpreter/eval.rs b/src/interpreter/eval.rs index c4ac1010..bf2d8af5 100644 --- a/src/interpreter/eval.rs +++ b/src/interpreter/eval.rs @@ -34,6 +34,9 @@ impl NyashInterpreter { ControlFlow::Break => { return Err(RuntimeError::BreakOutsideLoop); } + ControlFlow::Continue => { + return Err(RuntimeError::BreakOutsideLoop); + } ControlFlow::Return(_) => { return Err(RuntimeError::ReturnOutsideFunction); } diff --git a/src/interpreter/expressions/mod.rs b/src/interpreter/expressions/mod.rs index a554c91f..3cb8c3f2 100644 --- a/src/interpreter/expressions/mod.rs +++ b/src/interpreter/expressions/mod.rs @@ -129,6 +129,24 @@ impl NyashInterpreter { ASTNode::Arrow { sender, receiver, .. } => { self.execute_arrow(sender, receiver) } + ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => { + let val = self.execute_expression(scrutinee)?; + let sval = val.to_string_box().value; + for (pat, expr) in arms { + let pv = match pat { + crate::ast::LiteralValue::String(s) => s.clone(), + crate::ast::LiteralValue::Integer(i) => i.to_string(), + crate::ast::LiteralValue::Float(f) => f.to_string(), + crate::ast::LiteralValue::Bool(b) => if *b { "true".to_string() } else { "false".to_string() }, + crate::ast::LiteralValue::Null => "null".to_string(), + crate::ast::LiteralValue::Void => "void".to_string(), + }; + if pv == sval { + return self.execute_expression(expr); + } + } + self.execute_expression(else_expr) + } ASTNode::Include { filename, .. } => { // include式: 最初のstatic boxを返す diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 8000488c..be89a970 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -59,6 +59,7 @@ pub use errors::RuntimeError; pub enum ControlFlow { None, Break, + Continue, Return(Box), Throw(Box), } diff --git a/src/interpreter/statements.rs b/src/interpreter/statements.rs index 6daebc8d..971f6aac 100644 --- a/src/interpreter/statements.rs +++ b/src/interpreter/statements.rs @@ -86,6 +86,10 @@ impl NyashInterpreter { self.control_flow = super::ControlFlow::Break; Ok(Box::new(VoidBox::new())) } + ASTNode::Continue { .. } => { + self.control_flow = super::ControlFlow::Continue; + Ok(Box::new(VoidBox::new())) + } ASTNode::Nowait { variable, expression, .. } => { self.execute_nowait(variable, expression) @@ -272,6 +276,10 @@ impl NyashInterpreter { self.control_flow = super::ControlFlow::None; return Ok(Box::new(VoidBox::new())); } + super::ControlFlow::Continue => { + self.control_flow = super::ControlFlow::None; + continue; + } super::ControlFlow::Return(_) => { // returnはループを抜けるが、上位に伝播 return Ok(Box::new(VoidBox::new())); diff --git a/src/parser/declarations/box_definition.rs b/src/parser/declarations/box_definition.rs index b7c08e98..9357b8b9 100644 --- a/src/parser/declarations/box_definition.rs +++ b/src/parser/declarations/box_definition.rs @@ -482,8 +482,16 @@ impl NyashParser { methods.insert(field_or_method, method); } else { - // フィールド定義 - fields.push(field_or_method); + // フィールド定義(P0: 型注釈 name: Type を受理して破棄) + let fname = field_or_method; + if self.match_token(&TokenType::COLON) { + self.advance(); // consume ':' + // 型名(識別子)を許可(P0は保持せず破棄) + if let TokenType::IDENTIFIER(_ty) = &self.current_token().token_type { + self.advance(); + } + } + fields.push(fname); } } else { return Err(ParseError::UnexpectedToken { diff --git a/src/parser/expressions.rs b/src/parser/expressions.rs index 2cf430fa..2ff20bd0 100644 --- a/src/parser/expressions.rs +++ b/src/parser/expressions.rs @@ -192,6 +192,10 @@ impl NyashParser { /// 単項演算子をパース fn parse_unary(&mut self) -> Result { + // peek式の先読み + if self.match_token(&TokenType::PEEK) { + return self.parse_peek_expr(); + } if self.match_token(&TokenType::MINUS) { self.advance(); // consume '-' let operand = self.parse_unary()?; // 再帰的に単項演算をパース @@ -223,6 +227,73 @@ impl NyashParser { self.parse_call() } + + /// peek式: peek { lit => expr ... else => expr } + fn parse_peek_expr(&mut self) -> Result { + self.advance(); // consume 'peek' + let scrutinee = self.parse_expression()?; + self.consume(TokenType::LBRACE)?; + + let mut arms: Vec<(crate::ast::LiteralValue, ASTNode)> = Vec::new(); + let mut else_expr: Option = None; + + while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { + self.skip_newlines(); + while self.match_token(&TokenType::COMMA) || self.match_token(&TokenType::NEWLINE) { + self.advance(); + self.skip_newlines(); + } + if self.match_token(&TokenType::RBRACE) { break; } + + // else or literal + let is_else = matches!(self.current_token().token_type, TokenType::ELSE); + if is_else { + self.advance(); // consume 'else' + self.consume(TokenType::FAT_ARROW)?; + let expr = self.parse_expression()?; + else_expr = Some(expr); + } else { + // リテラルのみ許可(P0) + let lit = self.parse_literal_only()?; + self.consume(TokenType::FAT_ARROW)?; + let expr = self.parse_expression()?; + arms.push((lit, expr)); + } + + // 区切り(カンマや改行を許可) + if self.match_token(&TokenType::COMMA) { self.advance(); } + if self.match_token(&TokenType::NEWLINE) { self.advance(); } + } + + self.consume(TokenType::RBRACE)?; + let else_expr = else_expr.ok_or(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "else => in peek".to_string(), + line: self.current_token().line, + })?; + + Ok(ASTNode::PeekExpr { + scrutinee: Box::new(scrutinee), + arms, + else_expr: Box::new(else_expr), + span: Span::unknown(), + }) + } + + fn parse_literal_only(&mut self) -> Result { + match &self.current_token().token_type { + TokenType::STRING(s) => { let v = crate::ast::LiteralValue::String(s.clone()); self.advance(); Ok(v) } + TokenType::NUMBER(n) => { let v = crate::ast::LiteralValue::Integer(*n); self.advance(); Ok(v) } + TokenType::FLOAT(f) => { let v = crate::ast::LiteralValue::Float(*f); self.advance(); Ok(v) } + TokenType::TRUE => { self.advance(); Ok(crate::ast::LiteralValue::Bool(true)) } + TokenType::FALSE => { self.advance(); Ok(crate::ast::LiteralValue::Bool(false)) } + TokenType::NULL => { self.advance(); Ok(crate::ast::LiteralValue::Null) } + _ => { + let line = self.current_token().line; + Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "literal".to_string(), line }) + } + } + } /// 関数・メソッド呼び出しをパース fn parse_call(&mut self) -> Result { diff --git a/src/parser/statements.rs b/src/parser/statements.rs index 6706895c..77ca7746 100644 --- a/src/parser/statements.rs +++ b/src/parser/statements.rs @@ -40,6 +40,9 @@ impl NyashParser { TokenType::BREAK => { self.parse_break() }, + TokenType::CONTINUE => { + self.parse_continue() + }, TokenType::RETURN => { self.parse_return() }, @@ -204,6 +207,12 @@ impl NyashParser { self.advance(); // consume 'break' Ok(ASTNode::Break { span: Span::unknown() }) } + + /// continue文をパース + pub(super) fn parse_continue(&mut self) -> Result { + self.advance(); // consume 'continue' + Ok(ASTNode::Continue { span: Span::unknown() }) + } /// return文をパース pub(super) fn parse_return(&mut self) -> Result { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 86fad9db..5b880274 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -24,10 +24,12 @@ pub enum TokenType { GLOBAL, SINGLETON, NEW, + PEEK, IF, ELSE, LOOP, BREAK, + CONTINUE, RETURN, FUNCTION, PRINT, @@ -55,7 +57,8 @@ pub enum TokenType { USING, // using (名前空間インポート) // 演算子 (長いものから先に定義) - ARROW, // >> + ARROW, // >> (legacy arrow) + FAT_ARROW, // => (peek arms) EQUALS, // == NotEquals, // != LessEquals, // <= @@ -73,6 +76,7 @@ pub enum TokenType { // 記号 DOT, // . + DOUBLE_COLON, // :: (Parent::method) - P1用(定義のみ) LPAREN, // ( RPAREN, // ) LBRACE, // { @@ -193,6 +197,16 @@ impl NyashTokenizer { self.advance(); Ok(Token::new(TokenType::ARROW, start_line, start_column)) } + Some(':') if self.peek_char() == Some(':') => { + self.advance(); + self.advance(); + Ok(Token::new(TokenType::DOUBLE_COLON, start_line, start_column)) + } + Some('=') if self.peek_char() == Some('>') => { + self.advance(); + self.advance(); + Ok(Token::new(TokenType::FAT_ARROW, start_line, start_column)) + } Some('=') if self.peek_char() == Some('=') => { self.advance(); self.advance(); @@ -385,16 +399,18 @@ impl NyashTokenizer { } } - // キーワードチェック + // キーワードチェック let tok = match identifier.as_str() { "box" => TokenType::BOX, "global" => TokenType::GLOBAL, "singleton" => TokenType::SINGLETON, "new" => TokenType::NEW, + "peek" => TokenType::PEEK, "if" => TokenType::IF, "else" => TokenType::ELSE, "loop" => TokenType::LOOP, "break" => TokenType::BREAK, + "continue" => TokenType::CONTINUE, "return" => TokenType::RETURN, "function" => TokenType::FUNCTION, // Alias support: `fn` as shorthand for function