2025-09-03 13:58:52 +09:00
|
|
|
|
# ANCP Implementation Plan
|
|
|
|
|
|
|
|
|
|
|
|
## 🎯 実装戦略:段階的アプローチ
|
|
|
|
|
|
|
|
|
|
|
|
### 基本方針
|
|
|
|
|
|
1. **最小実装から開始**: 20語の固定辞書でMVP
|
|
|
|
|
|
2. **段階的拡張**: 機能を少しずつ追加
|
|
|
|
|
|
3. **早期統合**: スモークテストと早期に統合
|
|
|
|
|
|
4. **継続的検証**: 各段階で往復テスト実施
|
|
|
|
|
|
|
|
|
|
|
|
## 📅 Week 1: 基礎実装
|
|
|
|
|
|
|
|
|
|
|
|
### Day 1-2: プロジェクトセットアップ
|
|
|
|
|
|
```toml
|
|
|
|
|
|
# Cargo.toml に追加
|
|
|
|
|
|
[features]
|
|
|
|
|
|
ancp = []
|
|
|
|
|
|
|
|
|
|
|
|
[dependencies]
|
|
|
|
|
|
phf = "0.11" # 静的マップ用
|
|
|
|
|
|
tiktoken-rs = "0.5" # トークン計測用(optional)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Day 3-4: 基本Transcoder実装
|
|
|
|
|
|
```rust
|
|
|
|
|
|
// src/ancp/mod.rs
|
|
|
|
|
|
pub mod transcoder;
|
|
|
|
|
|
pub mod mappings;
|
|
|
|
|
|
pub mod error;
|
|
|
|
|
|
|
|
|
|
|
|
// src/ancp/transcoder.rs
|
|
|
|
|
|
use phf::phf_map;
|
|
|
|
|
|
|
|
|
|
|
|
static NYASH_TO_ANCP: phf::Map<&'static str, &'static str> = phf_map! {
|
|
|
|
|
|
"box" => "$",
|
|
|
|
|
|
"new" => "n",
|
|
|
|
|
|
"me" => "m",
|
|
|
|
|
|
"local" => "l",
|
|
|
|
|
|
"return" => "r",
|
|
|
|
|
|
// ... 初期20語
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
pub struct BasicTranscoder;
|
|
|
|
|
|
|
|
|
|
|
|
impl BasicTranscoder {
|
|
|
|
|
|
pub fn encode(&self, input: &str) -> String {
|
|
|
|
|
|
let mut result = String::with_capacity(input.len());
|
|
|
|
|
|
let tokens = tokenize_simple(input);
|
|
|
|
|
|
|
|
|
|
|
|
for token in tokens {
|
|
|
|
|
|
match NYASH_TO_ANCP.get(token.text) {
|
|
|
|
|
|
Some(ancp) => result.push_str(ancp),
|
|
|
|
|
|
None => result.push_str(token.text),
|
|
|
|
|
|
}
|
|
|
|
|
|
result.push_str(&token.trailing_space);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Day 5-7: 基本往復テスト
|
|
|
|
|
|
```rust
|
|
|
|
|
|
// tests/ancp_roundtrip.rs
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basic_roundtrip() {
|
|
|
|
|
|
let cases = vec![
|
|
|
|
|
|
"box Test { }",
|
|
|
|
|
|
"new StringBox()",
|
|
|
|
|
|
"me.field = 42",
|
|
|
|
|
|
"local x = 10",
|
|
|
|
|
|
"return result",
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
let transcoder = BasicTranscoder::new();
|
|
|
|
|
|
|
|
|
|
|
|
for case in cases {
|
|
|
|
|
|
let encoded = transcoder.encode(case);
|
|
|
|
|
|
let decoded = transcoder.decode(&encoded);
|
|
|
|
|
|
assert_eq!(case, decoded, "Failed for: {}", case);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 📅 Week 2: スマート変換
|
|
|
|
|
|
|
|
|
|
|
|
### Day 8-9: コンテキスト認識パーサー
|
|
|
|
|
|
```rust
|
|
|
|
|
|
// src/ancp/context_parser.rs
|
|
|
|
|
|
pub struct ContextAwareTranscoder {
|
|
|
|
|
|
basic: BasicTranscoder,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl ContextAwareTranscoder {
|
|
|
|
|
|
pub fn encode(&self, input: &str) -> String {
|
|
|
|
|
|
let mut result = String::new();
|
|
|
|
|
|
let mut in_string = false;
|
|
|
|
|
|
let mut in_comment = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 文字列・コメント内は変換しない
|
|
|
|
|
|
for (i, ch) in input.chars().enumerate() {
|
|
|
|
|
|
match ch {
|
|
|
|
|
|
'"' if !in_comment => in_string = !in_string,
|
|
|
|
|
|
'/' if !in_string && peek_next(input, i) == Some('/') => {
|
|
|
|
|
|
in_comment = true;
|
|
|
|
|
|
},
|
|
|
|
|
|
'\n' => in_comment = false,
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// コンテキストに応じて処理
|
|
|
|
|
|
if !in_string && !in_comment {
|
|
|
|
|
|
// トークン変換
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// そのまま出力
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Day 10-11: Lexer統合
|
|
|
|
|
|
```rust
|
|
|
|
|
|
// src/parser/lexer.rs に追加
|
|
|
|
|
|
impl Lexer {
|
|
|
|
|
|
pub fn with_ancp_support(input: &str) -> Self {
|
|
|
|
|
|
if input.starts_with(";ancp:") {
|
|
|
|
|
|
// ANCPモードで初期化
|
|
|
|
|
|
Self::new_ancp_mode(input)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Self::new(input)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn new_ancp_mode(input: &str) -> Self {
|
|
|
|
|
|
// ANCP → Nyash変換してからレキシング
|
|
|
|
|
|
let transcoder = get_transcoder();
|
|
|
|
|
|
let nyash_code = transcoder.decode(input).unwrap();
|
|
|
|
|
|
Self::new(&nyash_code)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Day 12-14: エラー位置マッピング
|
|
|
|
|
|
```rust
|
|
|
|
|
|
// src/ancp/source_map.rs
|
|
|
|
|
|
pub struct SourceMap {
|
|
|
|
|
|
mappings: Vec<Mapping>,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl SourceMap {
|
|
|
|
|
|
pub fn translate_position(&self, ancp_pos: Position) -> Position {
|
|
|
|
|
|
// ANCP位置 → Nyash位置への変換
|
|
|
|
|
|
self.mappings
|
|
|
|
|
|
.binary_search_by_key(&ancp_pos, |m| m.ancp_pos)
|
2025-11-06 15:41:52 +09:00
|
|
|
|
.map(|i| self.mappings[i].hako_pos)
|
2025-09-03 13:58:52 +09:00
|
|
|
|
.unwrap_or(ancp_pos)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 📅 Week 3: ツール実装
|
|
|
|
|
|
|
|
|
|
|
|
### Day 15-16: CLIツール
|
|
|
|
|
|
```rust
|
|
|
|
|
|
// src/bin/nyash2ancp.rs
|
|
|
|
|
|
use clap::Parser;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Parser)]
|
|
|
|
|
|
struct Args {
|
|
|
|
|
|
#[clap(short, long)]
|
|
|
|
|
|
input: PathBuf,
|
|
|
|
|
|
|
|
|
|
|
|
#[clap(short, long)]
|
|
|
|
|
|
output: Option<PathBuf>,
|
|
|
|
|
|
|
|
|
|
|
|
#[clap(long)]
|
|
|
|
|
|
measure_tokens: bool,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
|
|
let args = Args::parse();
|
|
|
|
|
|
let content = fs::read_to_string(&args.input)?;
|
|
|
|
|
|
|
|
|
|
|
|
let transcoder = AncpTranscoder::new();
|
|
|
|
|
|
let encoded = transcoder.encode(&content)?;
|
|
|
|
|
|
|
|
|
|
|
|
if args.measure_tokens {
|
|
|
|
|
|
let reduction = measure_token_reduction(&content, &encoded);
|
|
|
|
|
|
eprintln!("Token reduction: {:.1}%", reduction * 100.0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
match args.output {
|
|
|
|
|
|
Some(path) => fs::write(path, encoded)?,
|
|
|
|
|
|
None => print!("{}", encoded),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Day 17-18: スモークテスト統合
|
|
|
|
|
|
```bash
|
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
# tools/test_ancp_roundtrip.sh
|
|
|
|
|
|
|
|
|
|
|
|
test_file=$1
|
|
|
|
|
|
expected_pattern=$2
|
|
|
|
|
|
|
|
|
|
|
|
# 1. 通常実行
|
|
|
|
|
|
normal_output=$(./target/release/nyash "$test_file" 2>&1)
|
|
|
|
|
|
|
|
|
|
|
|
# 2. ANCP変換
|
2025-11-06 15:41:52 +09:00
|
|
|
|
ancp_file="${test_file%.hako}.ancp"
|
2025-09-03 13:58:52 +09:00
|
|
|
|
./target/release/nyash2ancp -i "$test_file" -o "$ancp_file"
|
|
|
|
|
|
|
|
|
|
|
|
# 3. ANCP実行
|
|
|
|
|
|
ancp_output=$(./target/release/nyash "$ancp_file" 2>&1)
|
|
|
|
|
|
|
|
|
|
|
|
# 4. 出力比較
|
|
|
|
|
|
if [ "$normal_output" != "$ancp_output" ]; then
|
|
|
|
|
|
echo "ERROR: Output mismatch for $test_file"
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# 5. パターン検証(既存のスモークテスト方式)
|
|
|
|
|
|
echo "$ancp_output" | grep -q "$expected_pattern"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Day 19-21: VSCode拡張(基礎)
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// vscode-extension/src/extension.ts
|
|
|
|
|
|
import * as vscode from 'vscode';
|
|
|
|
|
|
|
|
|
|
|
|
export function activate(context: vscode.ExtensionContext) {
|
|
|
|
|
|
// ホバープロバイダー
|
|
|
|
|
|
const hoverProvider = vscode.languages.registerHoverProvider(
|
|
|
|
|
|
'ancp',
|
|
|
|
|
|
{
|
|
|
|
|
|
provideHover(document, position) {
|
|
|
|
|
|
const word = document.getText(
|
|
|
|
|
|
document.getWordRangeAtPosition(position)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const original = ancpToNyash(word);
|
|
|
|
|
|
if (original !== word) {
|
|
|
|
|
|
return new vscode.Hover(
|
|
|
|
|
|
`ANCP: \`${word}\` → Nyash: \`${original}\``
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
context.subscriptions.push(hoverProvider);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 📅 Week 4: 最適化と統合
|
|
|
|
|
|
|
|
|
|
|
|
### Day 22-23: tiktoken実測と最適化
|
|
|
|
|
|
```python
|
|
|
|
|
|
# tools/measure_ancp_efficiency.py
|
|
|
|
|
|
import tiktoken
|
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
|
|
|
|
enc = tiktoken.get_encoding("cl100k_base")
|
|
|
|
|
|
|
|
|
|
|
|
def measure_file(nyash_path, ancp_path):
|
|
|
|
|
|
with open(nyash_path) as f:
|
|
|
|
|
|
nyash_code = f.read()
|
|
|
|
|
|
with open(ancp_path) as f:
|
|
|
|
|
|
ancp_code = f.read()
|
|
|
|
|
|
|
|
|
|
|
|
nyash_tokens = len(enc.encode(nyash_code))
|
|
|
|
|
|
ancp_tokens = len(enc.encode(ancp_code))
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
"file": nyash_path,
|
|
|
|
|
|
"nyash_tokens": nyash_tokens,
|
|
|
|
|
|
"ancp_tokens": ancp_tokens,
|
|
|
|
|
|
"reduction": 1 - (ancp_tokens / nyash_tokens),
|
|
|
|
|
|
"nyash_chars": len(nyash_code),
|
|
|
|
|
|
"ancp_chars": len(ancp_code),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# 全サンプルファイルで測定
|
|
|
|
|
|
results = []
|
2025-11-06 15:41:52 +09:00
|
|
|
|
for nyash_file in glob.glob("examples/*.hako"):
|
|
|
|
|
|
ancp_file = nyash_file.replace(".hako", ".ancp")
|
2025-09-03 13:58:52 +09:00
|
|
|
|
results.append(measure_file(nyash_file, ancp_file))
|
|
|
|
|
|
|
|
|
|
|
|
# 統計出力
|
|
|
|
|
|
avg_reduction = sum(r["reduction"] for r in results) / len(results)
|
|
|
|
|
|
print(f"Average token reduction: {avg_reduction:.1%}")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Day 24-25: CI/CD統合
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
# .github/workflows/ancp.yml
|
|
|
|
|
|
name: ANCP Tests
|
|
|
|
|
|
|
|
|
|
|
|
on: [push, pull_request]
|
|
|
|
|
|
|
|
|
|
|
|
jobs:
|
|
|
|
|
|
ancp-roundtrip:
|
|
|
|
|
|
runs-on: ubuntu-latest
|
|
|
|
|
|
steps:
|
|
|
|
|
|
- uses: actions/checkout@v4
|
|
|
|
|
|
|
|
|
|
|
|
- name: Build with ANCP
|
|
|
|
|
|
run: cargo build --release --features ancp
|
|
|
|
|
|
|
|
|
|
|
|
- name: Run roundtrip tests
|
|
|
|
|
|
run: |
|
2025-11-06 15:41:52 +09:00
|
|
|
|
for f in examples/*.hako; do
|
2025-09-03 13:58:52 +09:00
|
|
|
|
echo "Testing: $f"
|
|
|
|
|
|
./tools/test_ancp_roundtrip.sh "$f"
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
- name: Measure efficiency
|
|
|
|
|
|
run: |
|
|
|
|
|
|
python3 tools/measure_ancp_efficiency.py > ancp_report.json
|
|
|
|
|
|
|
|
|
|
|
|
- name: Upload report
|
|
|
|
|
|
uses: actions/upload-artifact@v3
|
|
|
|
|
|
with:
|
|
|
|
|
|
name: ancp-efficiency-report
|
|
|
|
|
|
path: ancp_report.json
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Day 26-28: ドキュメント・最終調整
|
|
|
|
|
|
- ユーザーガイド作成
|
|
|
|
|
|
- API ドキュメント生成
|
|
|
|
|
|
- パフォーマンスチューニング
|
|
|
|
|
|
- 最終テスト
|
|
|
|
|
|
|
|
|
|
|
|
## 🎯 成功指標
|
|
|
|
|
|
|
|
|
|
|
|
### Week 1終了時
|
|
|
|
|
|
- [ ] 基本20語で往復変換成功
|
|
|
|
|
|
- [ ] 単純なNyashプログラムが動作
|
|
|
|
|
|
|
|
|
|
|
|
### Week 2終了時
|
|
|
|
|
|
- [ ] コンテキスト認識変換
|
|
|
|
|
|
- [ ] Lexer統合完了
|
|
|
|
|
|
- [ ] エラー位置の正確なマッピング
|
|
|
|
|
|
|
|
|
|
|
|
### Week 3終了時
|
|
|
|
|
|
- [ ] CLI ツール完成
|
|
|
|
|
|
- [ ] スモークテスト統合
|
|
|
|
|
|
- [ ] VSCode基本機能
|
|
|
|
|
|
|
|
|
|
|
|
### Week 4終了時
|
|
|
|
|
|
- [ ] トークン削減率50%以上達成
|
|
|
|
|
|
- [ ] 全サンプルで往復テスト成功
|
|
|
|
|
|
- [ ] CI/CD完全統合
|
|
|
|
|
|
- [ ] ドキュメント完成
|
|
|
|
|
|
|
|
|
|
|
|
## 🚧 リスクと対策
|
|
|
|
|
|
|
|
|
|
|
|
### 技術的リスク
|
|
|
|
|
|
1. **パフォーマンス劣化**
|
|
|
|
|
|
- 対策: 段階的実装で早期発見
|
|
|
|
|
|
- 対策: プロファイリング継続実施
|
|
|
|
|
|
|
|
|
|
|
|
2. **互換性問題**
|
|
|
|
|
|
- 対策: 既存テストスイートで検証
|
|
|
|
|
|
- 対策: feature flagで段階的有効化
|
|
|
|
|
|
|
|
|
|
|
|
### 運用リスク
|
|
|
|
|
|
1. **採用障壁**
|
|
|
|
|
|
- 対策: 分かりやすいドキュメント
|
|
|
|
|
|
- 対策: 移行ツール提供
|
|
|
|
|
|
|
|
|
|
|
|
2. **メンテナンス負荷**
|
|
|
|
|
|
- 対策: 自動テスト充実
|
|
|
|
|
|
- 対策: CI/CDで品質保証
|
|
|
|
|
|
|
|
|
|
|
|
## 📝 チェックリスト
|
|
|
|
|
|
|
|
|
|
|
|
### 実装前
|
|
|
|
|
|
- [ ] tiktoken実測による記号選定完了
|
|
|
|
|
|
- [ ] 関係者への影響確認
|
|
|
|
|
|
- [ ] feature flag設計確認
|
|
|
|
|
|
|
|
|
|
|
|
### 実装中
|
|
|
|
|
|
- [ ] 日次で往復テスト実施
|
|
|
|
|
|
- [ ] パフォーマンス計測継続
|
|
|
|
|
|
- [ ] ドキュメント同時更新
|
|
|
|
|
|
|
|
|
|
|
|
### 実装後
|
|
|
|
|
|
- [ ] 全スモークテスト合格
|
|
|
|
|
|
- [ ] トークン削減率目標達成
|
|
|
|
|
|
- [ ] ユーザーガイド完成
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
この計画に従って実装を進めることで、4週間でANCPを完成させ、AIとの協働開発を革命的に改善します!
|