MVP validation pseudo-code for the non-normative FROG reference implementation
FROG — Free Open Graphical Language
- 1. Overview
- 2. Status and Boundary
- 3. MVP Scope
- 4. Input Contract
- 5. Output Contract
- 6. Validation Strategy
- 7. Phase Breakdown
- 8. Helper Conventions
- 9. Pseudo-Code — Main Function
- 10. Pseudo-Code — Phase Functions
- 11. MVP Rules Enforced
- 12. Expected Invalid Rejections
- 13. Summary
This document defines the first detailed pseudo-code sketch for validate_source() in the FROG reference implementation.
Its purpose is to make the first validation stage directly implementable for the MVP slice already selected by the repository work.
The validator must decide whether loaded canonical source is acceptable as the basis for later derivation. If validation fails, the pipeline stops. If validation succeeds, the validator returns a compact validated basis for derivation.
This document is non-normative. It does not own the language rules. It is an implementation sketch that consumes the published repository rules and turns them into a staged validation procedure.
If this pseudo-code reveals ambiguity in the language rules, the specification should be clarified upstream instead of silently inventing private validator behavior.
The first validator slice covers:
- required top-level sections,
- basic metadata presence,
- basic interface declarations,
- diagram nodes and edges,
- core primitive use for the first examples,
interface_inputandinterface_output,widget_value,widget_reference,frog.ui.property_read,frog.ui.property_write,frog.ui.method_invoke,frog.core.delaywith requiredinitial,- basic cycle legality.
The first slice does not attempt to validate the entire future language surface. It validates enough to process the first example and conformance corpus coherently.
The validator consumes a loaded-source artifact conceptually equivalent to:
{
"artifact_kind": "frog_loaded_source",
"artifact_version": "0.1-dev",
"source": {
"path": "...",
"content_hash": "...",
"spec_version": "0.1"
},
"document": { ... canonical source object ... },
"diagnostics": []
}The validator assumes the source has already been decoded as structured data. It does not assume that the source is valid.
On success, the validator returns an artifact conceptually equivalent to:
{
"artifact_kind": "frog_validation_result",
"artifact_version": "0.1-dev",
"source_ref": {
"path": "...",
"content_hash": "..."
},
"status": "ok",
"validated_subset": { ... },
"validated_program": {
"program_id": "...",
"entry_kind": "single_frog_program",
"type_facts": [],
"resolved_entities": { ... }
},
"diagnostics": []
}On failure, the validator returns:
{
"artifact_kind": "frog_validation_result",
"artifact_version": "0.1-dev",
"source_ref": {
"path": "...",
"content_hash": "..."
},
"status": "error",
"error_code": "...",
"diagnostics": [
{
"severity": "error",
"message": "...",
"source_anchor": { ... }
}
]
}
No later stage may claim success if validation returned status = error.
The first validator should use a strict phased strategy:
- check top-level document shape,
- build local indexes,
- validate interface declarations,
- validate front-panel declarations if present,
- validate diagram node declarations,
- validate diagram edges,
- resolve types and participation roles,
- validate UI primitive usage,
- validate cycle legality,
- emit success or failure.
This phased shape keeps the code easy to debug and makes rejection reasons easier to explain.
Check that the source contains the required sections for canonical FROG source in the first slice.
Build fast lookup maps for:
- interface inputs,
- interface outputs,
- widgets,
- diagram nodes,
- diagram edges.
Validate uniqueness, identifiers, and supported MVP types for interface declarations.
If a front_panel exists, validate widgets, widget IDs, widget classes, roles, and value-carrying constraints.
Validate every node by kind and collect resolved facts.
Validate edge endpoints, port existence, directionality, and provisional type compatibility.
Check that:
widget_valueis used only for value participation,widget_referenceis used only as object-style widget participation,- standardized UI primitives receive the right kinds of inputs,
- no confusion exists between natural valueflow and object-style access.
Detect directed cycles and confirm that each cycle contains at least one explicit local-memory primitive in the MVP slice.
The pseudo-code below uses the following conventions:
ctxis a mutable validation context.ctx.errorsstores accumulated hard failures.ctx.warningsstores optional non-fatal notes.fail(...)appends an error and marks the validation as failed.stop_if_errors(ctx)returns early if hard failures already exist.resolved_*helpers compute implementation-side facts for later derivation.
function validate_source(loaded_source):
ctx = new ValidationContext()
ctx.source_ref = {
"path": loaded_source.source.path,
"content_hash": loaded_source.source.content_hash
}
document = loaded_source.document
validate_top_level_shape(document, ctx)
if ctx.has_errors():
return build_validation_error(ctx, "invalid_top_level_shape")
build_indexes(document, ctx)
if ctx.has_errors():
return build_validation_error(ctx, "index_build_failed")
validate_metadata(document, ctx)
validate_interface(document, ctx)
validate_front_panel(document, ctx)
if ctx.has_errors():
return build_validation_error(ctx, "invalid_declarations")
validate_diagram_nodes(document, ctx)
if ctx.has_errors():
return build_validation_error(ctx, "invalid_diagram_nodes")
validate_diagram_edges(document, ctx)
if ctx.has_errors():
return build_validation_error(ctx, "invalid_diagram_edges")
validate_ui_interaction_rules(document, ctx)
if ctx.has_errors():
return build_validation_error(ctx, "invalid_ui_interaction")
validate_cycle_legality(document, ctx)
if ctx.has_errors():
return build_validation_error(ctx, "invalid_cycles")
validated_program = build_validated_program(document, ctx)
return {
"artifact_kind": "frog_validation_result",
"artifact_version": "0.1-dev",
"source_ref": ctx.source_ref,
"status": "ok",
"validated_subset": build_validated_subset(ctx),
"validated_program": validated_program,
"diagnostics": ctx.warnings
}function validate_top_level_shape(document, ctx):
required_sections = ["spec_version", "metadata", "interface", "diagram"]
for key in required_sections:
if key not in document:
fail(ctx,
code = "missing_required_section",
message = "Missing required top-level section: " + key,
anchor = {"section": key})
if "spec_version" in document and document["spec_version"] != "0.1":
fail(ctx,
code = "unsupported_spec_version",
message = "Only spec_version 0.1 is supported in the first reference slice.",
anchor = {"section": "spec_version"})
if "interface" in document:
if type(document["interface"]) is not object:
fail(ctx,
code = "invalid_interface_section",
message = "interface must be an object.",
anchor = {"section": "interface"})
if "diagram" in document:
if type(document["diagram"]) is not object:
fail(ctx,
code = "invalid_diagram_section",
message = "diagram must be an object.",
anchor = {"section": "diagram"})function build_indexes(document, ctx):
ctx.interface_inputs = {}
ctx.interface_outputs = {}
ctx.widgets = {}
ctx.nodes = {}
ctx.edges = {}
if "interface" in document:
for item in document["interface"].get("inputs", []):
register_unique(ctx.interface_inputs, item, "id", ctx, "duplicate_interface_input_id")
for item in document["interface"].get("outputs", []):
register_unique(ctx.interface_outputs, item, "id", ctx, "duplicate_interface_output_id")
if "front_panel" in document:
for widget in document["front_panel"].get("widgets", []):
register_unique(ctx.widgets, widget, "id", ctx, "duplicate_widget_id")
if "diagram" in document:
for node in document["diagram"].get("nodes", []):
register_unique(ctx.nodes, node, "id", ctx, "duplicate_node_id")
for edge in document["diagram"].get("edges", []):
register_unique(ctx.edges, edge, "id", ctx, "duplicate_edge_id")function validate_metadata(document, ctx):
metadata = document.get("metadata", {})
if "name" not in metadata:
fail(ctx,
code = "missing_metadata_name",
message = "metadata.name is required in the first reference slice.",
anchor = {"section": "metadata"})
if "program_version" not in metadata:
ctx.warnings.append({
"severity": "warning",
"message": "metadata.program_version is recommended.",
"source_anchor": {"section": "metadata"}
})function validate_interface(document, ctx):
interface = document.get("interface", {})
inputs = interface.get("inputs", [])
outputs = interface.get("outputs", [])
for port in inputs:
validate_port_decl(port, "input", ctx)
for port in outputs:
validate_port_decl(port, "output", ctx)
function validate_port_decl(port, role, ctx):
if "id" not in port:
fail(ctx,
code = "missing_interface_port_id",
message = "Interface " + role + " is missing id.",
anchor = {"role": role})
return
if "type" not in port:
fail(ctx,
code = "missing_interface_port_type",
message = "Interface " + role + " '" + port["id"] + "' is missing type.",
anchor = {"interface_port": port["id"]})
return
if not is_supported_mvp_type(port["type"]):
fail(ctx,
code = "unsupported_interface_type",
message = "Unsupported MVP interface type: " + to_string(port["type"]),
anchor = {"interface_port": port["id"]})function validate_front_panel(document, ctx):
if "front_panel" not in document:
return
fp = document["front_panel"]
if "widgets" not in fp:
fail(ctx,
code = "missing_front_panel_widgets",
message = "front_panel.widgets is required when front_panel is present.",
anchor = {"section": "front_panel"})
return
for widget in fp["widgets"]:
validate_widget_decl(widget, ctx)
function validate_widget_decl(widget, ctx):
widget_id = widget.get("id")
if "widget" not in widget:
fail(ctx,
code = "missing_widget_class",
message = "Widget is missing widget class.",
anchor = {"widget_id": widget_id})
return
if not is_supported_mvp_widget_class(widget["widget"]):
fail(ctx,
code = "unsupported_widget_class",
message = "Unsupported MVP widget class: " + widget["widget"],
anchor = {"widget_id": widget_id})
role = widget.get("role")
if role not in ["control", "indicator"]:
fail(ctx,
code = "invalid_widget_role",
message = "Widget role must be 'control' or 'indicator' in the MVP slice.",
anchor = {"widget_id": widget_id})
if widget_requires_value_type(widget["widget"]):
if "value_type" not in widget:
fail(ctx,
code = "missing_widget_value_type",
message = "Value-carrying widget is missing value_type.",
anchor = {"widget_id": widget_id})
elif not is_supported_mvp_type(widget["value_type"]):
fail(ctx,
code = "unsupported_widget_value_type",
message = "Unsupported MVP widget value_type.",
anchor = {"widget_id": widget_id})function validate_diagram_nodes(document, ctx):
for node in document["diagram"].get("nodes", []):
kind = node.get("kind")
if kind == "interface_input":
validate_interface_input_node(node, ctx)
elif kind == "interface_output":
validate_interface_output_node(node, ctx)
elif kind == "primitive":
validate_primitive_node(node, ctx)
elif kind == "widget_value":
validate_widget_value_node(node, ctx)
elif kind == "widget_reference":
validate_widget_reference_node(node, ctx)
else:
fail(ctx,
code = "unsupported_node_kind",
message = "Unsupported MVP node kind: " + to_string(kind),
anchor = {"node_id": node.get("id")})function validate_interface_input_node(node, ctx):
port_id = node.get("interface_port")
if port_id not in ctx.interface_inputs:
fail(ctx,
code = "unknown_interface_input",
message = "interface_input references unknown input port.",
anchor = {"node_id": node.get("id")})
return
ctx.resolved_node_facts[node["id"]] = {
"kind": "interface_input",
"value_type": ctx.interface_inputs[port_id]["type"],
"interface_port": port_id
}
function validate_interface_output_node(node, ctx):
port_id = node.get("interface_port")
if port_id not in ctx.interface_outputs:
fail(ctx,
code = "unknown_interface_output",
message = "interface_output references unknown output port.",
anchor = {"node_id": node.get("id")})
return
ctx.resolved_node_facts[node["id"]] = {
"kind": "interface_output",
"value_type": ctx.interface_outputs[port_id]["type"],
"interface_port": port_id
}function validate_primitive_node(node, ctx):
primitive_ref = node.get("type")
if primitive_ref not in supported_mvp_primitives():
fail(ctx,
code = "unsupported_primitive_ref",
message = "Unsupported MVP primitive: " + to_string(primitive_ref),
anchor = {"node_id": node.get("id")})
return
if primitive_ref == "frog.core.delay":
if "initial" not in node:
fail(ctx,
code = "missing_delay_initial",
message = "frog.core.delay requires explicit initial in the MVP slice.",
anchor = {"node_id": node.get("id")})
return
if not is_numeric_literal(node["initial"]):
fail(ctx,
code = "invalid_delay_initial",
message = "frog.core.delay initial must be a numeric literal in the MVP slice.",
anchor = {"node_id": node.get("id")})
if primitive_ref in ["frog.ui.property_read", "frog.ui.property_write", "frog.ui.method_invoke"]:
validate_ui_primitive_metadata(node, ctx)
ctx.resolved_node_facts[node["id"]] = {
"kind": "primitive",
"primitive_ref": primitive_ref
}
function validate_ui_primitive_metadata(node, ctx):
primitive_ref = node["type"]
if primitive_ref in ["frog.ui.property_read", "frog.ui.property_write"]:
if "widget_member" not in node:
fail(ctx,
code = "missing_widget_member",
message = primitive_ref + " requires widget_member.",
anchor = {"node_id": node.get("id")})
return
member = node["widget_member"]
if "member" not in member:
fail(ctx,
code = "missing_widget_member_name",
message = "widget_member.member is required.",
anchor = {"node_id": node.get("id")})
if primitive_ref == "frog.ui.method_invoke":
if "widget_method" not in node:
fail(ctx,
code = "missing_widget_method",
message = "frog.ui.method_invoke requires widget_method.",
anchor = {"node_id": node.get("id")})function validate_widget_value_node(node, ctx):
widget_id = node.get("widget")
if widget_id not in ctx.widgets:
fail(ctx,
code = "unknown_widget_for_widget_value",
message = "widget_value references unknown widget.",
anchor = {"node_id": node.get("id")})
return
widget = ctx.widgets[widget_id]
if not widget_has_primary_value(widget):
fail(ctx,
code = "widget_value_on_non_value_widget",
message = "widget_value requires a value-carrying widget.",
anchor = {"node_id": node.get("id")})
return
ctx.resolved_node_facts[node["id"]] = {
"kind": "widget_value",
"widget_id": widget_id,
"widget_role": widget.get("role"),
"value_type": widget.get("value_type")
}
function validate_widget_reference_node(node, ctx):
widget_id = node.get("widget")
if widget_id not in ctx.widgets:
fail(ctx,
code = "unknown_widget_for_widget_reference",
message = "widget_reference references unknown widget.",
anchor = {"node_id": node.get("id")})
return
ctx.resolved_node_facts[node["id"]] = {
"kind": "widget_reference",
"widget_id": widget_id
}function validate_diagram_edges(document, ctx):
for edge in document["diagram"].get("edges", []):
validate_single_edge(edge, ctx)
function validate_single_edge(edge, ctx):
from_node_id = edge.get("from", {}).get("node")
from_port = edge.get("from", {}).get("port")
to_node_id = edge.get("to", {}).get("node")
to_port = edge.get("to", {}).get("port")
if from_node_id not in ctx.nodes:
fail(ctx,
code = "unknown_edge_source_node",
message = "Edge source node does not exist.",
anchor = {"edge_id": edge.get("id")})
return
if to_node_id not in ctx.nodes:
fail(ctx,
code = "unknown_edge_target_node",
message = "Edge target node does not exist.",
anchor = {"edge_id": edge.get("id")})
return
source_fact = ctx.resolved_node_facts.get(from_node_id)
target_fact = ctx.resolved_node_facts.get(to_node_id)
if source_fact is null or target_fact is null:
fail(ctx,
code = "edge_before_node_resolution",
message = "Cannot validate edge because node facts were not resolved.",
anchor = {"edge_id": edge.get("id")})
return
if not is_valid_output_port(source_fact, from_port):
fail(ctx,
code = "invalid_source_port",
message = "Invalid edge source port.",
anchor = {"edge_id": edge.get("id")})
return
if not is_valid_input_port(target_fact, to_port):
fail(ctx,
code = "invalid_target_port",
message = "Invalid edge target port.",
anchor = {"edge_id": edge.get("id")})
return
source_type = resolve_output_port_type(source_fact, from_port)
target_type = resolve_input_port_type(target_fact, to_port)
if not types_compatible(source_type, target_type):
fail(ctx,
code = "edge_type_mismatch",
message = "Edge type mismatch: " + to_string(source_type) + " -> " + to_string(target_type),
anchor = {"edge_id": edge.get("id")})function validate_ui_interaction_rules(document, ctx):
for node in document["diagram"].get("nodes", []):
if node.get("kind") != "primitive":
continue
primitive_ref = node.get("type")
if primitive_ref in ["frog.ui.property_read", "frog.ui.property_write", "frog.ui.method_invoke"]:
validate_ui_primitive_wiring(node, document, ctx)
validate_no_widget_reference_confusion(document, ctx)
function validate_ui_primitive_wiring(node, document, ctx):
node_id = node["id"]
incoming = find_incoming_edges(document, node_id)
has_ref = any(edge.to.port == "ref" for edge in incoming)
if not has_ref:
fail(ctx,
code = "missing_ui_primitive_ref_input",
message = node["type"] + " requires a ref input.",
anchor = {"node_id": node_id})
return
ref_edge = first(edge for edge in incoming if edge.to.port == "ref")
ref_source_fact = ctx.resolved_node_facts[ref_edge["from"]["node"]]
if ref_source_fact["kind"] != "widget_reference":
fail(ctx,
code = "ui_primitive_ref_must_come_from_widget_reference",
message = "UI primitive ref input must be driven by widget_reference.",
anchor = {"node_id": node_id})
function validate_no_widget_reference_confusion(document, ctx):
for edge in document["diagram"].get("edges", []):
from_node_id = edge["from"]["node"]
source_fact = ctx.resolved_node_facts[from_node_id]
if source_fact["kind"] != "widget_reference":
continue
target_node_id = edge["to"]["node"]
target_node = ctx.nodes[target_node_id]
if target_node.get("kind") != "primitive":
fail(ctx,
code = "widget_reference_without_ui_primitive",
message = "widget_reference must be used through a valid standardized UI primitive in the MVP slice.",
anchor = {"edge_id": edge.get("id")})
continue
if target_node.get("type") not in ["frog.ui.property_read", "frog.ui.property_write", "frog.ui.method_invoke"]:
fail(ctx,
code = "widget_reference_without_ui_primitive",
message = "widget_reference cannot feed ordinary primitives in the MVP slice.",
anchor = {"edge_id": edge.get("id")})function validate_cycle_legality(document, ctx):
graph = build_directed_graph_from_edges(document["diagram"].get("edges", []))
cycles = find_directed_cycles(graph)
for cycle in cycles:
if not cycle_contains_explicit_local_memory(cycle, ctx):
fail(ctx,
code = "illegal_feedback_without_explicit_memory",
message = "Directed cycle without explicit local-memory primitive.",
anchor = {
"node_ids": cycle.node_ids,
"edge_ids": cycle.edge_ids
})
function cycle_contains_explicit_local_memory(cycle, ctx):
for node_id in cycle.node_ids:
fact = ctx.resolved_node_facts[node_id]
if fact["kind"] == "primitive" and fact["primitive_ref"] == "frog.core.delay":
return true
return falsefunction build_validated_program(document, ctx):
return {
"program_id": make_program_id(ctx.source_ref["path"], document),
"entry_kind": "single_frog_program",
"type_facts": collect_type_facts(ctx),
"resolved_entities": {
"interface_inputs": list(keys(ctx.interface_inputs)),
"interface_outputs": list(keys(ctx.interface_outputs)),
"widgets": list(keys(ctx.widgets)),
"primitive_refs": collect_primitive_refs(ctx)
}
}
function build_validated_subset(ctx):
return {
"core_primitives": ctx.used_core_primitives,
"public_interface": ctx.used_public_interface,
"widget_value": ctx.used_widget_value,
"widget_reference": ctx.used_widget_reference,
"ui_object_primitives": ctx.used_ui_object_primitives,
"explicit_local_memory": ctx.used_explicit_local_memory
}The first validator should enforce at least the following MVP rules:
- required top-level sections must exist,
- interface ports must be declared explicitly and typed,
- front-panel widgets must be declared explicitly if referenced,
widget_valuerequires a value-carrying widget,widget_referencerequires an existing widget,- UI primitives must carry the required metadata,
- UI primitive
refmust come fromwidget_reference, widget_referencemust not be treated as ordinary valueflow,frog.core.delayrequires explicitinitial,- a cycle is valid only if it contains explicit local memory.
The first validator should explicitly reject the three initial invalid conformance cases:
- widget reference without UI primitive
- illegal feedback without explicit memory
- interface / widget role confusion
The validator must reject those cases rather than:
- inventing a missing primitive,
- inserting hidden memory,
- auto-converting widget participation into public interface participation,
- normalizing
widget_valueinto property access or the reverse.
This document provides a directly codable MVP pseudo-code sketch for validate_source() in the FROG reference implementation.
It keeps the validator narrow,
explicit,
and aligned with the published repository boundaries:
reject invalid source,
establish a validated basis,
and stop the pipeline unless derivation is semantically justified.