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.hako - フォーマッター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. 設定ファイル
|
||
================================================================================
|
||
|
||
// .hakofmt.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 // 可逆性チェック
|
||
}
|
||
|
||
================================================================================ |