feat(m02-df): 36-0b 선 조건 여러 줄(그리고 · 또는) · 값 목록
- CfRule gate conds[{from · op · value}] · join 그리고/또는 · 옛 한 줄 꼴(from · op · value)도 읽음 — 옛 칸은 첫 조건과 같게 둠(makeGate)
- gateConds · makeGate · valueChoices(변수 고르기 · 재료 규격 · 표 칸 값 · 수 · 함수 결과는 null)
- 규칙 → 받는 칸 엑셀 식 IF(AND/OR(...),A1,NA()) · 선 이름표 「지반 = 토사 그리고 높이 ≥ 3 일 때만」 · 겹침 · 안 맞음은 조건 알약 묶음끼리
- 조건 값 열쇠 「상자|알약」 먼저(이름 겹침 막음) · M02_DF_Compute.py gate 같은 뜻(서버 = 화면 시험)
- 시험 test_m02_df_model 30개
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VBToTCGk5dqDKeXCEP3vhk
This commit is contained in:
@@ -126,9 +126,27 @@ export type CfGateOp = "=" | "≠" | "≥" | "≤" | ">" | "<";
|
||||
/** 선 규칙(선 하나에 하나 · 없으면 그대로) — 받는 칸의 엑셀 수식으로 바뀌어 엔진 한 벌로 셈
|
||||
* lookup = 보낸 표에서 조건 칸마다 맞출 알약 · 꺼낼 칸(범위 칸은 「이름·아래」 「이름·위」)
|
||||
* gate = 알약 값이 조건을 통과할 때만 보냄 · scale = × ÷ +% -% · round = 끝수 */
|
||||
/** 조건 통과의 조건 한 줄 */
|
||||
export interface CfGateCond {
|
||||
from: CfPort;
|
||||
op: CfGateOp;
|
||||
value: number | string;
|
||||
}
|
||||
|
||||
/** 조건 통과 — conds 여러 줄을 join(그리고 · 또는)으로 · 옛 한 줄 꼴(from · op · value)도 읽음
|
||||
* from · op · value 는 첫 조건과 같게 둠(옛 화면 코드가 그대로 돌게 · makeGate 가 맞춰 줌) — conds 가 있으면 conds 가 이김 */
|
||||
export type CfGateRule = {
|
||||
kind: "gate";
|
||||
conds?: CfGateCond[];
|
||||
join?: "그리고" | "또는";
|
||||
from: CfPort;
|
||||
op: CfGateOp;
|
||||
value: number | string;
|
||||
};
|
||||
|
||||
export type CfRule =
|
||||
| { kind: "lookup"; conds: { col: string; from: CfPort }[]; col: string }
|
||||
| { kind: "gate"; from: CfPort; op: CfGateOp; value: number | string }
|
||||
| CfGateRule
|
||||
| { kind: "scale"; op: "×" | "÷" | "+%" | "-%"; value: number }
|
||||
| ({ kind: "round" } & CfRound);
|
||||
|
||||
@@ -357,10 +375,65 @@ export function portDir(box: CfBox, name: string): "in" | "out" {
|
||||
export function ruleSources(rule: CfRule | undefined): CfPort[] {
|
||||
if (!rule) return [];
|
||||
if (rule.kind === "lookup") return rule.conds.map((c) => c.from);
|
||||
if (rule.kind === "gate") return [rule.from];
|
||||
if (rule.kind === "gate") return gateConds(rule).conds.map((c) => c.from);
|
||||
return [];
|
||||
}
|
||||
|
||||
/** 조건 통과의 조건 줄 · 잇는 말 — 옛 한 줄 꼴은 조건 하나 */
|
||||
export function gateConds(rule: CfGateRule): { conds: CfGateCond[]; join: "그리고" | "또는" } {
|
||||
const conds = rule.conds?.length
|
||||
? rule.conds
|
||||
: [{ from: rule.from, op: rule.op, value: rule.value }];
|
||||
return { conds, join: rule.join ?? "그리고" };
|
||||
}
|
||||
|
||||
/** 조건 통과 만들기 — 옛 칸(from · op · value)은 첫 조건과 같게 */
|
||||
export function makeGate(conds: CfGateCond[], join: "그리고" | "또는" = "그리고"): CfGateRule {
|
||||
const first = conds[0] ?? { from: { box: "", name: "" }, op: "=" as const, value: "" };
|
||||
return {
|
||||
kind: "gate",
|
||||
conds: clone(conds),
|
||||
join,
|
||||
from: { ...first.from },
|
||||
op: first.op,
|
||||
value: first.value,
|
||||
};
|
||||
}
|
||||
|
||||
/** 알약이 가질 수 있는 값 목록 — 조건 값 고르기 · null = 숫자 칸(범위 검사)
|
||||
* 입력 변수 고르기 = choices · 재료 알약 = 그 요소들의 규격 · 표 알약 = opts.col 칸 값들(opts.values 의 표 덩어리)
|
||||
* · 함수 결과 · 다른 로직 출력 · 수 변수 = null */
|
||||
export function valueChoices(
|
||||
lg: CfLogic,
|
||||
port: CfPort,
|
||||
opts: { col?: string; values?: Record<string, Record<string, unknown>> } = {},
|
||||
): (string | number)[] | null {
|
||||
const box = boxOf(lg, port.box);
|
||||
const inp = box?.input;
|
||||
if (!box || box.kind !== "input" || !inp || inp.mode === "logic") return null;
|
||||
const v = inp.variables.find((x) => x.name === port.name);
|
||||
if (v) return v.type === "choice" && v.choices?.length ? [...v.choices] : null;
|
||||
if (port.name === TABLE_PORT && inp.table) {
|
||||
const block = opts.values?.[box.id]?.[TABLE_PORT];
|
||||
if (!opts.col || !Array.isArray(block) || !Array.isArray(block[0])) return null;
|
||||
const head = (block[0] as unknown[]).map(String);
|
||||
const at = head.indexOf(opts.col);
|
||||
if (at < 0) return null;
|
||||
const seen: (string | number)[] = [];
|
||||
for (const row of block.slice(1) as unknown[][]) {
|
||||
const x = row[at];
|
||||
if ((typeof x === "number" || typeof x === "string") && x !== "" && !seen.includes(x))
|
||||
seen.push(x);
|
||||
}
|
||||
return seen.length ? seen : null;
|
||||
}
|
||||
const specs = inp.elements
|
||||
.filter((e) => elementPort(e) === port.name && e.spec.trim())
|
||||
.map((e) => e.spec.trim());
|
||||
const uniq = [...new Set(specs)];
|
||||
return uniq.length ? uniq : null;
|
||||
}
|
||||
|
||||
/** 상자 사이 기댐 — 선 + 선 규칙의 조건 알약 + 재료 조건 찾기의 알약(→ 그 상자) */
|
||||
export function depends(lg: CfLogic): { from: string; to: string }[] {
|
||||
const out: { from: string; to: string }[] = [];
|
||||
|
||||
Reference in New Issue
Block a user