Extract import declaration parsing helpers
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Consolidation Implementation Plan
|
# Consolidation Implementation Plan
|
||||||
|
|
||||||
Status: in progress. Stages 1 through 10 are complete; follow Stage 11 through Stage 20 in order.
|
Status: in progress. Stages 1 through 11 are complete; follow Stage 12 through Stage 20 in order.
|
||||||
The target executor is a Luna coding agent working through one end-to-end sprint.
|
The target executor is a Luna coding agent working through one end-to-end sprint.
|
||||||
|
|
||||||
## Authority, Baseline, And Execution
|
## Authority, Baseline, And Execution
|
||||||
@@ -482,7 +482,7 @@ Progress notes (2026-10-25): moved request acquisition and traversal admission t
|
|||||||
projection to `import_projection.go`. Shared limits, traversal data, and import
|
projection to `import_projection.go`. Shared limits, traversal data, and import
|
||||||
diagnostics remain in `imports.go`. `./scripts/check.sh` passed.
|
diagnostics remain in `imports.go`. `./scripts/check.sh` passed.
|
||||||
|
|
||||||
## Stage 11 — Extract Import Declaration Parsing Operations
|
## Stage 11 — Extract Import Declaration Parsing Operations (Complete)
|
||||||
|
|
||||||
**Owner:** engine `import_spec.go`.
|
**Owner:** engine `import_spec.go`.
|
||||||
|
|
||||||
@@ -502,6 +502,12 @@ diagnostics remain in `imports.go`. `./scripts/check.sh` passed.
|
|||||||
selector rules, default resolution, or diagnostics. Keep behavior tests as the
|
selector rules, default resolution, or diagnostics. Keep behavior tests as the
|
||||||
owner; no helper-by-helper assertion inventory is needed.
|
owner; no helper-by-helper assertion inventory is needed.
|
||||||
|
|
||||||
|
Progress notes (2026-10-25): extracted boolean assignment, shared wildcard set
|
||||||
|
assignment, placement parsing, and selector-value parsing into private helpers.
|
||||||
|
The declaration parser retains its validation ordering, authored member sites,
|
||||||
|
and nil-versus-empty presence behavior. Import-focused tests and
|
||||||
|
`./scripts/check.sh` passed.
|
||||||
|
|
||||||
## Stage 12 — Extract Directory Entry Admission And Descent
|
## Stage 12 — Extract Directory Entry Admission And Descent
|
||||||
|
|
||||||
**Owner:** engine `import_selection.go`, using existing directory/link helpers.
|
**Owner:** engine `import_selection.go`, using existing directory/link helpers.
|
||||||
|
|||||||
@@ -251,40 +251,17 @@ func parseImportDeclarationSpec(node *document.Node, declarationPath Path) (impo
|
|||||||
}
|
}
|
||||||
switch entry.Key {
|
switch entry.Key {
|
||||||
case "required", "recursive", "follow", "hidden":
|
case "required", "recursive", "follow", "hidden":
|
||||||
if entry.Value.Kind != document.Boolean {
|
if diagnostic := parseImportBooleanAssignment(&spec, entry.Key, entry.Value, fieldPath); diagnostic != nil {
|
||||||
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, fieldPath, entry.Key+" must be a boolean", entry.Value)}
|
return importDeclarationSpec{}, []Diagnostic{*diagnostic}
|
||||||
}
|
|
||||||
value := entry.Value.Text == "true"
|
|
||||||
switch entry.Key {
|
|
||||||
case "required":
|
|
||||||
spec.required = &value
|
|
||||||
case "recursive":
|
|
||||||
spec.recursive = &value
|
|
||||||
case "follow":
|
|
||||||
spec.follow = &value
|
|
||||||
case "hidden":
|
|
||||||
spec.hidden = &value
|
|
||||||
}
|
}
|
||||||
case "pattern":
|
case "pattern":
|
||||||
values, origins, ok, badIndex := parseStringSet(entry.Value)
|
if diagnostic := parseImportWildcardAssignment(&spec, entry.Key, entry.Value, fieldPath); diagnostic != nil {
|
||||||
if !ok {
|
return importDeclarationSpec{}, []Diagnostic{*diagnostic}
|
||||||
return importDeclarationSpec{}, []Diagnostic{stringSetDiagnostic(entry.Value, fieldPath, "pattern", badIndex)}
|
|
||||||
}
|
}
|
||||||
if badIndex, ok := invalidWildcard(values); ok {
|
|
||||||
badPath, badNode := listMemberSite(entry.Value, fieldPath, origins[badIndex])
|
|
||||||
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, badPath, "pattern contains an invalid wildcard expression", badNode)}
|
|
||||||
}
|
|
||||||
spec.pattern, spec.patternSet = values, true
|
|
||||||
case "exclude":
|
case "exclude":
|
||||||
values, origins, ok, badIndex := parseStringSet(entry.Value)
|
if diagnostic := parseImportWildcardAssignment(&spec, entry.Key, entry.Value, fieldPath); diagnostic != nil {
|
||||||
if !ok {
|
return importDeclarationSpec{}, []Diagnostic{*diagnostic}
|
||||||
return importDeclarationSpec{}, []Diagnostic{stringSetDiagnostic(entry.Value, fieldPath, "exclude", badIndex)}
|
|
||||||
}
|
}
|
||||||
if badIndex, ok := invalidWildcard(values); ok {
|
|
||||||
badPath, badNode := listMemberSite(entry.Value, fieldPath, origins[badIndex])
|
|
||||||
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, badPath, "exclude contains an invalid wildcard expression", badNode)}
|
|
||||||
}
|
|
||||||
spec.exclude, spec.excludeSet = values, true
|
|
||||||
case "depth":
|
case "depth":
|
||||||
depth, ok := parseImportDepth(entry.Value)
|
depth, ok := parseImportDepth(entry.Value)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -298,17 +275,11 @@ func parseImportDeclarationSpec(node *document.Node, declarationPath Path) (impo
|
|||||||
}
|
}
|
||||||
spec.destination = destination
|
spec.destination = destination
|
||||||
case "placement":
|
case "placement":
|
||||||
if entry.Value.Kind != document.String {
|
placement, diagnostic := parseImportPlacement(entry.Value, fieldPath)
|
||||||
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, fieldPath, "placement must be mapping or sequence-item", entry.Value)}
|
if diagnostic != nil {
|
||||||
}
|
return importDeclarationSpec{}, []Diagnostic{*diagnostic}
|
||||||
switch entry.Value.Text {
|
|
||||||
case "mapping":
|
|
||||||
spec.placement = placementModeMapping
|
|
||||||
case "sequence-item":
|
|
||||||
spec.placement = placementModeSequenceItem
|
|
||||||
default:
|
|
||||||
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, fieldPath, "placement must be mapping or sequence-item", entry.Value)}
|
|
||||||
}
|
}
|
||||||
|
spec.placement = placement
|
||||||
default:
|
default:
|
||||||
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, fieldPath, "import declaration field is not recognized", entry.Value)}
|
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, fieldPath, "import declaration field is not recognized", entry.Value)}
|
||||||
}
|
}
|
||||||
@@ -316,33 +287,98 @@ func parseImportDeclarationSpec(node *document.Node, declarationPath Path) (impo
|
|||||||
|
|
||||||
selectorPath := declarationPath.Key(selectorEntry.Key)
|
selectorPath := declarationPath.Key(selectorEntry.Key)
|
||||||
spec.fieldSites[selectorEntry.Key] = importFieldSite{path: selectorPath.clone(), location: publicLocation(selectorEntry.Value.Location)}
|
spec.fieldSites[selectorEntry.Key] = importFieldSite{path: selectorPath.clone(), location: publicLocation(selectorEntry.Value.Location)}
|
||||||
|
if diagnostic := parseImportSelectorValue(&spec, selectorEntry.Key, selectorEntry.Value, selectorPath); diagnostic != nil {
|
||||||
|
return importDeclarationSpec{}, []Diagnostic{*diagnostic}
|
||||||
|
}
|
||||||
|
return spec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseImportBooleanAssignment(spec *importDeclarationSpec, field string, node *document.Node, fieldPath Path) *Diagnostic {
|
||||||
|
if node.Kind != document.Boolean {
|
||||||
|
diagnostic := nodeImportDiagnostic(CodeImportInvalid, fieldPath, field+" must be a boolean", node)
|
||||||
|
return &diagnostic
|
||||||
|
}
|
||||||
|
value := node.Text == "true"
|
||||||
|
switch field {
|
||||||
|
case "required":
|
||||||
|
spec.required = &value
|
||||||
|
case "recursive":
|
||||||
|
spec.recursive = &value
|
||||||
|
case "follow":
|
||||||
|
spec.follow = &value
|
||||||
|
case "hidden":
|
||||||
|
spec.hidden = &value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseImportWildcardAssignment(spec *importDeclarationSpec, field string, node *document.Node, fieldPath Path) *Diagnostic {
|
||||||
|
values, origins, ok, badIndex := parseStringSet(node)
|
||||||
|
if !ok {
|
||||||
|
diagnostic := stringSetDiagnostic(node, fieldPath, field, badIndex)
|
||||||
|
return &diagnostic
|
||||||
|
}
|
||||||
|
if badIndex, ok := invalidWildcard(values); ok {
|
||||||
|
badPath, badNode := listMemberSite(node, fieldPath, origins[badIndex])
|
||||||
|
diagnostic := nodeImportDiagnostic(CodeImportInvalid, badPath, field+" contains an invalid wildcard expression", badNode)
|
||||||
|
return &diagnostic
|
||||||
|
}
|
||||||
|
switch field {
|
||||||
|
case "pattern":
|
||||||
|
spec.pattern, spec.patternSet = values, true
|
||||||
|
case "exclude":
|
||||||
|
spec.exclude, spec.excludeSet = values, true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseImportPlacement(node *document.Node, fieldPath Path) (placementMode, *Diagnostic) {
|
||||||
|
if node.Kind != document.String {
|
||||||
|
diagnostic := nodeImportDiagnostic(CodeImportInvalid, fieldPath, "placement must be mapping or sequence-item", node)
|
||||||
|
return placementModeMapping, &diagnostic
|
||||||
|
}
|
||||||
|
switch node.Text {
|
||||||
|
case "mapping":
|
||||||
|
return placementModeMapping, nil
|
||||||
|
case "sequence-item":
|
||||||
|
return placementModeSequenceItem, nil
|
||||||
|
default:
|
||||||
|
diagnostic := nodeImportDiagnostic(CodeImportInvalid, fieldPath, "placement must be mapping or sequence-item", node)
|
||||||
|
return placementModeMapping, &diagnostic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseImportSelectorValue(spec *importDeclarationSpec, field string, node *document.Node, selectorPath Path) *Diagnostic {
|
||||||
switch spec.selector {
|
switch spec.selector {
|
||||||
case importSelectorPath, importSelectorDirectory:
|
case importSelectorPath, importSelectorDirectory:
|
||||||
values, origins, ok, badIndex := parseStringSet(selectorEntry.Value)
|
values, origins, ok, badIndex := parseStringSet(node)
|
||||||
if !ok {
|
if !ok {
|
||||||
name := selectorEntry.Key
|
diagnostic := stringSetDiagnostic(node, selectorPath, field, badIndex)
|
||||||
return importDeclarationSpec{}, []Diagnostic{stringSetDiagnostic(selectorEntry.Value, selectorPath, name, badIndex)}
|
return &diagnostic
|
||||||
}
|
}
|
||||||
for index, value := range values {
|
for index, value := range values {
|
||||||
if !validImportPath(value) {
|
if !validImportPath(value) {
|
||||||
badPath, badNode := listMemberSite(selectorEntry.Value, selectorPath, origins[index])
|
badPath, badNode := listMemberSite(node, selectorPath, origins[index])
|
||||||
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, badPath, selectorEntry.Key+" contains an invalid source path", badNode)}
|
diagnostic := nodeImportDiagnostic(CodeImportInvalid, badPath, field+" contains an invalid source path", badNode)
|
||||||
|
return &diagnostic
|
||||||
}
|
}
|
||||||
badPath, badNode := listMemberSite(selectorEntry.Value, selectorPath, origins[index])
|
badPath, badNode := listMemberSite(node, selectorPath, origins[index])
|
||||||
spec.selectorSites = append(spec.selectorSites, importFieldSite{path: badPath.clone(), location: publicLocation(badNode.Location)})
|
spec.selectorSites = append(spec.selectorSites, importFieldSite{path: badPath.clone(), location: publicLocation(badNode.Location)})
|
||||||
}
|
}
|
||||||
spec.paths = values
|
spec.paths = values
|
||||||
case importSelectorGlob:
|
case importSelectorGlob:
|
||||||
if selectorEntry.Value.Kind != document.String || !validImportPath(selectorEntry.Value.Text) {
|
if node.Kind != document.String || !validImportPath(node.Text) {
|
||||||
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, selectorPath, "glob must be a valid nonempty path pattern string", selectorEntry.Value)}
|
diagnostic := nodeImportDiagnostic(CodeImportInvalid, selectorPath, "glob must be a valid nonempty path pattern string", node)
|
||||||
|
return &diagnostic
|
||||||
}
|
}
|
||||||
segments, ok := validGlobPattern(selectorEntry.Value.Text)
|
segments, ok := validGlobPattern(node.Text)
|
||||||
if !ok {
|
if !ok {
|
||||||
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, selectorPath, "glob pattern is invalid", selectorEntry.Value)}
|
diagnostic := nodeImportDiagnostic(CodeImportInvalid, selectorPath, "glob pattern is invalid", node)
|
||||||
|
return &diagnostic
|
||||||
}
|
}
|
||||||
spec.glob, spec.globSegments = selectorEntry.Value.Text, segments
|
spec.glob, spec.globSegments = node.Text, segments
|
||||||
}
|
}
|
||||||
return spec, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectorForField(field string) importSelector {
|
func selectorForField(field string) importSelector {
|
||||||
|
|||||||
Reference in New Issue
Block a user