diff --git a/package-lock.json b/package-lock.json index 10e1c79..4fdffbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "MIT", "dependencies": { "@abaplint/cli": "^2.118.10", - "@abaplint/runtime": "^2.13.10", - "@abaplint/transpiler-cli": "^2.13.10" + "@abaplint/runtime": "^2.13.11", + "@abaplint/transpiler-cli": "^2.13.11" } }, "node_modules/@abaplint/cli": { @@ -30,9 +30,9 @@ } }, "node_modules/@abaplint/runtime": { - "version": "2.13.10", - "resolved": "https://registry.npmjs.org/@abaplint/runtime/-/runtime-2.13.10.tgz", - "integrity": "sha512-tlbdT7bqQaLUF6kbk9uzObIP54DhLeOpB3rLCcXsxvCECc3pQWSs0Uxw4zPycuuvtSXF3jf3xMvkXKaHTHMzEA==", + "version": "2.13.11", + "resolved": "https://registry.npmjs.org/@abaplint/runtime/-/runtime-2.13.11.tgz", + "integrity": "sha512-X82A2QdZUEeTlyPGSXzuCT0p5jykk6gobBILF10ZSdBGWQVNSMup8mAcRbeZJvXPvE++V+1Oamn5kaM5epU0Eg==", "license": "MIT", "dependencies": { "temporal-polyfill": "^0.3.2" @@ -42,9 +42,9 @@ } }, "node_modules/@abaplint/transpiler-cli": { - "version": "2.13.10", - "resolved": "https://registry.npmjs.org/@abaplint/transpiler-cli/-/transpiler-cli-2.13.10.tgz", - "integrity": "sha512-Zxy6YwuvjoqvMbN0PnhGp3gSjOliEVcXIna9V36SDq3Atnd4YPEvJZw09JiQQOOOXs3fkU7WyG6d9iN6DG0lUg==", + "version": "2.13.11", + "resolved": "https://registry.npmjs.org/@abaplint/transpiler-cli/-/transpiler-cli-2.13.11.tgz", + "integrity": "sha512-4XLkBF1144CGibVmjqA/4F7gUL5eathfXleJCHJU0bNwHWgYu+H06cjrzRdOyhgTZLM0LGRlQTeOjowcLlCu6w==", "license": "MIT", "bin": { "abap_transpile": "abap_transpile" diff --git a/package.json b/package.json index 2ded7e0..c2152ac 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "homepage": "https://github.com/larshp/zmjs#readme", "dependencies": { "@abaplint/cli": "^2.118.10", - "@abaplint/runtime": "^2.13.10", - "@abaplint/transpiler-cli": "^2.13.10" + "@abaplint/runtime": "^2.13.11", + "@abaplint/transpiler-cli": "^2.13.11" } } diff --git a/src/zcl_mjs.clas.abap b/src/zcl_mjs.clas.abap index 702667d..f091d90 100644 --- a/src/zcl_mjs.clas.abap +++ b/src/zcl_mjs.clas.abap @@ -3014,27 +3014,68 @@ CLASS zcl_mjs IMPLEMENTATION. IF lv_rxint = 2 OR lv_rxint = 3. lv_rxicase = abap_true. ENDIF. CLEAR lv_rxout. lv_rxrem = is_obj-str. - IF lv_rxicase = abap_true. - FIND ALL OCCURRENCES OF REGEX ls_rep1-str IN lv_rxrem - RESULTS DATA(lt_rx_res) IGNORING CASE. + DATA lv_rx_pos TYPE i VALUE 0. + DATA lt_rx_res TYPE match_result_tab. + IF strlen( ls_rep1-str ) = 0. + " Empty regex matches empty string between everywhere + DO. + IF lv_rx_pos > strlen( lv_rxrem ). EXIT. ENDIF. + DATA ls_e_res TYPE match_result. + ls_e_res-offset = lv_rx_pos. + ls_e_res-length = 0. + APPEND ls_e_res TO lt_rx_res. + lv_rx_pos = lv_rx_pos + 1. + IF lv_rxglob = abap_false OR lv_rx_pos > strlen( lv_rxrem ). + EXIT. + ENDIF. + ENDDO. ELSE. - FIND ALL OCCURRENCES OF REGEX ls_rep1-str IN lv_rxrem - RESULTS lt_rx_res. - ENDIF. - IF lv_rxglob = abap_false AND lines( lt_rx_res ) > 1. - DELETE lt_rx_res FROM 2. + DO. + IF lv_rx_pos > strlen( lv_rxrem ). EXIT. ENDIF. + DATA ls_rx_res TYPE match_result. + IF lv_rxicase = abap_true. + FIND REGEX ls_rep1-str IN SECTION OFFSET lv_rx_pos OF lv_rxrem + MATCH OFFSET ls_rx_res-offset + MATCH LENGTH ls_rx_res-length + IGNORING CASE. + ELSE. + FIND REGEX ls_rep1-str IN SECTION OFFSET lv_rx_pos OF lv_rxrem + MATCH OFFSET ls_rx_res-offset + MATCH LENGTH ls_rx_res-length. + ENDIF. + IF sy-subrc <> 0. + EXIT. + ENDIF. + IF lv_rx_pos > 0 AND ls_rx_res-offset = lv_rx_pos AND ls_rx_res-length = 0 AND substring( val = ls_rep1-str len = 1 ) = '^'. + lv_rx_pos = lv_rx_pos + 1. + CONTINUE. + ENDIF. + APPEND ls_rx_res TO lt_rx_res. + IF lv_rxglob = abap_false. + EXIT. + ENDIF. + IF ls_rx_res-length = 0. + lv_rx_pos = ls_rx_res-offset + 1. + " Hack for open-abap: ^ matches start of section, which means we might get infinite exact zero-length hits if we keep searching from lv_rx_pos. But wait! We just incremented lv_rx_pos! + ELSE. + lv_rx_pos = ls_rx_res-offset + ls_rx_res-length. + ENDIF. + IF lv_rx_pos > strlen( lv_rxrem ). + EXIT. + ENDIF. + ENDDO. ENDIF. - DATA lv_rx_pos TYPE i. - lv_rx_pos = 0. + DATA lv_rx_pos2 TYPE i. + lv_rx_pos2 = 0. LOOP AT lt_rx_res ASSIGNING FIELD-SYMBOL(). - IF -offset > lv_rx_pos. - lv_rxout = lv_rxout && substring( val = lv_rxrem off = lv_rx_pos len = -offset - lv_rx_pos ). + IF -offset > lv_rx_pos2. + lv_rxout = lv_rxout && substring( val = lv_rxrem off = lv_rx_pos2 len = -offset - lv_rx_pos2 ). ENDIF. lv_rxout = lv_rxout && lv_rep_to. - lv_rx_pos = -offset + -length. + lv_rx_pos2 = -offset + -length. ENDLOOP. - IF lv_rx_pos < strlen( lv_rxrem ). - lv_rxout = lv_rxout && substring( val = lv_rxrem off = lv_rx_pos ). + IF lv_rx_pos2 < strlen( lv_rxrem ). + lv_rxout = lv_rxout && substring( val = lv_rxrem off = lv_rx_pos2 ). ENDIF. CLEAR lv_rxrem. lv_rxout = lv_rxout && lv_rxrem. diff --git a/src/zcl_mjs.clas.testclasses.abap b/src/zcl_mjs.clas.testclasses.abap index 22de31a..4369019 100644 --- a/src/zcl_mjs.clas.testclasses.abap +++ b/src/zcl_mjs.clas.testclasses.abap @@ -106,6 +106,8 @@ CLASS ltcl_test DEFINITION FOR TESTING METHODS test_array_map FOR TESTING RAISING zcx_mjs_runtime. METHODS test_rest_map FOR TESTING RAISING zcx_mjs_runtime. METHODS test_replace_regex_anchor FOR TESTING RAISING zcx_mjs_runtime. + METHODS test_replace_empty_regex FOR TESTING RAISING zcx_mjs_runtime. + METHODS test_replace_empty_regex_g FOR TESTING RAISING zcx_mjs_runtime. METHODS test262 FOR TESTING RAISING zcx_mjs_runtime. @@ -909,6 +911,23 @@ CLASS ltcl_test IMPLEMENTATION. exp = |xabc| ). ENDMETHOD. + METHOD test_replace_empty_regex. + DATA(lv_nl) = cl_abap_char_utilities=>newline. + DATA(lv_js) = + `var str = "abc";` && lv_nl && + `var res = str.replace(new RegExp(""), "-");` && lv_nl && + `console.log(res);`. + cl_abap_unit_assert=>assert_equals( + act = trim( zcl_mjs=>eval( lv_js ) ) + exp = |-abc| ). + ENDMETHOD. + + METHOD test_replace_empty_regex_g. + cl_abap_unit_assert=>assert_equals( + act = trim( zcl_mjs=>eval( `console.log("abc".replace(/(?:)/g, "-"));` ) ) + exp = |-a-b-c-| ). + ENDMETHOD. + METHOD test262. DATA(lv_nl) = cl_abap_char_utilities=>newline. DATA(lv_js) =