A node with an explicit tag is not subject to implicit plain-scalar resolution, but composeScalarValue fell through to it whenever the tag was not a recognized core type. So a local tag (`!foo`), the non-specific `!`, or an unknown verbatim URI still let `! 12` resolve to the integer 12 and `!Thing false` to a boolean. When a tag is present but not a core type, fix the node as the string of its content instead of resolving it. Recognized core tags and untagged scalars are unchanged. Two compatibility tests asserted the old behavior (a custom- tagged `true` as a boolean); they are corrected to expect the string, the behavior the yaml-test-suite case S4JQ requires. Raises yaml-test-suite value-level conformance to 188/194 (96.9%) with the parse-level and should-error rejection rates unchanged. 452 tests pass.
86 lines
2.8 KiB
Swift
86 lines
2.8 KiB
Swift
extension PureYAML.Parsing.Parser {
|
|
func composeScalarValue(
|
|
_ value: String,
|
|
tag: String?,
|
|
style: PureYAML.Parsing.ScalarStyle,
|
|
mark: PureYAML.Parsing.Mark,
|
|
) throws -> PureYAML.Model.Value {
|
|
if let tagged = try composeTaggedScalarValue(value, tag: tag, mark: mark) {
|
|
return tagged
|
|
}
|
|
|
|
// An explicit tag that is not a recognized core type, a local tag like `!foo` or the
|
|
// non-specific `!`, still fixes the node as a string: a tagged scalar is not subject to the
|
|
// implicit plain-scalar resolution that would otherwise read `! 12` as the integer 12.
|
|
if normalizedTag(tag) != nil {
|
|
return .string(value)
|
|
}
|
|
|
|
switch style {
|
|
case .folded, .literal:
|
|
return .string(value)
|
|
case .plain:
|
|
return try parseScalar(value, line: mark.line)
|
|
case .doubleQuoted, .singleQuoted:
|
|
return .string(value)
|
|
}
|
|
}
|
|
|
|
func composeTaggedScalarValue(
|
|
_ value: String,
|
|
tag: String?,
|
|
mark: PureYAML.Parsing.Mark,
|
|
) throws -> PureYAML.Model.Value? {
|
|
guard let tag = normalizedTag(tag) else {
|
|
return nil
|
|
}
|
|
switch tag {
|
|
case "tag:yaml.org,2002:str":
|
|
return .string(value)
|
|
case "tag:yaml.org,2002:int":
|
|
guard let int = parseInteger(trim(value)) else {
|
|
throw invalidTaggedScalar(tag: "tag:yaml.org,2002:int", value: value, mark: mark)
|
|
}
|
|
return .int(int)
|
|
case "tag:yaml.org,2002:float":
|
|
guard let double = parseDouble(trim(value)) else {
|
|
throw invalidTaggedScalar(tag: "tag:yaml.org,2002:float", value: value, mark: mark)
|
|
}
|
|
return .double(double)
|
|
case "tag:yaml.org,2002:bool":
|
|
return try composeTaggedBoolValue(value, mark: mark)
|
|
case "tag:yaml.org,2002:null":
|
|
return .null
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func composeTaggedBoolValue(
|
|
_ value: String,
|
|
mark: PureYAML.Parsing.Mark,
|
|
) throws -> PureYAML.Model.Value {
|
|
guard let bool = parseBool(trim(value)) else {
|
|
throw invalidTaggedScalar(tag: "tag:yaml.org,2002:bool", value: value, mark: mark)
|
|
}
|
|
return .bool(bool)
|
|
}
|
|
|
|
func normalizedTag(_ tag: String?) -> String? {
|
|
PureYAML.Parsing.TagNormalizer.normalize(tag)
|
|
}
|
|
|
|
func invalidTaggedScalar(
|
|
tag: String,
|
|
value: String,
|
|
mark: PureYAML.Parsing.Mark,
|
|
) -> PureYAML.Parsing.ParseError {
|
|
PureYAML.Parsing.ParseError.invalidTaggedScalar(
|
|
tag: tag,
|
|
value: value,
|
|
line: mark.line,
|
|
column: mark.column,
|
|
)
|
|
}
|
|
}
|