Files
hakorune/tools/hako_check/cli.hako
nyash-codex 86489ffe43 Phase 21.3 WIP: Hako Source Checker improvements
## 🎯 Checker/Analyzer拡張

###  実装追加
- テストフレームワーク追加(tools/hako_check/tests/)
- ルール改善(HC003グローバルassign、HC040静的箱トップレベルassign)
- テストランナー(run_tests.sh)

### 🔧 Rust側修正
- AST utilities拡張(src/ast/utils.rs)
- MIR lowerers新設(src/mir/lowerers/)
- Parser制御フロー改善(src/parser/statements/control_flow.rs)
- Tokenizer識別子処理改善(src/tokenizer/lex_ident.rs)

### 📁 主要変更
- tools/hako_check/cli.hako - CLI改善
- tools/hako_check/hako_source_checker.hako - Checker core更新
- tools/hako_check/tests/ - NEW (テストケース追加)
- tools/hako_check/run_tests.sh - NEW (テストランナー)
- src/mir/lowerers/ - NEW (MIR lowering utilities)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 21:04:01 +09:00

101 lines
3.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// tools/hako_check/cli.hako — HakoAnalyzerBox (MVP)
using tools.hako_check.analysis_consumer as HakoAnalysisBuilderBox
using tools.hako_check.rules.rule_include_forbidden as RuleIncludeForbiddenBox
using tools.hako_check.rules.rule_using_quoted as RuleUsingQuotedBox
using tools.hako_check.rules.rule_static_top_assign as RuleStaticTopAssignBox
using tools.hako_check.rules.rule_global_assign as RuleGlobalAssignBox
using tools.hako_check.rules.rule_dead_methods as RuleDeadMethodsBox
using tools.hako_check.rules.rule_jsonfrag_usage as RuleJsonfragUsageBox
static box HakoAnalyzerBox {
run(args) {
if args == null || args.size() < 1 { print("[lint/error] missing paths"); return 2 }
// options: --format {text|dot|json}
local fmt = "text"
local start = 0
if args.size() >= 2 && args.get(0) == "--format" {
fmt = args.get(1)
start = 2
}
if args.size() <= start { print("[lint/error] missing paths"); return 2 }
local fail = 0
local irs = new ArrayBox()
// for i in start..(args.size()-1)
local i = start
while i < args.size() {
local p = args.get(i)
local f = new FileBox(); if f.open(p) == 0 { print("[lint/error] cannot open: " + p); fail = fail + 1; continue }
local text = f.read(); f.close()
// pre-sanitize (ASCII quotes, normalize newlines) — minimal & reversible
text = me._sanitize(text)
// analysis
local ir = HakoAnalysisBuilderBox.build_from_source(text, p)
irs.push(ir)
// rules that work on raw source
local out = new ArrayBox()
RuleIncludeForbiddenBox.apply(text, p, out)
RuleUsingQuotedBox.apply(text, p, out)
RuleStaticTopAssignBox.apply(text, p, out)
RuleGlobalAssignBox.apply(text, p, out)
RuleJsonfragUsageBox.apply(text, p, out)
// rules that need IR (enable dead code detection)
RuleDeadMethodsBox.apply_ir(ir, p, out)
// flush
// for j in 0..(n-1)
local n = out.size(); if n > 0 && fmt == "text" {
local j = 0; while j < n { print(out.get(j)); j = j + 1 }
}
fail = fail + n
i = i + 1
}
// optional DOT/JSON output (MVP: dot only)
if fmt == "dot" { me._render_dot_multi(irs) }
// return number of findings as RC
return fail
}
_sanitize(text) {
if text == null { return text }
// Normalize CRLF -> LF and convert fancy quotes to ASCII
local out = ""
local n = text.length()
local i2 = 0
while i2 < n {
local ch = text.substring(i2, i2+1)
// drop CR
if ch == "\r" { i2 = i2 + 1; continue }
// fancy double quotes → ASCII
if ch == "“" || ch == "”" { out = out.concat("\""); i2 = i2 + 1; continue }
// fancy single quotes → ASCII
if ch == "" || ch == "" { out = out.concat("'"); i2 = i2 + 1; continue }
out = out.concat(ch)
i2 = i2 + 1
}
return out
}
_render_dot_multi(irs) {
// Minimal DOT: emit method nodes; edges omitted in MVP
print("digraph Hako {")
if irs == null { print("}"); return 0 }
local i = 0
while i < irs.size() {
local ir = irs.get(i)
if ir != null {
local ms = ir.get("methods")
if ms != null {
local j = 0
while j < ms.size() {
local name = ms.get(j)
print(" \"" + name + "\";")
j = j + 1
}
}
}
i = i + 1
}
print("}")
return 0
}
}
static box HakoAnalyzerCliMain { method main(args) { return HakoAnalyzerBox.run(args) } }