diff options
| -rw-r--r-- | lstrlib.c | 130 | ||||
| -rw-r--r-- | testes/gc.lua | 2 | ||||
| -rw-r--r-- | testes/pm.lua | 30 |
3 files changed, 115 insertions, 47 deletions
| @@ -660,25 +660,46 @@ static const char *lmemfind (const char *s1, size_t l1, | |||
| 660 | } | 660 | } |
| 661 | 661 | ||
| 662 | 662 | ||
| 663 | static void push_onecapture (MatchState *ms, int i, const char *s, | 663 | /* |
| 664 | const char *e) { | 664 | ** get information about the i-th capture. If there are no captures |
| 665 | ** and 'i==0', return information about the whole match, which | ||
| 666 | ** is the range 's'..'e'. If the capture is a string, return | ||
| 667 | ** its length and put its address in '*cap'. If it is an integer | ||
| 668 | ** (a position), push it on the stack and return CAP_POSITION. | ||
| 669 | */ | ||
| 670 | static size_t get_onecapture (MatchState *ms, int i, const char *s, | ||
| 671 | const char *e, const char **cap) { | ||
| 665 | if (i >= ms->level) { | 672 | if (i >= ms->level) { |
| 666 | if (i == 0) /* ms->level == 0, too */ | 673 | if (i != 0) |
| 667 | lua_pushlstring(ms->L, s, e - s); /* add whole match */ | ||
| 668 | else | ||
| 669 | luaL_error(ms->L, "invalid capture index %%%d", i + 1); | 674 | luaL_error(ms->L, "invalid capture index %%%d", i + 1); |
| 675 | *cap = s; | ||
| 676 | return e - s; | ||
| 670 | } | 677 | } |
| 671 | else { | 678 | else { |
| 672 | ptrdiff_t l = ms->capture[i].len; | 679 | ptrdiff_t capl = ms->capture[i].len; |
| 673 | if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); | 680 | *cap = ms->capture[i].init; |
| 674 | if (l == CAP_POSITION) | 681 | if (capl == CAP_UNFINISHED) |
| 682 | luaL_error(ms->L, "unfinished capture"); | ||
| 683 | else if (capl == CAP_POSITION) | ||
| 675 | lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); | 684 | lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); |
| 676 | else | 685 | return capl; |
| 677 | lua_pushlstring(ms->L, ms->capture[i].init, l); | ||
| 678 | } | 686 | } |
| 679 | } | 687 | } |
| 680 | 688 | ||
| 681 | 689 | ||
| 690 | /* | ||
| 691 | ** Push the i-th capture on the stack. | ||
| 692 | */ | ||
| 693 | static void push_onecapture (MatchState *ms, int i, const char *s, | ||
| 694 | const char *e) { | ||
| 695 | const char *cap; | ||
| 696 | ptrdiff_t l = get_onecapture(ms, i, s, e, &cap); | ||
| 697 | if (l != CAP_POSITION) | ||
| 698 | lua_pushlstring(ms->L, cap, l); | ||
| 699 | /* else position was already pushed */ | ||
| 700 | } | ||
| 701 | |||
| 702 | |||
| 682 | static int push_captures (MatchState *ms, const char *s, const char *e) { | 703 | static int push_captures (MatchState *ms, const char *s, const char *e) { |
| 683 | int i; | 704 | int i; |
| 684 | int nlevels = (ms->level == 0 && s) ? 1 : ms->level; | 705 | int nlevels = (ms->level == 0 && s) ? 1 : ms->level; |
| @@ -817,60 +838,72 @@ static int gmatch (lua_State *L) { | |||
| 817 | 838 | ||
| 818 | static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, | 839 | static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, |
| 819 | const char *e) { | 840 | const char *e) { |
| 820 | size_t l, i; | 841 | size_t l; |
| 821 | lua_State *L = ms->L; | 842 | lua_State *L = ms->L; |
| 822 | const char *news = lua_tolstring(L, 3, &l); | 843 | const char *news = lua_tolstring(L, 3, &l); |
| 823 | for (i = 0; i < l; i++) { | 844 | const char *p; |
| 824 | if (news[i] != L_ESC) | 845 | while ((p = (char *)memchr(news, L_ESC, l)) != NULL) { |
| 825 | luaL_addchar(b, news[i]); | 846 | luaL_addlstring(b, news, p - news); |
| 826 | else { | 847 | p++; /* skip ESC */ |
| 827 | i++; /* skip ESC */ | 848 | if (*p == L_ESC) /* '%%' */ |
| 828 | if (!isdigit(uchar(news[i]))) { | 849 | luaL_addchar(b, *p); |
| 829 | if (news[i] != L_ESC) | 850 | else if (*p == '0') /* '%0' */ |
| 830 | luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); | 851 | luaL_addlstring(b, s, e - s); |
| 831 | luaL_addchar(b, news[i]); | 852 | else if (isdigit(uchar(*p))) { /* '%n' */ |
| 832 | } | 853 | const char *cap; |
| 833 | else if (news[i] == '0') | 854 | ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap); |
| 834 | luaL_addlstring(b, s, e - s); | 855 | if (resl == CAP_POSITION) |
| 835 | else { | 856 | luaL_addvalue(b); /* add position to accumulated result */ |
| 836 | push_onecapture(ms, news[i] - '1', s, e); | 857 | else |
| 837 | luaL_tolstring(L, -1, NULL); /* if number, convert it to string */ | 858 | luaL_addlstring(b, cap, resl); |
| 838 | lua_remove(L, -2); /* remove original value */ | ||
| 839 | luaL_addvalue(b); /* add capture to accumulated result */ | ||
| 840 | } | ||
| 841 | } | 859 | } |
| 860 | else | ||
| 861 | luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); | ||
| 862 | l -= p + 1 - news; | ||
| 863 | news = p + 1; | ||
| 842 | } | 864 | } |
| 865 | luaL_addlstring(b, news, l); | ||
| 843 | } | 866 | } |
| 844 | 867 | ||
| 845 | 868 | ||
| 846 | static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, | 869 | /* |
| 847 | const char *e, int tr) { | 870 | ** Add the replacement value to the string buffer 'b'. |
| 871 | ** Return true if the original string was changed. (Function calls and | ||
| 872 | ** table indexing resulting in nil or false do not change the subject.) | ||
| 873 | */ | ||
| 874 | static int add_value (MatchState *ms, luaL_Buffer *b, const char *s, | ||
| 875 | const char *e, int tr) { | ||
| 848 | lua_State *L = ms->L; | 876 | lua_State *L = ms->L; |
| 849 | switch (tr) { | 877 | switch (tr) { |
| 850 | case LUA_TFUNCTION: { | 878 | case LUA_TFUNCTION: { /* call the function */ |
| 851 | int n; | 879 | int n; |
| 852 | lua_pushvalue(L, 3); | 880 | lua_pushvalue(L, 3); /* push the function */ |
| 853 | n = push_captures(ms, s, e); | 881 | n = push_captures(ms, s, e); /* all captures as arguments */ |
| 854 | lua_call(L, n, 1); | 882 | lua_call(L, n, 1); /* call it */ |
| 855 | break; | 883 | break; |
| 856 | } | 884 | } |
| 857 | case LUA_TTABLE: { | 885 | case LUA_TTABLE: { /* index the table */ |
| 858 | push_onecapture(ms, 0, s, e); | 886 | push_onecapture(ms, 0, s, e); /* first capture is the index */ |
| 859 | lua_gettable(L, 3); | 887 | lua_gettable(L, 3); |
| 860 | break; | 888 | break; |
| 861 | } | 889 | } |
| 862 | default: { /* LUA_TNUMBER or LUA_TSTRING */ | 890 | default: { /* LUA_TNUMBER or LUA_TSTRING */ |
| 863 | add_s(ms, b, s, e); | 891 | add_s(ms, b, s, e); /* add value to the buffer */ |
| 864 | return; | 892 | return 1; /* something changed */ |
| 865 | } | 893 | } |
| 866 | } | 894 | } |
| 867 | if (!lua_toboolean(L, -1)) { /* nil or false? */ | 895 | if (!lua_toboolean(L, -1)) { /* nil or false? */ |
| 868 | lua_pop(L, 1); | 896 | lua_pop(L, 1); /* remove value */ |
| 869 | lua_pushlstring(L, s, e - s); /* keep original text */ | 897 | luaL_addlstring(b, s, e - s); /* keep original text */ |
| 898 | return 0; /* no changes */ | ||
| 870 | } | 899 | } |
| 871 | else if (!lua_isstring(L, -1)) | 900 | else if (!lua_isstring(L, -1)) |
| 872 | luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); | 901 | return luaL_error(L, "invalid replacement value (a %s)", |
| 873 | luaL_addvalue(b); /* add result to accumulator */ | 902 | luaL_typename(L, -1)); |
| 903 | else { | ||
| 904 | luaL_addvalue(b); /* add result to accumulator */ | ||
| 905 | return 1; /* something changed */ | ||
| 906 | } | ||
| 874 | } | 907 | } |
| 875 | 908 | ||
| 876 | 909 | ||
| @@ -883,6 +916,7 @@ static int str_gsub (lua_State *L) { | |||
| 883 | lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */ | 916 | lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */ |
| 884 | int anchor = (*p == '^'); | 917 | int anchor = (*p == '^'); |
| 885 | lua_Integer n = 0; /* replacement count */ | 918 | lua_Integer n = 0; /* replacement count */ |
| 919 | int changed = 0; /* change flag */ | ||
| 886 | MatchState ms; | 920 | MatchState ms; |
| 887 | luaL_Buffer b; | 921 | luaL_Buffer b; |
| 888 | luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || | 922 | luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || |
| @@ -898,7 +932,7 @@ static int str_gsub (lua_State *L) { | |||
| 898 | reprepstate(&ms); /* (re)prepare state for new match */ | 932 | reprepstate(&ms); /* (re)prepare state for new match */ |
| 899 | if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ | 933 | if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ |
| 900 | n++; | 934 | n++; |
| 901 | add_value(&ms, &b, src, e, tr); /* add replacement to buffer */ | 935 | changed = add_value(&ms, &b, src, e, tr) | changed; |
| 902 | src = lastmatch = e; | 936 | src = lastmatch = e; |
| 903 | } | 937 | } |
| 904 | else if (src < ms.src_end) /* otherwise, skip one character */ | 938 | else if (src < ms.src_end) /* otherwise, skip one character */ |
| @@ -906,8 +940,12 @@ static int str_gsub (lua_State *L) { | |||
| 906 | else break; /* end of subject */ | 940 | else break; /* end of subject */ |
| 907 | if (anchor) break; | 941 | if (anchor) break; |
| 908 | } | 942 | } |
| 909 | luaL_addlstring(&b, src, ms.src_end-src); | 943 | if (!changed) /* no changes? */ |
| 910 | luaL_pushresult(&b); | 944 | lua_pushvalue(L, 1); /* return original string */ |
| 945 | else { /* something changed */ | ||
| 946 | luaL_addlstring(&b, src, ms.src_end-src); | ||
| 947 | luaL_pushresult(&b); /* create and return new string */ | ||
| 948 | } | ||
| 911 | lua_pushinteger(L, n); /* number of substitutions */ | 949 | lua_pushinteger(L, n); /* number of substitutions */ |
| 912 | return 2; | 950 | return 2; |
| 913 | } | 951 | } |
diff --git a/testes/gc.lua b/testes/gc.lua index 91e78a48..6d24e0d8 100644 --- a/testes/gc.lua +++ b/testes/gc.lua | |||
| @@ -113,7 +113,7 @@ do | |||
| 113 | contCreate = 0 | 113 | contCreate = 0 |
| 114 | while contCreate <= limit do | 114 | while contCreate <= limit do |
| 115 | a = contCreate .. "b"; | 115 | a = contCreate .. "b"; |
| 116 | a = string.gsub(a, '(%d%d*)', string.upper) | 116 | a = string.gsub(a, '(%d%d*)', "%1 %1") |
| 117 | a = "a" | 117 | a = "a" |
| 118 | contCreate = contCreate+1 | 118 | contCreate = contCreate+1 |
| 119 | end | 119 | end |
diff --git a/testes/pm.lua b/testes/pm.lua index 8cc8772e..4d87fad2 100644 --- a/testes/pm.lua +++ b/testes/pm.lua | |||
| @@ -387,5 +387,35 @@ assert(string.match("abc\0\0\0", "%\0%\0?") == "\0\0") | |||
| 387 | assert(string.find("abc\0\0","\0.") == 4) | 387 | assert(string.find("abc\0\0","\0.") == 4) |
| 388 | assert(string.find("abcx\0\0abc\0abc","x\0\0abc\0a.") == 4) | 388 | assert(string.find("abcx\0\0abc\0abc","x\0\0abc\0a.") == 4) |
| 389 | 389 | ||
| 390 | |||
| 391 | do -- test reuse of original string in gsub | ||
| 392 | local s = string.rep("a", 100) | ||
| 393 | local r = string.gsub(s, "b", "c") -- no match | ||
| 394 | assert(string.format("%p", s) == string.format("%p", r)) | ||
| 395 | |||
| 396 | r = string.gsub(s, ".", {x = "y"}) -- no substitutions | ||
| 397 | assert(string.format("%p", s) == string.format("%p", r)) | ||
| 398 | |||
| 399 | local count = 0 | ||
| 400 | r = string.gsub(s, ".", function (x) | ||
| 401 | assert(x == "a") | ||
| 402 | count = count + 1 | ||
| 403 | return nil -- no substitution | ||
| 404 | end) | ||
| 405 | r = string.gsub(r, ".", {b = 'x'}) -- "a" is not a key; no subst. | ||
| 406 | assert(count == 100) | ||
| 407 | assert(string.format("%p", s) == string.format("%p", r)) | ||
| 408 | |||
| 409 | count = 0 | ||
| 410 | r = string.gsub(s, ".", function (x) | ||
| 411 | assert(x == "a") | ||
| 412 | count = count + 1 | ||
| 413 | return x -- substitution... | ||
| 414 | end) | ||
| 415 | assert(count == 100) | ||
| 416 | -- no reuse in this case | ||
| 417 | assert(r == s and string.format("%p", s) ~= string.format("%p", r)) | ||
| 418 | end | ||
| 419 | |||
| 390 | print('OK') | 420 | print('OK') |
| 391 | 421 | ||
