feat(m02-cf): 단가산출 다이어그램 Model · Eval 타입 · 함수 머리 올림

CfLogic · CfBox · CfInput · CfVariable · CfFunc · CfOutput · CfPort · CfLink · CfElementRef · CfUserElement · CfPill · CfKind · CfGroup · CfCost · CfValue · CfValues · CfEvalResult
newLogic · addBox · removeBox · moveBox · setColor · setUnit · portsOut · portsIn · link · unlink · evalLogic · checkLogic (속은 준비 중)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VBToTCGk5dqDKeXCEP3vhk
This commit is contained in:
2026-10-01 15:59:53 +09:00
co-authored by Claude Opus 5.5
parent 84c2b968ec
commit 2e66b0a6ce
2 changed files with 208 additions and 0 deletions
@@ -0,0 +1,44 @@
/* =============================================================================
* M02_CF_Eval.ts
* 단가산출 다이어그램 계산 · 검사 — 함수 상자는 차례대로 시트를 우리 스프레드시트 엔진으로 셈
* (받는 칸에 앞 값을 넣음) · 출력 상자를 비용 종류별로 모아 원가 · DOM 없음
* ========================================================================== */
import type { CfLogic } from "./M02_CF_Model";
/** 알약 값 — 수 · 글 · 참거짓 · 빈 값 · 표(머리 줄 포함 칸 덩어리) */
export type CfValue = number | string | boolean | null | (number | string | null)[][];
/** 입력 값 묶음 — 상자 id → 알약 이름 → 값(입력 상자 요소 값 · 표 · 변수 값 · 함수 상자 변수 값) */
export type CfValues = Record<string, Record<string, CfValue>>;
export interface CfEvalResult {
/** 상자 id → 내보내는 알약 이름 → 값 */
ports: Record<string, Record<string, CfValue>>;
/** 출력 상자 id → 값 */
outputs: Record<string, number | string | boolean | null>;
cost: {
노무비: number;
재료비: number;
경비: number;
/** 비용 아닌 출력(값) — 출력 상자 이름 → 값 */
값들: Record<string, number | string | boolean | null>;
계: number;
};
/** 쉬운 말 */
errors: string[];
}
const notReady = (): never => {
throw new Error("준비 중");
};
/** 화면 계산 */
export function evalLogic(_lg: CfLogic, _values: CfValues = {}): CfEvalResult {
return notReady();
}
/** 검사 — 빈 받는 자리 · 출력 하나 아님 · 순환 · 오류(쉬운 말 목록) */
export function checkLogic(_lg: CfLogic, _values: CfValues = {}): string[] {
return notReady();
}
+164
View File
@@ -0,0 +1,164 @@
/* =============================================================================
* M02_CF_Model.ts
* 단가산출 다이어그램 Model — 입력 · 함수 · 출력 상자를 선으로 이음 · 함수 상자 = 시트 하나(출력 하나)
* DOM 없음 · 모양은 `docs/raw/M01_쉬운단가/원가다이어그램_정함.md` 「상자 · 선 약속」 그대로
* ========================================================================== */
import type { Sheet } from "../../A00_Common/spreadsheet/spreadsheet_types";
export type CfKind = "input" | "function" | "output";
export type CfGroup = "인력" | "재료" | "유가전력" | "기계" | "소요량" | "계수" | "환율" | "요율";
export type CfCost = "노무비" | "재료비" | "경비" | "값";
/** 입력 상자가 고른 요소 — ref = 마스터 키 또는 사용자 요소 id */
export interface CfElementRef {
ref: string;
name: string;
spec: string;
unit: string;
}
/** 변수(입력 정의) — 그룹 밖 정보(면적 · 길이 …) · 함수 상자의 받는 값 */
export interface CfVariable {
name: string;
type: "number" | "choice" | "text";
unit: string;
choices?: (string | number)[];
range?: [number, number];
example: number | string | null;
/** 함수 상자 안 변수의 칸(「B2」) — 입력 상자 변수는 없음 */
cell?: string;
}
export interface CfInput {
mode: "group" | "variable";
group?: CfGroup;
elements: CfElementRef[];
/** 소요량 · 계수 = 표(row 가 있으면 그 한 줄) */
table?: { key: string; row?: number };
variables: CfVariable[];
}
export interface CfFunc {
/** 우리 스프레드시트 시트 하나 */
sheet: Sheet;
/** 받는 알약 → 칸 */
inputs: { port: string; cell: string }[];
variables: CfVariable[];
/** 내보내는 알약 하나 */
output: { cell: string; name: string; unit: string };
}
export interface CfOutput {
cost: CfCost;
unit: string;
}
export interface CfBox {
id: string;
kind: CfKind;
name: string;
color: string;
/** 오른쪽 위 단위 — 기본 「-」 */
unit: string;
x: number;
y: number;
input?: CfInput;
func?: CfFunc;
output?: CfOutput;
}
/** 알약 한쪽 끝 */
export interface CfPort {
box: string;
name: string;
}
export interface CfLink {
id: string;
from: CfPort;
to: CfPort;
}
/** 사용자 요소 — 그룹에 없는 새 요소(마스터에 안 씀 · `_data` 에 저장) */
export interface CfUserElement {
id: string;
group: CfGroup;
name: string;
spec: string;
unit: string;
value: number | null;
}
export interface CfLogic {
key: string | null;
name: string;
resultUnit: string;
boxes: CfBox[];
links: CfLink[];
userElements: CfUserElement[];
}
/** 알약 하나의 모습 */
export interface CfPill {
name: string;
unit: string;
}
const notReady = (): never => {
throw new Error("준비 중");
};
export function newLogic(_name?: string, _resultUnit?: string): CfLogic {
return notReady();
}
/** 상자 더하기 — 종류별 기본 색 · 단위 「-」 · 함수는 빈 시트 하나 · 출력은 노무비 */
export function addBox(
_lg: CfLogic,
_kind: CfKind,
_x: number,
_y: number,
): { lg: CfLogic; box: CfBox } {
return notReady();
}
/** 상자 지우기 — 이어진 선도 지움 */
export function removeBox(_lg: CfLogic, _id: string): CfLogic {
return notReady();
}
export function moveBox(_lg: CfLogic, _id: string, _x: number, _y: number): CfLogic {
return notReady();
}
export function setColor(_lg: CfLogic, _id: string, _color: string): CfLogic {
return notReady();
}
export function setUnit(_lg: CfLogic, _id: string, _unit: string): CfLogic {
return notReady();
}
/** 내보내는 알약 — 입력 = 요소 · 표 · 변수마다 · 함수 = 출력 하나 · 출력 = 없음 */
export function portsOut(_box: CfBox): CfPill[] {
return notReady();
}
/** 받는 알약 — 함수 = 입력 알약 · 출력 = 하나 · 입력 = 없음 */
export function portsIn(_box: CfBox): CfPill[] {
return notReady();
}
/** 선 잇기 — 규칙에 어긋나면 막고 까닭(쉬운 말) */
export function link(
_lg: CfLogic,
_from: CfPort,
_to: CfPort,
): { lg: CfLogic; why: string | null } {
return notReady();
}
export function unlink(_lg: CfLogic, _id: string): CfLogic {
return notReady();
}