303 lines
9.1 KiB
Plaintext
303 lines
9.1 KiB
Plaintext
|
|
================================================================================
|
|||
|
|
Nyash 糖衣構文 可逆フォーマッター仕様
|
|||
|
|
2025-09-03
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
【コンセプト】
|
|||
|
|
極限短縮構文 ⇄ 標準構文の相互変換ツール(Nyashで実装)
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
1. 基本設計
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
box NyashFormatter {
|
|||
|
|
mode: FormatterMode // Compact | Standard | Verbose
|
|||
|
|
|
|||
|
|
birth() {
|
|||
|
|
me.mode = FormatterMode.Standard
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 短縮 → 標準への展開
|
|||
|
|
expand(code: StringBox) -> StringBox {
|
|||
|
|
code |> tokenize
|
|||
|
|
|> expandSugar
|
|||
|
|
|> format(me.mode)
|
|||
|
|
|> toString
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 標準 → 短縮への圧縮
|
|||
|
|
compact(code: StringBox) -> StringBox {
|
|||
|
|
code |> tokenize
|
|||
|
|
|> compactSugar
|
|||
|
|
|> minify
|
|||
|
|
|> toString
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
2. 変換規則マッピング
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
// 糖衣構文の変換ルールを定義
|
|||
|
|
static box SugarRules {
|
|||
|
|
// 暗黙変数の展開
|
|||
|
|
expandImplicit(ast) {
|
|||
|
|
peek ast {
|
|||
|
|
ImplicitVar("$_") => {
|
|||
|
|
// コンテキストから引数名を推論
|
|||
|
|
local argName = inferArgumentName(ast.context)
|
|||
|
|
Identifier(argName)
|
|||
|
|
}
|
|||
|
|
ImplicitVar("$1") => Identifier("_arg1")
|
|||
|
|
ImplicitVar("$2") => Identifier("_arg2")
|
|||
|
|
else => ast
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// パイプラインの展開
|
|||
|
|
expandPipeline(ast) {
|
|||
|
|
peek ast {
|
|||
|
|
Pipeline(expr, func) => {
|
|||
|
|
// x |> f(a,b) → f(a, b, x)
|
|||
|
|
peek func {
|
|||
|
|
Call(name, args) => Call(name, [...args, expr])
|
|||
|
|
PropertyAccess(prop) => PropertyAccess(expr, prop)
|
|||
|
|
else => Call(func, [expr])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else => ast
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 短縮演算子の展開
|
|||
|
|
expandOperators(ast) {
|
|||
|
|
peek ast {
|
|||
|
|
MapOp(list, expr) => {
|
|||
|
|
// list /: expr → list.map(expr)
|
|||
|
|
MethodCall(list, "map", [expandLambda(expr)])
|
|||
|
|
}
|
|||
|
|
FilterOp(list, expr) => {
|
|||
|
|
// list \: expr → list.filter(expr)
|
|||
|
|
MethodCall(list, "filter", [expandLambda(expr)])
|
|||
|
|
}
|
|||
|
|
else => ast
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
3. フォーマットモード
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
// 3段階の詳細度
|
|||
|
|
enum FormatterMode {
|
|||
|
|
Compact, // 極限短縮
|
|||
|
|
Standard, // 標準的な記法
|
|||
|
|
Verbose // 初心者向け詳細
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// モード別の変換例
|
|||
|
|
box FormatExamples {
|
|||
|
|
showModes() {
|
|||
|
|
local code = "[$.name for users if $.age > 18]"
|
|||
|
|
|
|||
|
|
print("=== Compact (極限) ===")
|
|||
|
|
print(code)
|
|||
|
|
|
|||
|
|
print("=== Standard (標準) ===")
|
|||
|
|
local standard = formatter.expand(code)
|
|||
|
|
print(standard)
|
|||
|
|
// [user.name for user in users if user.age > 18]
|
|||
|
|
|
|||
|
|
print("=== Verbose (詳細) ===")
|
|||
|
|
local verbose = formatter.expandVerbose(code)
|
|||
|
|
print(verbose)
|
|||
|
|
// local result = new ArrayBox()
|
|||
|
|
// for user in users {
|
|||
|
|
// if user.age > 18 {
|
|||
|
|
// result.push(user.name)
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
4. 実装例:主要な糖衣構文の変換
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
// 1. 暗黙変数
|
|||
|
|
"list /: {$_*2}"
|
|||
|
|
↓ expand
|
|||
|
|
"list.map(fn(item) { return item * 2 })"
|
|||
|
|
|
|||
|
|
// 2. パイプライン
|
|||
|
|
"input |> trim |> upper"
|
|||
|
|
↓ expand
|
|||
|
|
"upper(trim(input))"
|
|||
|
|
|
|||
|
|
// 3. null安全
|
|||
|
|
"user?.profile?.name ?? 'Anonymous'"
|
|||
|
|
↓ expand
|
|||
|
|
"peek user {
|
|||
|
|
null => 'Anonymous'
|
|||
|
|
else => peek user.profile {
|
|||
|
|
null => 'Anonymous'
|
|||
|
|
else => peek user.profile.name {
|
|||
|
|
null => 'Anonymous'
|
|||
|
|
else => user.profile.name
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}"
|
|||
|
|
|
|||
|
|
// 4. キーワード短縮
|
|||
|
|
"l x = 42; ^ x*2"
|
|||
|
|
↓ expand
|
|||
|
|
"local x = 42
|
|||
|
|
return x * 2"
|
|||
|
|
|
|||
|
|
// 5. 演算子セクション
|
|||
|
|
"nums /: (+1)"
|
|||
|
|
↓ expand
|
|||
|
|
"nums.map(fn(x) { return x + 1 })"
|
|||
|
|
|
|||
|
|
// 6. リスト内包
|
|||
|
|
"[x*x for x in nums if x%2==0]"
|
|||
|
|
↓ expand (Standard)
|
|||
|
|
"nums.filter(fn(x) { return x % 2 == 0 })
|
|||
|
|
.map(fn(x) { return x * x })"
|
|||
|
|
↓ expand (Verbose)
|
|||
|
|
"local result = new ArrayBox()
|
|||
|
|
for x in nums {
|
|||
|
|
if x % 2 == 0 {
|
|||
|
|
result.push(x * x)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
result"
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
5. 使用例:コマンドラインツール
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
// nyashfmt.nyash - フォーマッターCLI
|
|||
|
|
static box Main {
|
|||
|
|
main(args) {
|
|||
|
|
local formatter = new NyashFormatter()
|
|||
|
|
local mode = args[1] ?? "standard"
|
|||
|
|
local file = args[2]
|
|||
|
|
|
|||
|
|
peek mode {
|
|||
|
|
"expand" => {
|
|||
|
|
local code = FileBox.read(file)
|
|||
|
|
local expanded = formatter.expand(code)
|
|||
|
|
print(expanded)
|
|||
|
|
}
|
|||
|
|
"compact" => {
|
|||
|
|
local code = FileBox.read(file)
|
|||
|
|
local compacted = formatter.compact(code)
|
|||
|
|
print(compacted)
|
|||
|
|
}
|
|||
|
|
"check" => {
|
|||
|
|
// 可逆性チェック
|
|||
|
|
local original = FileBox.read(file)
|
|||
|
|
local round = original |> formatter.compact
|
|||
|
|
|> formatter.expand
|
|||
|
|
|> formatter.compact
|
|||
|
|
|
|||
|
|
if round == formatter.compact(original) {
|
|||
|
|
print("✅ 可逆変換OK")
|
|||
|
|
} else {
|
|||
|
|
print("❌ 変換エラー:情報が失われています")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else => {
|
|||
|
|
print("Usage: nyashfmt [expand|compact|check] <file>")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
6. エディタ統合
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
// VSCode/エディタ向けのフォーマッター統合
|
|||
|
|
box EditorFormatter {
|
|||
|
|
// 選択範囲の展開/圧縮
|
|||
|
|
formatSelection(text, mode) {
|
|||
|
|
local formatter = new NyashFormatter()
|
|||
|
|
|
|||
|
|
peek mode {
|
|||
|
|
"toggle" => {
|
|||
|
|
// 自動判定:短縮記法が含まれていれば展開、なければ圧縮
|
|||
|
|
if me.hasShortSyntax(text) {
|
|||
|
|
formatter.expand(text)
|
|||
|
|
} else {
|
|||
|
|
formatter.compact(text)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
"expand" => formatter.expand(text)
|
|||
|
|
"compact" => formatter.compact(text)
|
|||
|
|
else => text
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ホバー時のツールチップ表示
|
|||
|
|
showExpanded(position) {
|
|||
|
|
local ast = me.getAstAt(position)
|
|||
|
|
local expanded = SugarRules.expandNode(ast)
|
|||
|
|
|
|||
|
|
// ツールチップに展開形を表示
|
|||
|
|
return "展開形: " + expanded.toString()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
7. 学習モード(初心者支援)
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
box LearningMode {
|
|||
|
|
// 段階的に糖衣構文を導入
|
|||
|
|
suggestSugar(code) {
|
|||
|
|
local suggestions = new ArrayBox()
|
|||
|
|
|
|||
|
|
// パターンマッチで改善可能な箇所を検出
|
|||
|
|
if code.contains("list.map(fn(x) { return") {
|
|||
|
|
suggestions.push({
|
|||
|
|
original: "list.map(fn(x) { return x * 2 })",
|
|||
|
|
sugar: "list /: {$_*2}",
|
|||
|
|
explanation: "/: は map の短縮記法です"
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if code.contains("if x != null") {
|
|||
|
|
suggestions.push({
|
|||
|
|
original: "if x != null { x.method() } else { null }",
|
|||
|
|
sugar: "x?.method()",
|
|||
|
|
explanation: "?. でnullチェックを簡潔に"
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return suggestions
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
8. 設定ファイル
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
// .nyashfmt.json - プロジェクト別設定
|
|||
|
|
{
|
|||
|
|
"mode": "standard",
|
|||
|
|
"rules": {
|
|||
|
|
"implicit_variable": true, // $_ を許可
|
|||
|
|
"short_keywords": true, // l, ^, fn を許可
|
|||
|
|
"unicode_operators": false, // λ, → は使わない
|
|||
|
|
"pipeline": true, // |> を許可
|
|||
|
|
"list_comprehension": true, // [...for...] を許可
|
|||
|
|
"operator_sections": true // (+1) を許可
|
|||
|
|
},
|
|||
|
|
"expand_on_save": false, // 保存時に展開
|
|||
|
|
"check_reversibility": true // 可逆性チェック
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
================================================================================
|