実験ノート:COBOLのモダナイゼーション with Opus4.6(その2):作ってみた
【注意】著者はCOBOLの言語仕様をしらない、これはあくまでClaude Codeの能力を測るためである。
前の記事では、COBOLのモダナイゼーションについて、少し思いつきのアイデアをClaudeに投げてなんか仕様を得た。
これって、実装できるのか?ということで続き。
この仕組みのPOCをつくってほしい、仮想的なCOBOLサブセットでよいので、型言語での処理系を作って、このアイデアを証明したい。
なんか調子よくスタート。サブセットインタプリタを作ってくれる模様。実装はTypeScriptのstrict modelを使う。

出来たらしい。暗黙的な型をちゃんと捕まえるというのと、「例による仕様」という謎の仕組み、そして最後にコード生成。

COBOL Instrumented Interpreter
POCの大きな構造(README.mdより)

実装した3つの構成要素、最初は暗黙型を明示化。

そして、実行トレースが「例による仕様」になる。何言ってるのか不明。


サポートするCOBOLの仕様はこちら、

「サンプルCOBOLプログラム(利息計算)」
main.tsに「サンプルCOBOLプログラム(利息計算)」がある模様
// ============================================================
// サンプルCOBOLプログラム: ローン利息計算
// ============================================================
//
// 以下のCOBOLを模倣:
//
// IDENTIFICATION DIVISION.
// PROGRAM-ID. LOAN-CALC.
// DATA DIVISION.
// WORKING-STORAGE SECTION.
// 01 WS-PRINCIPAL PIC 9(7)V99 VALUE 100000.00.
// 01 WS-ANNUAL-RATE PIC 9(2)V9(4) VALUE 3.5000.
// 01 WS-MONTHLY-RATE PIC 9(2)V9(6).
// 01 WS-MONTHS PIC 9(3) VALUE 360.
// 01 WS-PAYMENT PIC 9(7)V99.
// 01 WS-TOTAL-INTEREST PIC 9(9)V99 VALUE 0.
// 01 WS-BALANCE PIC 9(9)V99.
// 01 WS-INTEREST-AMT PIC 9(7)V99.
// 01 WS-PRINCIPAL-AMT PIC 9(7)V99.
// 01 WS-MONTH-CTR PIC 9(3).
// 01 WS-STATUS PIC X(10) VALUE "ACTIVE".
// 01 WS-TEMP PIC 9(9)V9(4).
// PROCEDURE DIVISION.
// MAIN-LOGIC.
// PERFORM CALC-MONTHLY-RATE
// PERFORM CALC-PAYMENT
// MOVE WS-PRINCIPAL TO WS-BALANCE
// PERFORM CALC-AMORTIZATION
// VARYING WS-MONTH-CTR FROM 1 BY 1
// UNTIL WS-MONTH-CTR > 12
// DISPLAY "Total Interest (12 months): " WS-TOTAL-INTEREST
// IF WS-TOTAL-INTEREST > 3000
// MOVE "HIGH-INT" TO WS-STATUS
// ELSE
// MOVE "LOW-INT" TO WS-STATUS
// END-IF
// DISPLAY "Status: " WS-STATUS
// STOP RUN.
// CALC-MONTHLY-RATE.
// COMPUTE WS-MONTHLY-RATE ROUNDED =
// WS-ANNUAL-RATE / 100 / 12.
// CALC-PAYMENT.
// COMPUTE WS-TEMP = WS-MONTHLY-RATE * WS-PRINCIPAL
// COMPUTE WS-PAYMENT ROUNDED = WS-TEMP.
// CALC-AMORTIZATION.
// COMPUTE WS-INTEREST-AMT ROUNDED =
// WS-BALANCE * WS-MONTHLY-RATE
// COMPUTE WS-PRINCIPAL-AMT =
// WS-PAYMENT - WS-INTEREST-AMT
// SUBTRACT WS-PRINCIPAL-AMT FROM WS-BALANCE
// ADD WS-INTEREST-AMT TO WS-TOTAL-INTEREST
// DISPLAY "Month " WS-MONTH-CTR
// ": Interest=" WS-INTEREST-AMT
// " Principal=" WS-PRINCIPAL-AMT
// " Balance=" WS-BALANCE.
//本体は、ASTっぽく展開される、パラグラフが展開されて

そして、Main statementはこのようになる

IF-THENはこのように展開され、

最後に、DISPLAY

実行フェーズ1:Interpreterを起動
最初は、interpreterを起動し、

━━━ Phase 1: Execute COBOL in Type-Safe Interpreter ━━━
--- Program Output (DISPLAY) ---
Month 1: Interest=291.7 Principal=0 Balance=100000
Month 2: Interest=291.7 Principal=0 Balance=100000
Month 3: Interest=291.7 Principal=0 Balance=100000
Month 4: Interest=291.7 Principal=0 Balance=100000
Month 5: Interest=291.7 Principal=0 Balance=100000
Month 6: Interest=291.7 Principal=0 Balance=100000
Month 7: Interest=291.7 Principal=0 Balance=100000
Month 8: Interest=291.7 Principal=0 Balance=100000
Month 9: Interest=291.7 Principal=0 Balance=100000
Month 10: Interest=291.7 Principal=0 Balance=100000
Month 11: Interest=291.7 Principal=0 Balance=100000
Month 12: Interest=291.7 Principal=0 Balance=100000
Total Interest (12 months): 3500.37
Status: HIGH-INT
--- Final Variable States ---
WS-PRINCIPAL = 100000.00 [fixed-decimal]
WS-ANNUAL-RATE = 3.5000 [fixed-decimal]
WS-MONTHLY-RATE = 0.002917 [fixed-decimal]
WS-MONTHS = 360 [fixed-decimal]
WS-PAYMENT = 291.70 [fixed-decimal]
WS-TOTAL-INTEREST = 3500.37 [fixed-decimal]
WS-BALANCE = 100000.00 [fixed-decimal]
WS-INTEREST-AMT = 291.70 [fixed-decimal]
WS-PRINCIPAL-AMT = 0.00 [fixed-decimal]
WS-MONTH-CTR = 13 [fixed-decimal]
WS-STATUS = HIGH-INT [alphanumeric]実行フェーズ2:実行トレースの分析
トレースの分析

分岐カバレッジや型変換の検出をする

そして、最後にパラグラフを呼び出す。

━━━ Phase 2: Analyze Execution Traces ━━━
Total trace events: 186
Total statements: 72
Variables tracked: 12
Assignments recorded: 66
Arithmetic operations: 51
Branch decisions: 1
PERFORM calls: 14
Loop iterations: 12
Rounding operations: 4
Branch Coverage:
"WS-TOTAL-INTEREST > 3000"
THEN: 1/1 ELSE: 0/1
⚠️ Only one branch exercised - need more test data!
Type Conversions Detected:
WS-MONTHLY-RATE: numeric → fixed-decimal (rounded)
WS-PAYMENT: numeric → fixed-decimal (rounded)
WS-BALANCE: numeric → fixed-decimal (numeric-move)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-INTEREST-AMT: numeric → fixed-decimal (rounded)
WS-TOTAL-INTEREST: numeric → fixed-decimal (truncation)
WS-TOTAL-INTEREST: numeric → fixed-decimal (truncation)
WS-TOTAL-INTEREST: numeric → fixed-decimal (truncation)
WS-STATUS: alphanumeric → alphanumeric (alphanumeric-move)
Paragraph Call Counts:
CALC-MONTHLY-RATE: 1 calls
CALC-PAYMENT: 1 calls
CALC-AMORTIZATION: 12 calls実行フェーズ3:トレースからTypeScript生成
トレースから、TypeScriptを生成

━━━ Phase 3: Generate TypeScript from Traces ━━━
--- Generation Insights ---
⚠️ Variable "WS-MONTHLY-RATE" requires rounding (6 decimal places). Use BigDecimal or fixed-point library in production.
⚠️ Variable "WS-TOTAL-INTEREST" requires rounding (2 decimal places). Use BigDecimal or fixed-point library in production.
⚠️ Variable "WS-TOTAL-INTEREST" requires rounding (2 decimal places). Use BigDecimal or fixed-point library in production.
⚠️ Variable "WS-TOTAL-INTEREST" requires rounding (2 decimal places). Use BigDecimal or fixed-point library in production.
🔢 Program uses fixed-point arithmetic with rounding. BigDecimal recommended for production.
📊 Paragraph "CALC-MONTHLY-RATE" was called 1 time(s) during trace execution.
📊 Paragraph "CALC-PAYMENT" was called 1 time(s) during trace execution.
📊 Paragraph "CALC-AMORTIZATION" was called 12 time(s) during trace execution.
⚠️ Branch "WS-TOTAL-INTEREST > 3000" only exercised THEN path. Need additional test data to cover the other branch.
🔄 Implicit type conversion detected for "WS-MONTHLY-RATE": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-PAYMENT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-BALANCE": numeric → fixed-decimal (numeric-move)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-INTEREST-AMT": numeric → fixed-decimal (rounded)
🔄 Implicit type conversion detected for "WS-TOTAL-INTEREST": numeric → fixed-decimal (truncation)
🔄 Implicit type conversion detected for "WS-TOTAL-INTEREST": numeric → fixed-decimal (truncation)
🔄 Implicit type conversion detected for "WS-TOTAL-INTEREST": numeric → fixed-decimal (truncation)
🔄 Implicit type conversion detected for "WS-STATUS": alphanumeric → alphanumeric (alphanumeric-move)
そして生成されたTypeScriptは、
--- Generated TypeScript Code ---
/**
* Auto-generated from COBOL program: LOAN-CALC
* Generated via Instrumented Interpreter trace analysis
* Trace statistics: 72 statements, 186 events
*/
// ============================================================
// Type Definitions (inferred from execution traces)
// ============================================================
// ⚠️ PRECISION WARNING: This program uses fixed-point arithmetic.
// For production use, replace `number` with a BigDecimal library.
import { BigDecimal } from "bigdecimal"; // recommended for production
interface ProgramState {
wsPrincipal: number;
wsAnnualRate: number;
wsMonthlyRate: number; // used in arithmetic operations // PRECISION: 6 decimal places, ROUNDED
wsMonths: number;
wsPayment: number; // used in arithmetic operations
wsTotalInterest: number; // used in arithmetic operations // PRECISION: 2 decimal places, TRUNCATED
wsBalance: number; // used in arithmetic operations
wsInterestAmt: number; // used in arithmetic operations
wsPrincipalAmt: number; // used in arithmetic operations
wsMonthCtr: number;
wsStatus: string;
wsTemp: number; // used in arithmetic operations
}
function createInitialState(): ProgramState {
return {
wsPrincipal: 100000.00,
wsAnnualRate: 3.5000,
wsMonthlyRate: 0.000000,
wsMonths: 360,
wsPayment: 0.00,
wsTotalInterest: 0.00,
wsBalance: 0.00,
wsInterestAmt: 0.00,
wsPrincipalAmt: 0.00,
wsMonthCtr: 0,
wsStatus: "ACTIVE ",
wsTemp: 0.0000,
};
}
// ============================================================
// Functions (from COBOL paragraphs)
// ============================================================
/** Paragraph: CALC-MONTHLY-RATE (called 1x in trace) */
function calcMonthlyRate(state: ProgramState): void {
state.wsMonthlyRate = ((state.wsAnnualRate / 100) / 12);
}
/** Paragraph: CALC-PAYMENT (called 1x in trace) */
function calcPayment(state: ProgramState): void {
state.wsTemp = (state.wsMonthlyRate * state.wsPrincipal);
state.wsPayment = state.wsTemp;
}
/** Paragraph: CALC-AMORTIZATION (called 12x in trace) */
function calcAmortization(state: ProgramState): void {
state.wsInterestAmt = (state.wsBalance * state.wsMonthlyRate);
state.wsPrincipalAmt = (state.wsPayment - state.wsInterestAmt);
state.wsBalance -= state.wsPrincipalAmt;
state.wsTotalInterest += state.wsInterestAmt;
console.log("Month ", state.wsMonthCtr, ": Interest=", state.wsInterestAmt, " Principal=", state.wsPrincipalAmt, " Balance=", state.wsBalance);
}
// ============================================================
// Main execution
// ============================================================
function main(): ProgramState {
const state = createInitialState();
calcMonthlyRate(state);
calcPayment(state);
state.wsBalance = state.wsPrincipal;
for (state.wsMonthCtr = 1; !(state.wsMonthCtr > 12); state.wsMonthCtr += 1) {
calcAmortization(state);
}
console.log("Total Interest (12 months): ", state.wsTotalInterest);
if (state.wsTotalInterest > 3000) {
state.wsStatus = "HIGH-INT";
} else {
state.wsStatus = "LOW-INT";
}
console.log("Status: ", state.wsStatus);
return state;
return state;
}実行フェーズ4:トレースのサンプル出力
そして最後にサンプル出力。

Runの結果は、
// Run
const result = main();
console.log("Final state:", result);
━━━ Phase 4: Sample Trace Events (first 15) ━━━
[0] program-start @ 0ms
[1] var-init @ 1ms
WS-PRINCIPAL = 100000.00 (fixed-decimal)
[2] var-init @ 2ms
WS-ANNUAL-RATE = 3.5000 (fixed-decimal)
[3] var-init @ 2ms
WS-MONTHLY-RATE = 0.000000 (fixed-decimal)
[4] var-init @ 2ms
WS-MONTHS = 360 (fixed-decimal)
[5] var-init @ 2ms
WS-PAYMENT = 0.00 (fixed-decimal)
[6] var-init @ 2ms
WS-TOTAL-INTEREST = 0.00 (fixed-decimal)
[7] var-init @ 2ms
WS-BALANCE = 0.00 (fixed-decimal)
[8] var-init @ 2ms
WS-INTEREST-AMT = 0.00 (fixed-decimal)
[9] var-init @ 2ms
WS-PRINCIPAL-AMT = 0.00 (fixed-decimal)
[10] var-init @ 2ms
WS-MONTH-CTR = 0 (fixed-decimal)
[11] var-init @ 3ms
WS-STATUS = ACTIVE (alphanumeric)
[12] var-init @ 3ms
WS-TEMP = 0.0000 (fixed-decimal)
[13] perform-call @ 4ms
→ CALC-MONTHLY-RATE (depth: 1)
[14] var-assign @ 5ms
WS-MONTHLY-RATE: 0.000000 → 0.002917
conversion: rounded
... (171 more events)感想:アイデアは実現できたようだけど、、
動いているのか、どうなのか?COBLわからん。
【注意】著者はCOBOLの言語仕様をしらない、これはあくまでClaude Codeの能力を測るためである。
成果物をここに供養する
