Extract import declaration parsing helpers

This commit is contained in:
2026-10-25 13:23:09 +00:00
parent 6bc625b68f
commit 2c3eb3308b
2 changed files with 95 additions and 53 deletions

View File

@@ -1,6 +1,6 @@
# 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.
## 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
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`.
@@ -502,6 +502,12 @@ diagnostics remain in `imports.go`. `./scripts/check.sh` passed.
selector rules, default resolution, or diagnostics. Keep behavior tests as the
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
**Owner:** engine `import_selection.go`, using existing directory/link helpers.

View File

@@ -251,40 +251,17 @@ func parseImportDeclarationSpec(node *document.Node, declarationPath Path) (impo
}
switch entry.Key {
case "required", "recursive", "follow", "hidden":
if entry.Value.Kind != document.Boolean {
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, fieldPath, entry.Key+" must be a boolean", entry.Value)}
}
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
if diagnostic := parseImportBooleanAssignment(&spec, entry.Key, entry.Value, fieldPath); diagnostic != nil {
return importDeclarationSpec{}, []Diagnostic{*diagnostic}
}
case "pattern":
values, origins, ok, badIndex := parseStringSet(entry.Value)
if !ok {
return importDeclarationSpec{}, []Diagnostic{stringSetDiagnostic(entry.Value, fieldPath, "pattern", badIndex)}
if diagnostic := parseImportWildcardAssignment(&spec, entry.Key, entry.Value, fieldPath); diagnostic != nil {
return importDeclarationSpec{}, []Diagnostic{*diagnostic}
}
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":
values, origins, ok, badIndex := parseStringSet(entry.Value)
if !ok {
return importDeclarationSpec{}, []Diagnostic{stringSetDiagnostic(entry.Value, fieldPath, "exclude", badIndex)}
if diagnostic := parseImportWildcardAssignment(&spec, entry.Key, entry.Value, fieldPath); diagnostic != nil {
return importDeclarationSpec{}, []Diagnostic{*diagnostic}
}
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":
depth, ok := parseImportDepth(entry.Value)
if !ok {
@@ -298,17 +275,11 @@ func parseImportDeclarationSpec(node *document.Node, declarationPath Path) (impo
}
spec.destination = destination
case "placement":
if entry.Value.Kind != document.String {
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, fieldPath, "placement must be mapping or sequence-item", entry.Value)}
}
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)}
placement, diagnostic := parseImportPlacement(entry.Value, fieldPath)
if diagnostic != nil {
return importDeclarationSpec{}, []Diagnostic{*diagnostic}
}
spec.placement = placement
default:
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)
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 {
case importSelectorPath, importSelectorDirectory:
values, origins, ok, badIndex := parseStringSet(selectorEntry.Value)
values, origins, ok, badIndex := parseStringSet(node)
if !ok {
name := selectorEntry.Key
return importDeclarationSpec{}, []Diagnostic{stringSetDiagnostic(selectorEntry.Value, selectorPath, name, badIndex)}
diagnostic := stringSetDiagnostic(node, selectorPath, field, badIndex)
return &diagnostic
}
for index, value := range values {
if !validImportPath(value) {
badPath, badNode := listMemberSite(selectorEntry.Value, selectorPath, origins[index])
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, badPath, selectorEntry.Key+" contains an invalid source path", badNode)}
badPath, badNode := listMemberSite(node, selectorPath, origins[index])
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.paths = values
case importSelectorGlob:
if selectorEntry.Value.Kind != document.String || !validImportPath(selectorEntry.Value.Text) {
return importDeclarationSpec{}, []Diagnostic{nodeImportDiagnostic(CodeImportInvalid, selectorPath, "glob must be a valid nonempty path pattern string", selectorEntry.Value)}
if node.Kind != document.String || !validImportPath(node.Text) {
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 {
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 {