aboutsummaryrefslogtreecommitdiff
path: root/test.lua
diff options
context:
space:
mode:
Diffstat (limited to 'test.lua')
-rwxr-xr-xtest.lua142
1 files changed, 92 insertions, 50 deletions
diff --git a/test.lua b/test.lua
index d486c03..bbb3ccb 100755
--- a/test.lua
+++ b/test.lua
@@ -1,6 +1,6 @@
1#!/usr/bin/env lua5.1 1#!/usr/bin/env lua5.1
2 2
3-- $Id: test.lua,v 1.101 2013/04/12 16:30:33 roberto Exp $ 3-- $Id: test.lua,v 1.106 2015/03/04 17:31:33 roberto Exp $
4 4
5-- require"strict" -- just to be pedantic 5-- require"strict" -- just to be pedantic
6 6
@@ -170,8 +170,8 @@ assert(m.match( basiclookfor((#m.P(b) * 1) * m.Cp()), " ( (a)") == 7)
170a = {m.match(m.C(digit^1 * m.Cc"d") + m.C(letter^1 * m.Cc"l"), "123")} 170a = {m.match(m.C(digit^1 * m.Cc"d") + m.C(letter^1 * m.Cc"l"), "123")}
171checkeq(a, {"123", "d"}) 171checkeq(a, {"123", "d"})
172 172
173a = {m.match(m.C(digit^1) * "d" * -1 + m.C(letter^1 * m.Cc"l"), "123d")} 173-- bug in LPeg 0.12 (nil value does not create a 'ktable')
174checkeq(a, {"123"}) 174assert(m.match(m.Cc(nil), "") == nil)
175 175
176a = {m.match(m.C(digit^1 * m.Cc"d") + m.C(letter^1 * m.Cc"l"), "abcd")} 176a = {m.match(m.C(digit^1 * m.Cc"d") + m.C(letter^1 * m.Cc"l"), "abcd")}
177checkeq(a, {"abcd", "l"}) 177checkeq(a, {"abcd", "l"})
@@ -194,6 +194,16 @@ checkeq(a, {1, 5})
194t = {m.match({[1] = m.C(m.C(1) * m.V(1) + -1)}, "abc")} 194t = {m.match({[1] = m.C(m.C(1) * m.V(1) + -1)}, "abc")}
195checkeq(t, {"abc", "a", "bc", "b", "c", "c", ""}) 195checkeq(t, {"abc", "a", "bc", "b", "c", "c", ""})
196 196
197-- bug in 0.12 ('hascapture' did not check for captures inside a rule)
198do
199 local pat = m.P{
200 'S';
201 S1 = m.C('abc') + 3,
202 S = #m.V('S1') -- rule has capture, but '#' must ignore it
203 }
204 assert(pat:match'abc' == 1)
205end
206
197 207
198-- test for small capture boundary 208-- test for small capture boundary
199for i = 250,260 do 209for i = 250,260 do
@@ -201,9 +211,8 @@ for i = 250,260 do
201 assert(#m.match(m.C(m.C(i)), string.rep('a', i)) == i) 211 assert(#m.match(m.C(m.C(i)), string.rep('a', i)) == i)
202end 212end
203 213
204
205-- tests for any*n and any*-n 214-- tests for any*n and any*-n
206for n = 1, 550 do 215for n = 1, 550, 13 do
207 local x_1 = string.rep('x', n - 1) 216 local x_1 = string.rep('x', n - 1)
208 local x = x_1 .. 'a' 217 local x = x_1 .. 'a'
209 assert(not m.P(n):match(x_1)) 218 assert(not m.P(n):match(x_1))
@@ -345,8 +354,9 @@ checkeq(t, {hi = 10, ho = 20, 'a', 'b', 'c'})
345 354
346 355
347-- test for error messages 356-- test for error messages
348local function checkerr (msg, ...) 357local function checkerr (msg, f, ...)
349 assert(m.match({ m.P(msg) + 1 * m.V(1) }, select(2, pcall(...)))) 358 local st, err = pcall(f, ...)
359 assert(not st and m.match({ m.P(msg) + 1 * m.V(1) }, err))
350end 360end
351 361
352checkerr("rule '1' may be left recursive", m.match, { m.V(1) * 'a' }, "a") 362checkerr("rule '1' may be left recursive", m.match, { m.V(1) * 'a' }, "a")
@@ -370,6 +380,32 @@ p = {'a',
370} 380}
371checkerr("rule 'a' may be left recursive", m.match, p, "a") 381checkerr("rule 'a' may be left recursive", m.match, p, "a")
372 382
383-- Bug in peephole optimization of LPeg 0.12 (IJmp -> ICommit)
384-- the next grammar has an original sequence IJmp -> ICommit -> IJmp L1
385-- that is optimized to ICommit L1
386
387p = m.P { (m.P {m.P'abc'} + 'ayz') * m.V'y'; y = m.P'x' }
388assert(p:match('abcx') == 5 and p:match('ayzx') == 5 and not p:match'abc')
389
390
391do
392 -- large dynamic Cc
393 local lim = 2^16 - 1
394 local c = 0
395 local function seq (n)
396 if n == 1 then c = c + 1; return m.Cc(c)
397 else
398 local m = math.floor(n / 2)
399 return seq(m) * seq(n - m)
400 end
401 end
402 p = m.Ct(seq(lim))
403 t = p:match('')
404 assert(t[lim] == lim)
405 checkerr("too many", function () p = p / print end)
406 checkerr("too many", seq, lim + 1)
407end
408
373 409
374-- tests for non-pattern as arguments to pattern functions 410-- tests for non-pattern as arguments to pattern functions
375 411
@@ -488,7 +524,10 @@ assert(m.match(1 * m.B(1), 'a') == 2)
488assert(m.match(-m.B(1), 'a') == 1) 524assert(m.match(-m.B(1), 'a') == 1)
489assert(m.match(m.B(250), string.rep('a', 250)) == nil) 525assert(m.match(m.B(250), string.rep('a', 250)) == nil)
490assert(m.match(250 * m.B(250), string.rep('a', 250)) == 251) 526assert(m.match(250 * m.B(250), string.rep('a', 250)) == 251)
491assert(not pcall(m.B, 260)) 527
528-- look-behind with an open call
529checkerr("pattern may not have fixed length", m.B, m.V'S1')
530checkerr("too long to look behind", m.B, 260)
492 531
493B = #letter * -m.B(letter) + -letter * m.B(letter) 532B = #letter * -m.B(letter) + -letter * m.B(letter)
494x = m.Ct({ (B * m.Cp())^-1 * (1 * m.V(1) + m.P(true)) }) 533x = m.Ct({ (B * m.Cp())^-1 * (1 * m.V(1) + m.P(true)) })
@@ -523,7 +562,6 @@ assert(m.match(#m.S'567' * 1, "6") == 2)
523 562
524-- tests for Tail Calls 563-- tests for Tail Calls
525 564
526--labeled failure
527p = m.P{ 'a' * m.V(1) + '' } 565p = m.P{ 'a' * m.V(1) + '' }
528assert(p:match(string.rep('a', 1000)) == 1001) 566assert(p:match(string.rep('a', 1000)) == 1001)
529 567
@@ -546,7 +584,6 @@ p = m.P{
546 [4] = '0' * m.V(3) + '1' * m.V(2), 584 [4] = '0' * m.V(3) + '1' * m.V(2),
547} 585}
548 586
549-- labeled failure
550assert(p:match(string.rep("00", 10000))) 587assert(p:match(string.rep("00", 10000)))
551assert(p:match(string.rep("01", 10000))) 588assert(p:match(string.rep("01", 10000)))
552assert(p:match(string.rep("011", 10000))) 589assert(p:match(string.rep("011", 10000)))
@@ -557,16 +594,15 @@ assert(not p:match(string.rep("011", 10001)))
557-- this grammar does need backtracking info. 594-- this grammar does need backtracking info.
558local lim = 10000 595local lim = 10000
559p = m.P{ '0' * m.V(1) + '0' } 596p = m.P{ '0' * m.V(1) + '0' }
560assert(not pcall(m.match, p, string.rep("0", lim))) 597checkerr("too many pending", m.match, p, string.rep("0", lim))
561m.setmaxstack(2*lim) 598m.setmaxstack(2*lim)
562assert(not pcall(m.match, p, string.rep("0", lim))) 599checkerr("too many pending", m.match, p, string.rep("0", lim))
563m.setmaxstack(2*lim + 4) 600m.setmaxstack(2*lim + 4)
564assert(pcall(m.match, p, string.rep("0", lim))) 601assert(m.match(p, string.rep("0", lim)) == lim + 1)
565 602
566-- this repetition should not need stack space (only the call does) 603-- this repetition should not need stack space (only the call does)
567p = m.P{ ('a' * m.V(1))^0 * 'b' + 'c' } 604p = m.P{ ('a' * m.V(1))^0 * 'b' + 'c' }
568m.setmaxstack(200) 605m.setmaxstack(200)
569-- labeled failure
570assert(p:match(string.rep('a', 180) .. 'c' .. string.rep('b', 180)) == 362) 606assert(p:match(string.rep('a', 180) .. 'c' .. string.rep('b', 180)) == 362)
571 607
572m.setmaxstack(5) -- restore original limit 608m.setmaxstack(5) -- restore original limit
@@ -591,10 +627,10 @@ print("+")
591 627
592 628
593-- tests for argument captures 629-- tests for argument captures
594assert(not pcall(m.Carg, 0)) 630checkerr("invalid argument", m.Carg, 0)
595assert(not pcall(m.Carg, -1)) 631checkerr("invalid argument", m.Carg, -1)
596assert(not pcall(m.Carg, 2^18)) 632checkerr("invalid argument", m.Carg, 2^18)
597assert(not pcall(m.match, m.Carg(1), 'a', 1)) 633checkerr("absent extra argument #1", m.match, m.Carg(1), 'a', 1)
598assert(m.match(m.Carg(1), 'a', 1, print) == print) 634assert(m.match(m.Carg(1), 'a', 1, print) == print)
599x = {m.match(m.Carg(1) * m.Carg(2), '', 1, 10, 20)} 635x = {m.match(m.Carg(1) * m.Carg(2), '', 1, 10, 20)}
600checkeq(x, {10, 20}) 636checkeq(x, {10, 20})
@@ -647,14 +683,16 @@ assert(m.match(p, "aaaa") == 5)
647assert(m.match(p, "abaa") == 2) 683assert(m.match(p, "abaa") == 2)
648assert(not m.match(p, "baaa")) 684assert(not m.match(p, "baaa"))
649 685
650assert(not pcall(m.match, function () return 2^20 end, s)) 686checkerr("invalid position", m.match, function () return 2^20 end, s)
651assert(not pcall(m.match, function () return 0 end, s)) 687checkerr("invalid position", m.match, function () return 0 end, s)
652assert(not pcall(m.match, function (s, i) return i - 1 end, s)) 688checkerr("invalid position", m.match, function (s, i) return i - 1 end, s)
653assert(not pcall(m.match, m.P(1)^0 * function (_, i) return i - 1 end, s)) 689checkerr("invalid position", m.match,
690 m.P(1)^0 * function (_, i) return i - 1 end, s)
654assert(m.match(m.P(1)^0 * function (_, i) return i end * -1, s)) 691assert(m.match(m.P(1)^0 * function (_, i) return i end * -1, s))
655assert(not pcall(m.match, m.P(1)^0 * function (_, i) return i + 1 end, s)) 692checkerr("invalid position", m.match,
693 m.P(1)^0 * function (_, i) return i + 1 end, s)
656assert(m.match(m.P(function (s, i) return s:len() + 1 end) * -1, s)) 694assert(m.match(m.P(function (s, i) return s:len() + 1 end) * -1, s))
657assert(not pcall(m.match, m.P(function (s, i) return s:len() + 2 end) * -1, s)) 695checkerr("invalid position", m.match, m.P(function (s, i) return s:len() + 2 end) * -1, s)
658assert(not m.match(m.P(function (s, i) return s:len() end) * -1, s)) 696assert(not m.match(m.P(function (s, i) return s:len() end) * -1, s))
659assert(m.match(m.P(1)^0 * function (_, i) return true end, s) == 697assert(m.match(m.P(1)^0 * function (_, i) return true end, s) ==
660 string.len(s) + 1) 698 string.len(s) + 1)
@@ -737,9 +775,9 @@ assert(m.match(m.Cs((m.P(1) / ".xx")^0), "abcd") == ".xx.xx.xx.xx")
737assert(m.match(m.Cp() * m.P(3) * m.Cp()/"%2%1%1 - %0 ", "abcde") == 775assert(m.match(m.Cp() * m.P(3) * m.Cp()/"%2%1%1 - %0 ", "abcde") ==
738 "411 - abc ") 776 "411 - abc ")
739 777
740assert(pcall(m.match, m.P(1)/"%0", "abc")) 778assert(m.match(m.P(1)/"%0", "abc") == "a")
741assert(not pcall(m.match, m.P(1)/"%1", "abc")) -- out of range 779checkerr("invalid capture index", m.match, m.P(1)/"%1", "abc")
742assert(not pcall(m.match, m.P(1)/"%9", "abc")) -- out of range 780checkerr("invalid capture index", m.match, m.P(1)/"%9", "abc")
743 781
744p = m.C(1) 782p = m.C(1)
745p = p * p; p = p * p; p = p * p * m.C(1) / "%9 - %1" 783p = p * p; p = p * p; p = p * p * m.C(1) / "%9 - %1"
@@ -757,7 +795,7 @@ assert(m.match(m.C(1)^0 / "%9-%1-%0-%3", s) == "9-1-" .. s .. "-3")
757p = m.Cc('alo') * m.C(1) / "%1 - %2 - %1" 795p = m.Cc('alo') * m.C(1) / "%1 - %2 - %1"
758assert(p:match'x' == 'alo - x - alo') 796assert(p:match'x' == 'alo - x - alo')
759 797
760assert(not pcall(m.match, m.Cc(true) / "%1", "a")) 798checkerr("invalid capture value (a boolean)", m.match, m.Cc(true) / "%1", "a")
761 799
762-- long strings for string capture 800-- long strings for string capture
763l = 10000 801l = 10000
@@ -785,35 +823,37 @@ checkeq(t, {a="b", c="du", xux="yuy"})
785 823
786-- errors in accumulator capture 824-- errors in accumulator capture
787 825
788-- very long match (forces fold to be a pair open-close) producing with
789-- no initial capture 826-- no initial capture
790assert(not pcall(m.match, m.Cf(m.P(500), print), string.rep('a', 600))) 827checkerr("no initial value", m.match, m.Cf(m.P(5), print), 'aaaaaa')
828-- no initial capture (very long match forces fold to be a pair open-close)
829checkerr("no initial value", m.match, m.Cf(m.P(500), print),
830 string.rep('a', 600))
791 831
792-- nested capture produces no initial value 832-- nested capture produces no initial value
793assert(not pcall(m.match, m.Cf(m.P(1) / {}, print), "alo")) 833checkerr("no initial value", m.match, m.Cf(m.P(1) / {}, print), "alo")
794 834
795 835
796-- tests for loop checker 836-- tests for loop checker
797 837
798local function haveloop (p) 838local function isnullable (p)
799 assert(not pcall(function (p) return p^0 end, m.P(p))) 839 checkerr("may accept empty string", function (p) return p^0 end, m.P(p))
800end 840end
801 841
802haveloop(m.P("x")^-4) 842isnullable(m.P("x")^-4)
803assert(m.match(((m.P(0) + 1) * m.S"al")^0, "alo") == 3) 843assert(m.match(((m.P(0) + 1) * m.S"al")^0, "alo") == 3)
804assert(m.match((("x" + #m.P(1))^-4 * m.S"al")^0, "alo") == 3) 844assert(m.match((("x" + #m.P(1))^-4 * m.S"al")^0, "alo") == 3)
805haveloop("") 845isnullable("")
806haveloop(m.P("x")^0) 846isnullable(m.P("x")^0)
807haveloop(m.P("x")^-1) 847isnullable(m.P("x")^-1)
808haveloop(m.P("x") + 1 + 2 + m.P("a")^-1) 848isnullable(m.P("x") + 1 + 2 + m.P("a")^-1)
809haveloop(-m.P("ab")) 849isnullable(-m.P("ab"))
810haveloop(- -m.P("ab")) 850isnullable(- -m.P("ab"))
811haveloop(# #(m.P("ab") + "xy")) 851isnullable(# #(m.P("ab") + "xy"))
812haveloop(- #m.P("ab")^0) 852isnullable(- #m.P("ab")^0)
813haveloop(# -m.P("ab")^1) 853isnullable(# -m.P("ab")^1)
814haveloop(#m.V(3)) 854isnullable(#m.V(3))
815haveloop(m.V(3) + m.V(1) + m.P('a')^-1) 855isnullable(m.V(3) + m.V(1) + m.P('a')^-1)
816haveloop({[1] = m.V(2) * m.V(3), [2] = m.V(3), [3] = m.P(0)}) 856isnullable({[1] = m.V(2) * m.V(3), [2] = m.V(3), [3] = m.P(0)})
817assert(m.match(m.P{[1] = m.V(2) * m.V(3), [2] = m.V(3), [3] = m.P(1)}^0, "abc") 857assert(m.match(m.P{[1] = m.V(2) * m.V(3), [2] = m.V(3), [3] = m.P(1)}^0, "abc")
818 == 3) 858 == 3)
819assert(m.match(m.P""^-3, "a") == 1) 859assert(m.match(m.P""^-3, "a") == 1)
@@ -897,8 +937,8 @@ print"+"
897 937
898 938
899-- tests for back references 939-- tests for back references
900assert(not pcall(m.match, m.Cb('x'), '')) 940checkerr("back reference 'x' not found", m.match, m.Cb('x'), '')
901assert(not pcall(m.match, m.Cg(1, 'a') * m.Cb('b'), 'a')) 941checkerr("back reference 'b' not found", m.match, m.Cg(1, 'a') * m.Cb('b'), 'a')
902 942
903p = m.Cg(m.C(1) * m.C(1), "k") * m.Ct(m.Cb("k")) 943p = m.Cg(m.C(1) * m.C(1), "k") * m.Ct(m.Cb("k"))
904t = p:match("ab") 944t = p:match("ab")
@@ -1054,6 +1094,8 @@ local re = require "re"
1054 1094
1055local match, compile = re.match, re.compile 1095local match, compile = re.match, re.compile
1056 1096
1097
1098
1057assert(match("a", ".") == 2) 1099assert(match("a", ".") == 2)
1058assert(match("a", "''") == 1) 1100assert(match("a", "''") == 1)
1059assert(match("", " ! . ") == 1) 1101assert(match("", " ! . ") == 1)
@@ -1348,6 +1390,7 @@ eqlpeggsub("[%W%S]", "%W%S")
1348 1390
1349re.updatelocale() 1391re.updatelocale()
1350 1392
1393
1351-- testing nested substitutions x string captures 1394-- testing nested substitutions x string captures
1352 1395
1353p = re.compile[[ 1396p = re.compile[[
@@ -1370,8 +1413,7 @@ assert(rev:match"0123456789" == "9876543210")
1370-- testing error messages in re 1413-- testing error messages in re
1371 1414
1372local function errmsg (p, err) 1415local function errmsg (p, err)
1373 local s, msg = pcall(re.compile, p) 1416 checkerr(err, re.compile, p)
1374 assert(not s and string.find(msg, err))
1375end 1417end
1376 1418
1377errmsg('aaaa', "rule 'aaaa'") 1419errmsg('aaaa', "rule 'aaaa'")