From befb0c6bff25efb13133daae3eec15372aab6d91 Mon Sep 17 00:00:00 2001 From: youdie006 Date: Fri, 25 Sep 2026 23:45:12 +0900 Subject: [PATCH] Convert a number or boolean argument with asString in four string functions matches, normalize-space, substring and string-length switch only on string and query, so a number or boolean first argument fell through with an empty string. string-length(12345) returned 0, substring(12345, 2, 3) returned "" and normalize-space(false()) returned "". translate, replace, lower-case and concat already convert through asString, which formats numbers and booleans the way string() does. --- func.go | 8 ++++++++ xpath_function_test.go | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/func.go b/func.go index 56b14bc..0e26144 100644 --- a/func.go +++ b/func.go @@ -436,6 +436,8 @@ func matchesFunc(arg1, arg2 query) func(query, iterator) interface{} { return false } s = node.Value() + default: + s = asString(t, typ) } var pattern string var ok bool @@ -463,6 +465,8 @@ func normalizespaceFunc(arg1 query) func(query, iterator) interface{} { return "" } m = node.Value() + default: + m = asString(t, typ) } var b = builderPool.Get().(stringBuilder) b.Grow(len(m)) @@ -500,6 +504,8 @@ func substringFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} { return "" } m = node.Value() + default: + m = asString(t, typ) } var start, length float64 @@ -588,6 +594,8 @@ func stringLengthFunc(arg1 query) func(query, iterator) interface{} { break } return float64(utf8.RuneCountInString(node.Value())) + default: + return float64(utf8.RuneCountInString(asString(t, v))) } return float64(0) } diff --git a/xpath_function_test.go b/xpath_function_test.go index 5479b6d..50cb1bb 100644 --- a/xpath_function_test.go +++ b/xpath_function_test.go @@ -171,6 +171,8 @@ func Test_func_string_length(t *testing.T) { // characters, not bytes (REC 4.2) test_xpath_eval(t, empty_example, `string-length("héllo")`, float64(5)) test_xpath_eval(t, empty_example, `string-length("日本語")`, float64(3)) + test_xpath_eval(t, empty_example, `string-length(12345)`, float64(5)) + test_xpath_eval(t, empty_example, `string-length(true())`, float64(4)) } func Test_func_substring(t *testing.T) { @@ -180,6 +182,8 @@ func Test_func_substring(t *testing.T) { test_xpath_eval(t, empty_example, `substring("12345", 0, 1)`, "") test_xpath_eval(t, empty_example, `substring("12345", 0, 2)`, "1") test_xpath_eval(t, empty_example, `substring("12345", 0, 3)`, "12") + test_xpath_eval(t, empty_example, `substring(12345, 2, 3)`, "234") + test_xpath_eval(t, empty_example, `substring(1 div 0, 1, 3)`, "Inf") test_xpath_eval(t, empty_example, `substring("12345", 0, 5)`, "1234") test_xpath_eval(t, empty_example, `substring("12345", -1, 4)`, "12") test_xpath_eval(t, empty_example, `substring("12345", -2, 5)`, "12") @@ -246,6 +250,7 @@ func Test_func_translate(t *testing.T) { func Test_func_matches(t *testing.T) { test_xpath_eval(t, empty_example, `matches("abracadabra", "bra")`, true) + test_xpath_eval(t, empty_example, `matches(123, "^123$")`, true) test_xpath_eval(t, empty_example, `matches("abracadabra", "(?i)^A.*A$")`, true) test_xpath_eval(t, empty_example, `matches("abracadabra", "^a.*a$")`, true) test_xpath_eval(t, empty_example, `matches("abracadabra", "^bra")`, false) @@ -366,6 +371,8 @@ func Test_func_normalize_space(t *testing.T) { const expectedStr = `loooooooonnnnnnngggggggg tes t strin g` test_xpath_eval(t, empty_example, `normalize-space("`+testStr+`")`, expectedStr) test_xpath_eval(t, empty_example, `normalize-space(' abc ')`, "abc") + test_xpath_eval(t, empty_example, `normalize-space(12)`, "12") + test_xpath_eval(t, empty_example, `normalize-space(false())`, "false") n := selectNode(employee_example, `//employee[@id="1"]/name`) test_xpath_eval(t, n, `normalize-space()`, "Opal Kole") test_xpath_eval(t, n, `normalize-space(.)`, "Opal Kole")