feat(swift_core): implement SchemaCompiler for AST translation#1736
feat(swift_core): implement SchemaCompiler for AST translation#1736pinieb wants to merge 33 commits into
Conversation
This change establishes the foundational infrastructure for Swift libraries in the A2UI monorepo. There is just enough code (JSONValue.swift and its tests) to standup a SwiftPM library and get CI up and running.
Every node in a compiled JSON Schema AST must have a deterministic identity to allow for $ref resolution and accurate error reporting. This identity is a combination of a Base URI and a JSON Pointer (RFC 6901). This change introduces the mechanisms to do this.
…xt management - Add `ValidationContext` and `ValidatorConfiguration` to safely pass execution state (like max depth limits) down the AST without mutation. - Add `ValidationResult` struct designed to support the 2020-12 hierarchical output format, tracking errors and annotations. - Define the `KeywordEvaluator` protocol to standardize keyword execution. - Implement the `SchemaNode` executable AST struct with logic to aggregate results, errors, and annotations from multiple evaluators. - Add test coverage for recursion depth limits and result aggregation.
…egistry - Add `SchemaRegistry` as a shared reference type to cache compiled `SchemaNode`s, enabling synchronous `$ref` resolution and preventing infinite CoW loops during deep evaluation. - Add `KeywordRegistry` and `EvaluatorFactory` to decouple keyword parsing from the core engine, allowing runtime extensibility. - Stub `SchemaCompiler` to resolve dependency graph for applicators. - Add test coverage for node caching and custom keyword injection.
- Fully implement `SchemaCompiler` to translate `JSONValue` into `SchemaNode`s. - Add support for boolean schemas (`true` = always valid, `false` = always invalid). - Implement structural keyword interception (`$id`, `$defs`, `$dynamicAnchor`). - Ensure `$id` properly updates the base URI and resets the JSON pointer scope. - Ensure `$defs` are recursively compiled and cached in the `SchemaRegistry` for synchronous `$ref` lookup during evaluation. - Add comprehensive compiler test coverage for scope resets and definition caching.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Pete Biencourt <pinieb@users.noreply.github.com>
…eb/a2ui into swift-json-schema-protocols
# Conflicts: # Package.swift # renderers/swift_core/AGENTS.md # swift/core/CODING_STANDARDS.md # swift/core/JSONSchema/README.md # swift/core/JSONSchema/Sources/JSONPointer.swift # swift/core/JSONSchema/Sources/JSONValue.swift # swift/core/JSONSchema/Sources/KeywordEvaluator.swift # swift/core/JSONSchema/Sources/SchemaIdentity.swift # swift/core/JSONSchema/Sources/SchemaNode.swift # swift/core/JSONSchema/Sources/SchemaRegistry.swift # swift/core/JSONSchema/Sources/ValidationContext.swift # swift/core/JSONSchema/Sources/ValidationError.swift # swift/core/JSONSchema/Sources/ValidationResult.swift # swift/core/JSONSchema/Sources/ValidatorConfiguration.swift # swift/core/JSONSchema/Tests/ExecutionContractsTests.swift # swift/core/JSONSchema/Tests/JSONValueTests.swift # swift/core/JSONSchema/Tests/SchemaIdentityTests.swift # swift/core/README.md
…gement # Conflicts: # swift/core/JSONSchema/Sources/KeywordRegistry.swift # swift/core/JSONSchema/Sources/SchemaCompiler.swift # swift/core/JSONSchema/Tests/KeywordRegistryTests.swift # swift/core/JSONSchema/Tests/SchemaRegistryTests.swift
…piler # Conflicts: # swift/core/JSONSchema/Sources/SchemaCompilerError.swift # swift/core/JSONSchema/Tests/SchemaCompilerTests.swift
…and compiler tests
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Pete Biencourt <pinieb@users.noreply.github.com>
There was a problem hiding this comment.
Code Review
This pull request introduces the A2UISwiftCore package, establishing a generic JSON Schema Draft 2020-12 validator and DSL builder target (JSONSchema) in Swift, complete with unit tests, documentation, and formatting scripts. The review feedback highlights critical improvements needed for full schema specification compliance: SchemaIdentity should support plain string anchors in URI fragments rather than assuming they are always JSONPointers, and compiled schemas must be registered under their anchor-based URIs in the SchemaRegistry to prevent reference resolution failures. Additionally, updatingBaseURI should be updated to correctly handle and split fragments in the new URI to avoid malformed URIs.
| guard let parsedPointer = JSONPointer(stringRepresentation: fragment) else { | ||
| return nil | ||
| } |
There was a problem hiding this comment.
The SchemaIdentity initializer currently assumes that any URI fragment is a valid JSONPointer. If the fragment is a plain anchor (e.g., #my-anchor), JSONPointer(stringRepresentation:) will return nil, causing the entire SchemaIdentity initialization to fail.\n\nIn JSON Schema Draft 2020-12, plain anchors are valid URI fragments used by $anchor and $dynamicAnchor. To support resolving schemas via anchors, SchemaIdentity should be updated to allow fragments that are either a JSONPointer or a plain string anchor.
| var parsedAnchor: String? = nil | ||
| if let anchorVal = dict["$dynamicAnchor"], case .string(let anchorStr) = anchorVal { | ||
| parsedAnchor = anchorStr | ||
| } |
There was a problem hiding this comment.
While $dynamicAnchor is parsed here, the compiled SchemaNode is not registered in the SchemaRegistry under its anchor-based URI (e.g., baseURI + \"#\" + anchorStr). Additionally, interception and registration for the standard $anchor keyword are completely missing.\n\nThis is likely a consequence of SchemaIdentity only supporting JSONPointer fragments. Without registering schemas under their anchor URIs, resolving references ($ref or $dynamicRef) that point to anchors (e.g., \"$ref\": \"#my-anchor\") will fail during evaluation.
| public func updatingBaseURI(to newURI: String) -> SchemaIdentity { | ||
| return SchemaIdentity(uncheckedBaseURI: newURI, pointer: JSONPointer()) | ||
| } |
There was a problem hiding this comment.
If newURI contains a fragment, SchemaIdentity(uncheckedBaseURI:pointer:) will not split it, leaving the fragment inside baseURI. This can lead to malformed URIs when subsequent paths are appended (e.g., https://example.com/schema#foo#/properties/bar).\n\nWe can resolve this by attempting to parse newURI using SchemaIdentity(uri:) first, which correctly splits the fragment if present.
public func updatingBaseURI(to newURI: String) -> SchemaIdentity {
if let parsed = SchemaIdentity(uri: newURI) {
return parsed
}
return SchemaIdentity(uncheckedBaseURI: newURI, pointer: JSONPointer())
}…eb/a2ui into swift-json-schema-protocols
Description
SchemaCompilerto translateJSONValueintoSchemaNodes.true= always valid,false= always invalid).$id,$defs,$dynamicAnchor).$idproperly updates the base URI and resets the JSON pointer scope.$defsare recursively compiled and cached in theSchemaRegistryfor synchronous
$reflookup during evaluation.Pre-launch Checklist
For this PR:
run_tests.shfor SwiftIf you need help, consider asking for advice on the discussion board.