mirror of
https://github.com/morluto/rea.git
synced 2026-10-27 01:45:32 +00:00
141 lines
4.6 KiB
TypeScript
141 lines
4.6 KiB
TypeScript
import type { JsonValue } from "../../domain/jsonValue.js";
|
|
import type {
|
|
JavaScriptSemanticBinding,
|
|
JavaScriptSemanticValue,
|
|
} from "../../domain/javascript/javascriptSemanticIr.js";
|
|
import { createJavaScriptSemanticGraphUnknown } from "../../domain/javascript/javascriptSemanticGraph.js";
|
|
import {
|
|
retainSemanticGraphNode,
|
|
addSemanticGraphRelation,
|
|
addSemanticGraphUnknown,
|
|
} from "./JavaScriptSemanticGraphConstruction.js";
|
|
import type { SemanticFlowProjectionContext } from "./JavaScriptSemanticGraphFlowProjection.js";
|
|
import { observedSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
|
|
|
/** Project bounded literal values and object slots for exact query seeds. */
|
|
export const projectSemanticValues = (
|
|
context: SemanticFlowProjectionContext,
|
|
): void => {
|
|
for (const binding of context.ir.bindings) {
|
|
const bindingNode = context.bindingNodes.get(binding.bindingId);
|
|
if (bindingNode === undefined) continue;
|
|
projectValue({
|
|
context,
|
|
binding,
|
|
value: binding.value,
|
|
target: bindingNode,
|
|
role: "binding",
|
|
});
|
|
}
|
|
};
|
|
|
|
interface ValueProjectionInput {
|
|
readonly context: SemanticFlowProjectionContext;
|
|
readonly binding: JavaScriptSemanticBinding;
|
|
readonly value: JavaScriptSemanticValue;
|
|
readonly target: ReturnType<typeof semanticPropertySlot>;
|
|
readonly role: string;
|
|
}
|
|
|
|
const projectValue = (input: ValueProjectionInput): void => {
|
|
const { context, binding, value, target, role } = input;
|
|
if (target === null) return;
|
|
if (value.status === "literal") {
|
|
const literal = addLiteralNode(context, binding, value.value, role);
|
|
addSemanticGraphRelation(context.state, {
|
|
source: literal,
|
|
target,
|
|
relation: "defines",
|
|
resolution: "resolved",
|
|
});
|
|
} else if (value.status === "union")
|
|
for (const primitive of value.values) {
|
|
const literal = addLiteralNode(context, binding, primitive, role);
|
|
addSemanticGraphRelation(context.state, {
|
|
source: literal,
|
|
target,
|
|
relation: "defines",
|
|
resolution: "candidate",
|
|
});
|
|
}
|
|
else if (value.status === "object")
|
|
for (const property of value.properties) {
|
|
const slot = semanticPropertySlot(
|
|
context,
|
|
binding.bindingId,
|
|
property.name,
|
|
);
|
|
addSemanticGraphRelation(context.state, {
|
|
source: target,
|
|
target: slot,
|
|
relation: "writes-property",
|
|
resolution: value.unknownProperties ? "candidate" : "resolved",
|
|
});
|
|
projectValue({
|
|
context,
|
|
binding,
|
|
value: property.value,
|
|
target: slot,
|
|
role: `property:${property.name}`,
|
|
});
|
|
}
|
|
else if (value.status === "unknown" && value.resourceLimit !== undefined) {
|
|
const location = binding.definitions[0]?.location ?? null;
|
|
const evidence = observedSemanticEvidence(context.file, location);
|
|
const isPropertyValue = role.startsWith("property:");
|
|
addSemanticGraphUnknown(
|
|
context.state,
|
|
createJavaScriptSemanticGraphUnknown({
|
|
node_id: target.node_id,
|
|
family: isPropertyValue ? "object-flow" : "data-flow",
|
|
relation_kinds: [isPropertyValue ? "writes-property" : "defines"],
|
|
reason: "resource-limit",
|
|
detail: `${value.reason} Unknown value at ${role}.`,
|
|
candidate_node_ids: [target.node_id],
|
|
evidence: {
|
|
...evidence,
|
|
authority: "unknown",
|
|
state: "unknown",
|
|
confidence: "unknown",
|
|
limitations: [value.reason],
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
};
|
|
|
|
const addLiteralNode = (
|
|
context: SemanticFlowProjectionContext,
|
|
binding: JavaScriptSemanticBinding,
|
|
value: string | number | boolean | null,
|
|
role: string,
|
|
) =>
|
|
retainSemanticGraphNode(context.state, context.file, {
|
|
kind: "literal",
|
|
roleKey: `literal:${binding.bindingId}:${role}:${JSON.stringify(value)}`,
|
|
location: binding.definitions[0]?.location ?? null,
|
|
label: JSON.stringify(value),
|
|
functionNodeId:
|
|
context.bindingNodes.get(binding.bindingId)?.function_node_id ?? null,
|
|
properties: { value },
|
|
});
|
|
|
|
/** Create one canonical property slot for a binding/property identity. */
|
|
export const semanticPropertySlot = (
|
|
context: SemanticFlowProjectionContext,
|
|
objectBindingId: string,
|
|
name: string,
|
|
) =>
|
|
retainSemanticGraphNode(context.state, context.file, {
|
|
kind: "property-slot",
|
|
roleKey: `property:${objectBindingId}:${name}`,
|
|
location: null,
|
|
label: name,
|
|
functionNodeId:
|
|
context.bindingNodes.get(objectBindingId)?.function_node_id ?? null,
|
|
properties: {
|
|
name,
|
|
object_binding_id: objectBindingId,
|
|
} satisfies Readonly<Record<string, JsonValue>>,
|
|
});
|