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
This commit is contained in:
18
apps/box-field-decl-demo/main.nyash
Normal file
18
apps/box-field-decl-demo/main.nyash
Normal file
@ -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())
|
||||||
14
apps/loop-continue-demo/main.nyash
Normal file
14
apps/loop-continue-demo/main.nyash
Normal file
@ -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)
|
||||||
9
apps/peek-demo/main.nyash
Normal file
9
apps/peek-demo/main.nyash
Normal file
@ -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)
|
||||||
@ -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 <expr> { <lit> => <arm> ... else => <arm> }`
|
||||||
|
- else必須。<arm>は式 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最小化)
|
# 🎯 CURRENT TASK - 2025-08-30 Restart Snapshot(Plugin-First / Core最小化)
|
||||||
|
|
||||||
このスナップショットは最新の到達点へ更新済み。再起動時はここから辿る。
|
このスナップショットは最新の到達点へ更新済み。再起動時はここから辿る。
|
||||||
|
|||||||
@ -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文地獄を避けられる
|
||||||
|
- 初学者にも分かりやすい
|
||||||
|
|
||||||
|
プログラミング言語設計の観点から、必須機能の優先順位と実装方法を提案してください。
|
||||||
@ -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 <expression> {
|
||||||
|
<pattern> => <expression-or-block>
|
||||||
|
<pattern> => <expression-or-block>
|
||||||
|
else => <expression-or-block> // 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 <expr> { の識別
|
||||||
|
- => トークンの追加
|
||||||
|
- 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 二分探索
|
||||||
|
|
||||||
|
================================================================================
|
||||||
@ -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<Expr>,
|
||||||
|
arms: Vec<(Pattern, BlockOrExpr)>,
|
||||||
|
else_arm: Box<BlockOrExpr>,
|
||||||
|
},
|
||||||
|
FnLiteral {
|
||||||
|
params: Vec<String>,
|
||||||
|
body: Block,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Stmt {
|
||||||
|
// ...
|
||||||
|
Continue,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
【セマンティクス】
|
||||||
|
- peekは式として値を返す
|
||||||
|
- else必須のバリデーション追加
|
||||||
|
- returnは最内関数スコープから脱出
|
||||||
|
|
||||||
|
================================================================================
|
||||||
@ -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送り
|
||||||
|
✓ デシュガーでシンプル実装(最適化は後回し)
|
||||||
|
|
||||||
|
================================================================================
|
||||||
@ -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. 段階的に導入(基本形→拡張形)
|
||||||
|
|
||||||
|
================================================================================
|
||||||
@ -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!
|
||||||
|
|
||||||
|
気楽に、楽しく、ユニークな提案をお聞かせください!
|
||||||
|
「こんなの思いついたにゃ〜」という感じで!
|
||||||
@ -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個厳守ではなく、必要なものは追加する方針です。
|
||||||
|
|
||||||
|
プログラミング言語設計の観点から、この設計の妥当性と改善案をお聞かせください。
|
||||||
30
src/ast.rs
30
src/ast.rs
@ -194,6 +194,13 @@ pub enum ExpressionNode {
|
|||||||
MeExpression {
|
MeExpression {
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
|
/// peek式: peek <expr> { lit => expr, ... else => expr }
|
||||||
|
PeekExpr {
|
||||||
|
scrutinee: Box<ASTNode>,
|
||||||
|
arms: Vec<(LiteralValue, ASTNode)>,
|
||||||
|
else_expr: Box<ASTNode>,
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 文ノード - 実行可能なアクション
|
/// 文ノード - 実行可能なアクション
|
||||||
@ -215,6 +222,9 @@ pub enum StatementNode {
|
|||||||
Break {
|
Break {
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
|
Continue {
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
Include {
|
Include {
|
||||||
filename: String,
|
filename: String,
|
||||||
span: Span,
|
span: Span,
|
||||||
@ -413,6 +423,10 @@ pub enum ASTNode {
|
|||||||
Break {
|
Break {
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
|
/// continue文
|
||||||
|
Continue {
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
|
|
||||||
/// using文: using namespace_name
|
/// using文: using namespace_name
|
||||||
UsingStatement {
|
UsingStatement {
|
||||||
@ -433,6 +447,14 @@ pub enum ASTNode {
|
|||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// peek式: peek <expr> { lit => expr, ... else => expr }
|
||||||
|
PeekExpr {
|
||||||
|
scrutinee: Box<ASTNode>,
|
||||||
|
arms: Vec<(LiteralValue, ASTNode)>,
|
||||||
|
else_expr: Box<ASTNode>,
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
|
|
||||||
/// arrow文: (sender >> receiver).method(args)
|
/// arrow文: (sender >> receiver).method(args)
|
||||||
Arrow {
|
Arrow {
|
||||||
sender: Box<ASTNode>,
|
sender: Box<ASTNode>,
|
||||||
@ -619,6 +641,7 @@ impl ASTNode {
|
|||||||
ASTNode::Loop { .. } => "Loop",
|
ASTNode::Loop { .. } => "Loop",
|
||||||
ASTNode::Return { .. } => "Return",
|
ASTNode::Return { .. } => "Return",
|
||||||
ASTNode::Break { .. } => "Break",
|
ASTNode::Break { .. } => "Break",
|
||||||
|
ASTNode::Continue { .. } => "Continue",
|
||||||
ASTNode::UsingStatement { .. } => "UsingStatement",
|
ASTNode::UsingStatement { .. } => "UsingStatement",
|
||||||
ASTNode::BoxDeclaration { .. } => "BoxDeclaration",
|
ASTNode::BoxDeclaration { .. } => "BoxDeclaration",
|
||||||
ASTNode::FunctionDeclaration { .. } => "FunctionDeclaration",
|
ASTNode::FunctionDeclaration { .. } => "FunctionDeclaration",
|
||||||
@ -644,6 +667,7 @@ impl ASTNode {
|
|||||||
ASTNode::TryCatch { .. } => "TryCatch",
|
ASTNode::TryCatch { .. } => "TryCatch",
|
||||||
ASTNode::Throw { .. } => "Throw",
|
ASTNode::Throw { .. } => "Throw",
|
||||||
ASTNode::AwaitExpression { .. } => "AwaitExpression",
|
ASTNode::AwaitExpression { .. } => "AwaitExpression",
|
||||||
|
ASTNode::PeekExpr { .. } => "PeekExpr",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -672,6 +696,7 @@ impl ASTNode {
|
|||||||
ASTNode::FromCall { .. } => ASTNodeType::Expression,
|
ASTNode::FromCall { .. } => ASTNodeType::Expression,
|
||||||
ASTNode::ThisField { .. } => ASTNodeType::Expression,
|
ASTNode::ThisField { .. } => ASTNodeType::Expression,
|
||||||
ASTNode::MeField { .. } => ASTNodeType::Expression,
|
ASTNode::MeField { .. } => ASTNodeType::Expression,
|
||||||
|
ASTNode::PeekExpr { .. } => ASTNodeType::Expression,
|
||||||
|
|
||||||
// Statement nodes - 実行可能なアクション
|
// Statement nodes - 実行可能なアクション
|
||||||
ASTNode::Program { .. } => ASTNodeType::Statement, // プログラム全体
|
ASTNode::Program { .. } => ASTNodeType::Statement, // プログラム全体
|
||||||
@ -679,6 +704,7 @@ impl ASTNode {
|
|||||||
ASTNode::Print { .. } => ASTNodeType::Statement,
|
ASTNode::Print { .. } => ASTNodeType::Statement,
|
||||||
ASTNode::Return { .. } => ASTNodeType::Statement,
|
ASTNode::Return { .. } => ASTNodeType::Statement,
|
||||||
ASTNode::Break { .. } => ASTNodeType::Statement,
|
ASTNode::Break { .. } => ASTNodeType::Statement,
|
||||||
|
ASTNode::Continue { .. } => ASTNodeType::Statement,
|
||||||
ASTNode::UsingStatement { .. } => ASTNodeType::Statement,
|
ASTNode::UsingStatement { .. } => ASTNodeType::Statement,
|
||||||
ASTNode::GlobalVar { .. } => ASTNodeType::Statement,
|
ASTNode::GlobalVar { .. } => ASTNodeType::Statement,
|
||||||
ASTNode::Include { .. } => ASTNodeType::Statement,
|
ASTNode::Include { .. } => ASTNodeType::Statement,
|
||||||
@ -728,6 +754,7 @@ impl ASTNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ASTNode::Break { .. } => "Break".to_string(),
|
ASTNode::Break { .. } => "Break".to_string(),
|
||||||
|
ASTNode::Continue { .. } => "Continue".to_string(),
|
||||||
ASTNode::UsingStatement { namespace_name, .. } => {
|
ASTNode::UsingStatement { namespace_name, .. } => {
|
||||||
format!("UsingStatement({})", namespace_name)
|
format!("UsingStatement({})", namespace_name)
|
||||||
}
|
}
|
||||||
@ -823,6 +850,7 @@ impl ASTNode {
|
|||||||
ASTNode::AwaitExpression { expression, .. } => {
|
ASTNode::AwaitExpression { expression, .. } => {
|
||||||
format!("Await({:?})", expression)
|
format!("Await({:?})", expression)
|
||||||
}
|
}
|
||||||
|
ASTNode::PeekExpr { .. } => "PeekExpr".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -836,6 +864,7 @@ impl ASTNode {
|
|||||||
ASTNode::Loop { span, .. } => *span,
|
ASTNode::Loop { span, .. } => *span,
|
||||||
ASTNode::Return { span, .. } => *span,
|
ASTNode::Return { span, .. } => *span,
|
||||||
ASTNode::Break { span, .. } => *span,
|
ASTNode::Break { span, .. } => *span,
|
||||||
|
ASTNode::Continue { span, .. } => *span,
|
||||||
ASTNode::UsingStatement { span, .. } => *span,
|
ASTNode::UsingStatement { span, .. } => *span,
|
||||||
ASTNode::Nowait { span, .. } => *span,
|
ASTNode::Nowait { span, .. } => *span,
|
||||||
ASTNode::Arrow { span, .. } => *span,
|
ASTNode::Arrow { span, .. } => *span,
|
||||||
@ -861,6 +890,7 @@ impl ASTNode {
|
|||||||
ASTNode::Outbox { span, .. } => *span,
|
ASTNode::Outbox { span, .. } => *span,
|
||||||
ASTNode::FunctionCall { span, .. } => *span,
|
ASTNode::FunctionCall { span, .. } => *span,
|
||||||
ASTNode::AwaitExpression { span, .. } => *span,
|
ASTNode::AwaitExpression { span, .. } => *span,
|
||||||
|
ASTNode::PeekExpr { span, .. } => *span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,9 @@ impl NyashInterpreter {
|
|||||||
ControlFlow::Break => {
|
ControlFlow::Break => {
|
||||||
return Err(RuntimeError::BreakOutsideLoop);
|
return Err(RuntimeError::BreakOutsideLoop);
|
||||||
}
|
}
|
||||||
|
ControlFlow::Continue => {
|
||||||
|
return Err(RuntimeError::BreakOutsideLoop);
|
||||||
|
}
|
||||||
ControlFlow::Return(_) => {
|
ControlFlow::Return(_) => {
|
||||||
return Err(RuntimeError::ReturnOutsideFunction);
|
return Err(RuntimeError::ReturnOutsideFunction);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -129,6 +129,24 @@ impl NyashInterpreter {
|
|||||||
ASTNode::Arrow { sender, receiver, .. } => {
|
ASTNode::Arrow { sender, receiver, .. } => {
|
||||||
self.execute_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, .. } => {
|
ASTNode::Include { filename, .. } => {
|
||||||
// include式: 最初のstatic boxを返す
|
// include式: 最初のstatic boxを返す
|
||||||
|
|||||||
@ -59,6 +59,7 @@ pub use errors::RuntimeError;
|
|||||||
pub enum ControlFlow {
|
pub enum ControlFlow {
|
||||||
None,
|
None,
|
||||||
Break,
|
Break,
|
||||||
|
Continue,
|
||||||
Return(Box<dyn NyashBox>),
|
Return(Box<dyn NyashBox>),
|
||||||
Throw(Box<dyn NyashBox>),
|
Throw(Box<dyn NyashBox>),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -86,6 +86,10 @@ impl NyashInterpreter {
|
|||||||
self.control_flow = super::ControlFlow::Break;
|
self.control_flow = super::ControlFlow::Break;
|
||||||
Ok(Box::new(VoidBox::new()))
|
Ok(Box::new(VoidBox::new()))
|
||||||
}
|
}
|
||||||
|
ASTNode::Continue { .. } => {
|
||||||
|
self.control_flow = super::ControlFlow::Continue;
|
||||||
|
Ok(Box::new(VoidBox::new()))
|
||||||
|
}
|
||||||
|
|
||||||
ASTNode::Nowait { variable, expression, .. } => {
|
ASTNode::Nowait { variable, expression, .. } => {
|
||||||
self.execute_nowait(variable, expression)
|
self.execute_nowait(variable, expression)
|
||||||
@ -272,6 +276,10 @@ impl NyashInterpreter {
|
|||||||
self.control_flow = super::ControlFlow::None;
|
self.control_flow = super::ControlFlow::None;
|
||||||
return Ok(Box::new(VoidBox::new()));
|
return Ok(Box::new(VoidBox::new()));
|
||||||
}
|
}
|
||||||
|
super::ControlFlow::Continue => {
|
||||||
|
self.control_flow = super::ControlFlow::None;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
super::ControlFlow::Return(_) => {
|
super::ControlFlow::Return(_) => {
|
||||||
// returnはループを抜けるが、上位に伝播
|
// returnはループを抜けるが、上位に伝播
|
||||||
return Ok(Box::new(VoidBox::new()));
|
return Ok(Box::new(VoidBox::new()));
|
||||||
|
|||||||
@ -482,8 +482,16 @@ impl NyashParser {
|
|||||||
|
|
||||||
methods.insert(field_or_method, method);
|
methods.insert(field_or_method, method);
|
||||||
} else {
|
} else {
|
||||||
// フィールド定義
|
// フィールド定義(P0: 型注釈 name: Type を受理して破棄)
|
||||||
fields.push(field_or_method);
|
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 {
|
} else {
|
||||||
return Err(ParseError::UnexpectedToken {
|
return Err(ParseError::UnexpectedToken {
|
||||||
|
|||||||
@ -192,6 +192,10 @@ impl NyashParser {
|
|||||||
|
|
||||||
/// 単項演算子をパース
|
/// 単項演算子をパース
|
||||||
fn parse_unary(&mut self) -> Result<ASTNode, ParseError> {
|
fn parse_unary(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
// peek式の先読み
|
||||||
|
if self.match_token(&TokenType::PEEK) {
|
||||||
|
return self.parse_peek_expr();
|
||||||
|
}
|
||||||
if self.match_token(&TokenType::MINUS) {
|
if self.match_token(&TokenType::MINUS) {
|
||||||
self.advance(); // consume '-'
|
self.advance(); // consume '-'
|
||||||
let operand = self.parse_unary()?; // 再帰的に単項演算をパース
|
let operand = self.parse_unary()?; // 再帰的に単項演算をパース
|
||||||
@ -223,6 +227,73 @@ impl NyashParser {
|
|||||||
|
|
||||||
self.parse_call()
|
self.parse_call()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// peek式: peek <expr> { lit => expr ... else => expr }
|
||||||
|
fn parse_peek_expr(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
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<ASTNode> = 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 => <expr> 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<crate::ast::LiteralValue, ParseError> {
|
||||||
|
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<ASTNode, ParseError> {
|
fn parse_call(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
|||||||
@ -40,6 +40,9 @@ impl NyashParser {
|
|||||||
TokenType::BREAK => {
|
TokenType::BREAK => {
|
||||||
self.parse_break()
|
self.parse_break()
|
||||||
},
|
},
|
||||||
|
TokenType::CONTINUE => {
|
||||||
|
self.parse_continue()
|
||||||
|
},
|
||||||
TokenType::RETURN => {
|
TokenType::RETURN => {
|
||||||
self.parse_return()
|
self.parse_return()
|
||||||
},
|
},
|
||||||
@ -204,6 +207,12 @@ impl NyashParser {
|
|||||||
self.advance(); // consume 'break'
|
self.advance(); // consume 'break'
|
||||||
Ok(ASTNode::Break { span: Span::unknown() })
|
Ok(ASTNode::Break { span: Span::unknown() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// continue文をパース
|
||||||
|
pub(super) fn parse_continue(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
self.advance(); // consume 'continue'
|
||||||
|
Ok(ASTNode::Continue { span: Span::unknown() })
|
||||||
|
}
|
||||||
|
|
||||||
/// return文をパース
|
/// return文をパース
|
||||||
pub(super) fn parse_return(&mut self) -> Result<ASTNode, ParseError> {
|
pub(super) fn parse_return(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
|
|||||||
@ -24,10 +24,12 @@ pub enum TokenType {
|
|||||||
GLOBAL,
|
GLOBAL,
|
||||||
SINGLETON,
|
SINGLETON,
|
||||||
NEW,
|
NEW,
|
||||||
|
PEEK,
|
||||||
IF,
|
IF,
|
||||||
ELSE,
|
ELSE,
|
||||||
LOOP,
|
LOOP,
|
||||||
BREAK,
|
BREAK,
|
||||||
|
CONTINUE,
|
||||||
RETURN,
|
RETURN,
|
||||||
FUNCTION,
|
FUNCTION,
|
||||||
PRINT,
|
PRINT,
|
||||||
@ -55,7 +57,8 @@ pub enum TokenType {
|
|||||||
USING, // using (名前空間インポート)
|
USING, // using (名前空間インポート)
|
||||||
|
|
||||||
// 演算子 (長いものから先に定義)
|
// 演算子 (長いものから先に定義)
|
||||||
ARROW, // >>
|
ARROW, // >> (legacy arrow)
|
||||||
|
FAT_ARROW, // => (peek arms)
|
||||||
EQUALS, // ==
|
EQUALS, // ==
|
||||||
NotEquals, // !=
|
NotEquals, // !=
|
||||||
LessEquals, // <=
|
LessEquals, // <=
|
||||||
@ -73,6 +76,7 @@ pub enum TokenType {
|
|||||||
|
|
||||||
// 記号
|
// 記号
|
||||||
DOT, // .
|
DOT, // .
|
||||||
|
DOUBLE_COLON, // :: (Parent::method) - P1用(定義のみ)
|
||||||
LPAREN, // (
|
LPAREN, // (
|
||||||
RPAREN, // )
|
RPAREN, // )
|
||||||
LBRACE, // {
|
LBRACE, // {
|
||||||
@ -193,6 +197,16 @@ impl NyashTokenizer {
|
|||||||
self.advance();
|
self.advance();
|
||||||
Ok(Token::new(TokenType::ARROW, start_line, start_column))
|
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('=') => {
|
Some('=') if self.peek_char() == Some('=') => {
|
||||||
self.advance();
|
self.advance();
|
||||||
self.advance();
|
self.advance();
|
||||||
@ -385,16 +399,18 @@ impl NyashTokenizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// キーワードチェック
|
// キーワードチェック
|
||||||
let tok = match identifier.as_str() {
|
let tok = match identifier.as_str() {
|
||||||
"box" => TokenType::BOX,
|
"box" => TokenType::BOX,
|
||||||
"global" => TokenType::GLOBAL,
|
"global" => TokenType::GLOBAL,
|
||||||
"singleton" => TokenType::SINGLETON,
|
"singleton" => TokenType::SINGLETON,
|
||||||
"new" => TokenType::NEW,
|
"new" => TokenType::NEW,
|
||||||
|
"peek" => TokenType::PEEK,
|
||||||
"if" => TokenType::IF,
|
"if" => TokenType::IF,
|
||||||
"else" => TokenType::ELSE,
|
"else" => TokenType::ELSE,
|
||||||
"loop" => TokenType::LOOP,
|
"loop" => TokenType::LOOP,
|
||||||
"break" => TokenType::BREAK,
|
"break" => TokenType::BREAK,
|
||||||
|
"continue" => TokenType::CONTINUE,
|
||||||
"return" => TokenType::RETURN,
|
"return" => TokenType::RETURN,
|
||||||
"function" => TokenType::FUNCTION,
|
"function" => TokenType::FUNCTION,
|
||||||
// Alias support: `fn` as shorthand for function
|
// Alias support: `fn` as shorthand for function
|
||||||
|
|||||||
Reference in New Issue
Block a user