見出し画像

実験ノート: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の能力を測るためである。

成果物をここに供養する

いいなと思ったら応援しよう!