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:
Moe Charm
2025-09-03 15:26:15 +09:00
parent ceb22b6c18
commit 6d79d7d3ac
20 changed files with 1581 additions and 4 deletions

View 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())

View 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)

View 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)

View File

@ -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_COLONP1用、定義のみ)
- 既存の `>>`→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 SnapshotPlugin-First / Core最小化
このスナップショットは最新の到達点へ更新済み。再起動時はここから辿る。

View File

@ -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文地獄を避けられる
- 初学者にも分かりやすい
プログラミング言語設計の観点から、必須機能の優先順位と実装方法を提案してください。

View File

@ -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 二分探索
================================================================================

View File

@ -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は最内関数スコープから脱出
================================================================================

View File

@ -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送り
✓ デシュガーでシンプル実装(最適化は後回し)
================================================================================

View File

@ -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. 段階的に導入(基本形→拡張形)
================================================================================

View File

@ -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
気楽に、楽しく、ユニークな提案をお聞かせください!
「こんなの思いついたにゃ〜」という感じで!

View File

@ -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個厳守ではなく、必要なものは追加する方針です。
プログラミング言語設計の観点から、この設計の妥当性と改善案をお聞かせください。

View File

@ -194,6 +194,13 @@ pub enum ExpressionNode {
MeExpression {
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 {
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 <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: Box<ASTNode>,
@ -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,
}
}
}

View File

@ -34,6 +34,9 @@ impl NyashInterpreter {
ControlFlow::Break => {
return Err(RuntimeError::BreakOutsideLoop);
}
ControlFlow::Continue => {
return Err(RuntimeError::BreakOutsideLoop);
}
ControlFlow::Return(_) => {
return Err(RuntimeError::ReturnOutsideFunction);
}

View File

@ -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を返す

View File

@ -59,6 +59,7 @@ pub use errors::RuntimeError;
pub enum ControlFlow {
None,
Break,
Continue,
Return(Box<dyn NyashBox>),
Throw(Box<dyn NyashBox>),
}

View File

@ -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()));

View File

@ -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 {

View File

@ -192,6 +192,10 @@ impl NyashParser {
/// 単項演算子をパース
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) {
self.advance(); // consume '-'
let operand = self.parse_unary()?; // 再帰的に単項演算をパース
@ -223,6 +227,73 @@ impl NyashParser {
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> {

View File

@ -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<ASTNode, ParseError> {
self.advance(); // consume 'continue'
Ok(ASTNode::Continue { span: Span::unknown() })
}
/// return文をパース
pub(super) fn parse_return(&mut self) -> Result<ASTNode, ParseError> {

View File

@ -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