├── CHANGES.txt ├── COPYRIGHT.txt ├── MANIFEST ├── README.txt ├── luabalanced.lua ├── rockspec.in ├── test ├── example.lua ├── test.lua └── tuple.lua └── util.mk /CHANGES.txt: -------------------------------------------------------------------------------- 1 | 0.1.2.20130418 2 | Fix: `--"..."` and `--'...'` are not delimited comments 3 | like `--[[...]]` but rather terminate at the end of the line. 4 | 5 | 0.1.1.20120323 6 | Fix performance of LB.gsub on large strings. 7 | 8 | 0.1.20111203 9 | Import older code to git repo. 10 | -------------------------------------------------------------------------------- /COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | lua-balanced License 2 | 3 | =============================================================================== 4 | 5 | Copyright (C) 2008, David Manura. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | =============================================================================== 26 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | README.txt 2 | MANIFEST 3 | util.mk 4 | CHANGES.txt 5 | COPYRIGHT.txt 6 | luabalanced.lua 7 | rockspec.in 8 | test/test.lua 9 | test/example.lua 10 | test/tuple.lua 11 | 12 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | == Description == 2 | 3 | This module can, for example, match a Lua string, Lua comment, or Lua 4 | expression. It is useful in particular for source filters or parsing 5 | Lua snippets embedded in another language. It is inspired by Damian 6 | Conway's Text::Balanced [2] in Perl. The unique feature of this 7 | implementation is that that it does not rigorously lex and parse the 8 | Lua grammar. It doesn't need to. It assumes during the parse that the 9 | Lua code is syntactically correct (which can be verified later using 10 | loadstring). By assuming this, extraction of delimited sequences is 11 | significantly simplified yet can still be robust, and it also supports 12 | supersets of the Lua grammar. The code, which is written entirely in 13 | Lua, is just under 200 lines of Lua code (compare to Yueliang used in 14 | MetaLua, where the lexer alone is a few hundred lines). 15 | 16 | == Project Page == 17 | 18 | Home page: http://lua-users.org/wiki/LuaBalanced 19 | 20 | == Author == 21 | 22 | (c) 2008-2011, David Manura, Licensed under the same terms as Lua (MIT license). 23 | 24 | == References == 25 | 26 | [1] http://lua-users.org/wiki/LuaBalanced 27 | [2] http://search.cpan.org/dist/Text-Balanced/lib/Text/Balanced.pm 28 | -------------------------------------------------------------------------------- /luabalanced.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | 3 | LUA MODULE 4 | 5 | luabalanced v$(_VERSION) - Functions for matching delimited snippets of Lua code in a string 6 | 7 | SYNOPSIS 8 | 9 | local LB = require "luabalanced" 10 | -- Extract Lua expression starting at position 4. 11 | print(LB.match_expression("if x^2 + x > 5 then print(x) end", 4)) 12 | --> x^2 + x > 5 16 13 | -- Extract Lua string starting at (default) position 1. 14 | print(LB.match_string([["test\"123" .. "more"]])) 15 | --> "test\"123" 12 16 | -- Break Lua code into code types. 17 | LB.gsub([[ 18 | local x = 1 -- test 19 | print("x=", x) 20 | ]], function(u, s) 21 | print(u .. '[' .. s .. ']') 22 | end) 23 | --[[output: 24 | e[ local x = 1 ] 25 | c[-- test 26 | ] 27 | e[ print(] 28 | s["x="] 29 | e[, x) 30 | ] 31 | ]] 32 | 33 | DESCRIPTION 34 | 35 | This module can, for example, match a Lua string, Lua comment, or Lua 36 | expression. It is useful in particular for source filters or parsing 37 | Lua snippets embedded in another language. It is inspired by Damian Conway's 38 | Text::Balanced [1] in Perl. The unique feature of this implementation 39 | is that that it does not rigorously lex and parse the Lua grammar. 40 | It doesn't need to. It assumes during the parse that the Lua code is 41 | syntactically correct (which can be verified later using loadstring). 42 | By assuming this, extraction of delimited sequences is significantly 43 | simplified yet can still be robust, and it also supports supersets 44 | of the Lua grammar. The code, which is written entirely in Lua, 45 | is just under 200 lines of Lua code (compare to Yueliang used in 46 | MetaLua, where the lexer alone is a few hundred lines). 47 | 48 | API 49 | 50 | LB.match_string(s, pos) --> string, posnew 51 | 52 | Match Lua string in string starting at position `pos`. 53 | Returns `string`, `posnew`, where `string` is the matched 54 | string (or nil on no match) and `posnew` is the character 55 | following the match (or `pos` on no match). 56 | Supports all Lua string syntax: "...", '...', [[...]], [=[...]=], etc. 57 | 58 | LB.match_bracketed(s, pos) --> string, posnew 59 | 60 | Match bracketed Lua expression, e.g. "(...)", "{...}", "[...]", "[[...]]", 61 | [=[...]=], etc. 62 | Function interface is similar to `match_string`. 63 | 64 | LB.match_comment(s, pos) --> string, posnew 65 | 66 | Match Lua comment, e.g. "--...\n", "--[[...]]", "--[=[...]=]", etc. 67 | Function interface is similar to `match_string`. 68 | 69 | LB.match_expression(s, pos) --> string, posnew 70 | 71 | Match Lua expression, e.g. "a + b * c[e]". 72 | Function interface is similar to match_string. 73 | 74 | LB.match_namelist(s, pos) --> array, posnew 75 | 76 | Match name list (zero or more names). E.g. "a,b,c" 77 | Function interface is similar to match_string, 78 | but returns array as match. 79 | 80 | M.match_explist(s, pos) --> array, posnew 81 | 82 | Match expression list (zero or more expressions). E.g. "a+b,b*c". 83 | Function interface is similar to match_string, 84 | but returns array as match. 85 | 86 | M.gsub(s, f) 87 | 88 | Replace snippets of code in Lua code string `s` 89 | using replacement function `f(u,sin) --> sout`. 90 | `u` is the type of snippet ('c' = comment, 's' = string, 91 | 'e' = any other code). 92 | Snippet is replaced with `sout` (unless `sout` is `nil` or `false`, in 93 | which case the original snippet is kept) 94 | This is somewhat analogous to `string.gsub`. 95 | 96 | DEPENDENCIES 97 | 98 | None (other than Lua 5.1 or 5.2). 99 | 100 | HOME PAGE 101 | 102 | http://lua-users.org/wiki/LuaBalanced 103 | https://github.com/davidm/lua-balanced 104 | 105 | DOWNLOAD/INSTALL 106 | 107 | If using LuaRocks: 108 | luarocks install lua-balanced 109 | 110 | Otherwise, download 111 | and unzip. Alternately, if using git: 112 | git clone git://github.com/davidm/lua-balanced.git 113 | cd lua-balanced 114 | Optionally unpack: 115 | ./util.mk 116 | or unpack and install in LuaRocks: 117 | ./util.mk install 118 | 119 | REFERENCES 120 | 121 | [1] http://lua-users.org/wiki/LuaBalanced 122 | [2] http://search.cpan.org/dist/Text-Balanced/lib/Text/Balanced.pm 123 | 124 | LICENSE 125 | 126 | (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT). 127 | 128 | Permission is hereby granted, free of charge, to any person obtaining a copy 129 | of this software and associated documentation files (the "Software"), to deal 130 | in the Software without restriction, including without limitation the rights 131 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 132 | copies of the Software, and to permit persons to whom the Software is 133 | furnished to do so, subject to the following conditions: 134 | 135 | The above copyright notice and this permission notice shall be included in 136 | all copies or substantial portions of the Software. 137 | 138 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 139 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 140 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 141 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 142 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 143 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 144 | THE SOFTWARE. 145 | (end license) 146 | 147 | --]==]--------------------------------------------------------------------- 148 | 149 | local M = {_TYPE='module', _NAME='luabalanaced', _VERSION='0.1.2.20130418'} 150 | 151 | local assert = assert 152 | 153 | -- map opening brace <-> closing brace. 154 | local ends = { ['('] = ')', ['{'] = '}', ['['] = ']' } 155 | local begins = {}; for k,v in pairs(ends) do begins[v] = k end 156 | 157 | 158 | local function match_string(s, pos) 159 | pos = pos or 1 160 | local posa = pos 161 | local c = s:sub(pos,pos) 162 | if c == '"' or c == "'" then 163 | pos = pos + 1 164 | while 1 do 165 | pos = assert(s:find("[" .. c .. "\\]", pos), 'syntax error') 166 | if s:sub(pos,pos) == c then 167 | local part = s:sub(posa, pos) 168 | return part, pos + 1 169 | else 170 | pos = pos + 2 171 | end 172 | end 173 | else 174 | local sc = s:match("^%[(=*)%[", pos) 175 | if sc then 176 | local _; _, pos = s:find("%]" .. sc .. "%]", pos) 177 | assert(pos) 178 | local part = s:sub(posa, pos) 179 | return part, pos + 1 180 | else 181 | return nil, pos 182 | end 183 | end 184 | end 185 | M.match_string = match_string 186 | 187 | 188 | local function match_bracketed(s, pos) 189 | pos = pos or 1 190 | local posa = pos 191 | local ca = s:sub(pos,pos) 192 | if not ends[ca] then 193 | return nil, pos 194 | end 195 | local stack = {} 196 | while 1 do 197 | pos = s:find('[%(%{%[%)%}%]\"\']', pos) 198 | assert(pos, 'syntax error: unbalanced') 199 | local c = s:sub(pos,pos) 200 | if c == '"' or c == "'" then 201 | local part; part, pos = match_string(s, pos) 202 | assert(part) 203 | elseif ends[c] then -- open 204 | local mid, posb 205 | if c == '[' then mid, posb = s:match('^%[(=*)%[()', pos) end 206 | if mid then 207 | pos = s:match('%]' .. mid .. '%]()', posb) 208 | assert(pos, 'syntax error: long string not terminated') 209 | if #stack == 0 then 210 | local part = s:sub(posa, pos-1) 211 | return part, pos 212 | end 213 | else 214 | stack[#stack+1] = c 215 | pos = pos + 1 216 | end 217 | else -- close 218 | assert(stack[#stack] == assert(begins[c]), 'syntax error: unbalanced') 219 | stack[#stack] = nil 220 | if #stack == 0 then 221 | local part = s:sub(posa, pos) 222 | return part, pos+1 223 | end 224 | pos = pos + 1 225 | end 226 | end 227 | end 228 | M.match_bracketed = match_bracketed 229 | 230 | 231 | local function match_comment(s, pos) 232 | pos = pos or 1 233 | if s:sub(pos, pos+1) ~= '--' then 234 | return nil, pos 235 | end 236 | pos = pos + 2 237 | if s:sub(pos,pos) == '[' then 238 | local partt, post = match_string(s, pos) 239 | if partt then 240 | return '--' .. partt, post 241 | end 242 | end 243 | local part; part, pos = s:match('^([^\n]*\n?)()', pos) 244 | return '--' .. part, pos 245 | end 246 | 247 | 248 | local wordop = {['and']=true, ['or']=true, ['not']=true} 249 | local is_compare = {['>']=true, ['<']=true, ['~']=true} 250 | local function match_expression(s, pos) 251 | pos = pos or 1 252 | local posa = pos 253 | local lastident 254 | local poscs, posce 255 | while pos do 256 | local c = s:sub(pos,pos) 257 | if c == '"' or c == "'" or c == '[' and s:find('^[=%[]', pos+1) then 258 | local part; part, pos = match_string(s, pos) 259 | assert(part, 'syntax error') 260 | elseif c == '-' and s:sub(pos+1,pos+1) == '-' then 261 | -- note: handle adjacent comments in loop to properly support 262 | -- backtracing (poscs/posce). 263 | poscs = pos 264 | while s:sub(pos,pos+1) == '--' do 265 | local part; part, pos = match_comment(s, pos) 266 | assert(part) 267 | pos = s:match('^%s*()', pos) 268 | posce = pos 269 | end 270 | elseif c == '(' or c == '{' or c == '[' then 271 | local part; part, pos = match_bracketed(s, pos) 272 | elseif c == '=' and s:sub(pos+1,pos+1) == '=' then 273 | pos = pos + 2 -- skip over two-char op containing '=' 274 | elseif c == '=' and is_compare[s:sub(pos-1,pos-1)] then 275 | pos = pos + 1 -- skip over two-char op containing '=' 276 | elseif c:match'^[%)%}%];,=]' then 277 | local part = s:sub(posa, pos-1) 278 | return part, pos 279 | elseif c:match'^[%w_]' then 280 | local newident,newpos = s:match('^([%w_]+)()', pos) 281 | if pos ~= posa and not wordop[newident] then -- non-first ident 282 | local pose = ((posce == pos) and poscs or pos) - 1 283 | while s:match('^%s', pose) do pose = pose - 1 end 284 | local ce = s:sub(pose,pose) 285 | if ce:match'[%)%}\'\"%]]' or 286 | ce:match'[%w_]' and not wordop[lastident] 287 | then 288 | local part = s:sub(posa, pos-1) 289 | return part, pos 290 | end 291 | end 292 | lastident, pos = newident, newpos 293 | else 294 | pos = pos + 1 295 | end 296 | pos = s:find('[%(%{%[%)%}%]\"\';,=%w_%-]', pos) 297 | end 298 | local part = s:sub(posa, #s) 299 | return part, #s+1 300 | end 301 | M.match_expression = match_expression 302 | 303 | 304 | local function match_namelist(s, pos) 305 | pos = pos or 1 306 | local list = {} 307 | while 1 do 308 | local c = #list == 0 and '^' or '^%s*,%s*' 309 | local item, post = s:match(c .. '([%a_][%w_]*)%s*()', pos) 310 | if item then pos = post else break end 311 | list[#list+1] = item 312 | end 313 | return list, pos 314 | end 315 | M.match_namelist = match_namelist 316 | 317 | 318 | local function match_explist(s, pos) 319 | pos = pos or 1 320 | local list = {} 321 | while 1 do 322 | if #list ~= 0 then 323 | local post = s:match('^%s*,%s*()', pos) 324 | if post then pos = post else break end 325 | end 326 | local item; item, pos = match_expression(s, pos) 327 | assert(item, 'syntax error') 328 | list[#list+1] = item 329 | end 330 | return list, pos 331 | end 332 | M.match_explist = match_explist 333 | 334 | 335 | local function gsub(s, f) 336 | local pos = 1 337 | local posa = 1 338 | local ts = {} 339 | while 1 do 340 | pos = s:find('[%-\'\"%[]', pos) 341 | if not pos then break end 342 | if s:match('^%-%-', pos) then 343 | local exp = s:sub(posa, pos-1) 344 | if #exp > 0 then ts[#ts+1] = (f('e', exp) or exp) end 345 | local comment; comment, pos = match_comment(s, pos) 346 | ts[#ts+1] = (f('c', assert(comment)) or comment) 347 | posa = pos 348 | else 349 | local posb = s:find('^[\'\"%[]', pos) 350 | local str 351 | if posb then str, pos = match_string(s, posb) end 352 | if str then 353 | local exp = s:sub(posa, posb-1) 354 | if #exp > 0 then ts[#ts+1] = (f('e', exp) or exp) end 355 | ts[#ts+1] = (f('s', str) or str) 356 | posa = pos 357 | else 358 | pos = pos + 1 359 | end 360 | end 361 | end 362 | local exp = s:sub(posa) 363 | if #exp > 0 then ts[#ts+1] = (f('e', exp) or exp) end 364 | return table.concat(ts) 365 | end 366 | M.gsub = gsub 367 | 368 | 369 | return M 370 | -------------------------------------------------------------------------------- /rockspec.in: -------------------------------------------------------------------------------- 1 | package = "lua-balanced" 2 | version = "$(_VERSION)" 3 | source = { 4 | url = "git://github.com/davidm/lua-balanced.git", 5 | tag='v$(_VERSION)' 6 | } 7 | description = { 8 | summary = "Functions for matching delimited snippets of Lua code in a string", 9 | detailed = [[ 10 | This module can, for example, match a Lua string, Lua comment, or Lua expression. 11 | It is useful in particular for source filters or parsing Lua snippets 12 | embedded in another language. It is inspired by Damian Conway's 13 | Text::Balanced in Perl. The unique feature of this implementation is 14 | that that it does not rigorously lex and parse the Lua grammar. 15 | ]], 16 | license = "MIT/X11", 17 | homepage = "http://lua-users.org/wiki/LuaBalanced", 18 | -- https://github.com/davidm/lua-balanced 19 | maintainer = "David Manura ", 20 | } 21 | dependencies = { 22 | "lua >= 5.1", -- including 5.2 23 | } 24 | build = { 25 | type = "none", 26 | install = { 27 | lua = { 28 | ["luabalanced"] = "luabalanced.lua", 29 | } 30 | }, 31 | copy_directories = {"tests"}, 32 | } 33 | -- test: tests/test.lua 34 | -- _VERSION from luabalanced.lua 35 | -------------------------------------------------------------------------------- /test/example.lua: -------------------------------------------------------------------------------- 1 | local lb = require "luabalanced" 2 | 3 | -- Extract Lua expression starting at position 4. 4 | print(lb.match_expression("if x^2 + x > 5 then print(x) end", 4)) 5 | --> x^2 + x > 5 16 6 | 7 | -- Extract Lua string starting at (default) position 1. 8 | print(lb.match_string([["test\"123" .. "more"]])) 9 | --> "test\"123" 12 10 | 11 | -- Break Lua code into code types. 12 | lb.gsub([[ 13 | local x = 1 -- test 14 | print("x=", x) 15 | ]], function(u, s) 16 | print(u .. '[' .. s .. ']') 17 | end) 18 | --[[output: 19 | e[ local x = 1 ] 20 | c[-- test 21 | ] 22 | e[ print(] 23 | s["x="] 24 | e[, x) 25 | ] 26 | ]] 27 | -------------------------------------------------------------------------------- /test/test.lua: -------------------------------------------------------------------------------- 1 | -- luabalanced_test.lua 2 | -- test for luabalanced.lua 3 | 4 | package.path = package.path .. ';test/?.lua' 5 | 6 | local lb = require "luabalanced" 7 | local tuple = require "tuple" 8 | 9 | -- utility function for test suite. 10 | local function asserteq(a, b) 11 | if a ~= b then 12 | error(tostring(a) .. ' == ' .. tostring(b) .. ' failed', 2) 13 | end 14 | end 15 | 16 | -- utility function (wrap function: store return in tuple and protect) 17 | local function wrap2(f) 18 | return function(s, pos) 19 | local res = tuple(pcall(function() return f(s, pos) end)) 20 | if not res[1] then 21 | return 'error' 22 | else 23 | return tuple(unpack(res, 2, res.n)) 24 | end 25 | end 26 | end 27 | 28 | --## match_bracketed tests 29 | 30 | -- test wrapper for lb.match_bracketed 31 | local mb = wrap2(lb.match_bracketed) 32 | 33 | -- trivial tests 34 | asserteq(mb'', tuple(nil, 1)) 35 | asserteq(mb'a', tuple(nil, 1)) 36 | asserteq(mb'{', 'error') 37 | asserteq(mb'}', tuple(nil, 1)) 38 | asserteq(mb'{[}]', 'error') 39 | asserteq(mb('[{}]'), tuple('[{}]', 5)) 40 | 41 | -- test with pos 42 | asserteq(mb('[][a(a)a].', 3), tuple('[a(a)a]', 10)) 43 | 44 | -- test with strings 45 | asserteq(mb('[ "]" ]'), tuple('[ "]" ]', 8)) 46 | asserteq(mb("[ '[' ]"), tuple("[ '[' ]", 8)) 47 | asserteq(mb("[ [=[ ]=] ]"), tuple("[ [=[ ]=] ]", 12)) 48 | asserteq(mb("[[ ] ]]"), tuple("[[ ] ]]", 8)) 49 | asserteq(mb("[=[ [ ]=]"), tuple("[=[ [ ]=]", 10)) 50 | 51 | --## match_expression tests 52 | 53 | -- test wrapper for lb.match_expression 54 | local me = wrap2(lb.match_expression) 55 | asserteq(me'a', tuple('a', 2)) 56 | asserteq(me'a b=c', tuple('a ', 3)) 57 | asserteq(me'a and b', tuple('a and b', 8)) 58 | asserteq(me'a and b ', tuple('a and b ', 9)) 59 | asserteq(me'a and b c', tuple('a and b ', 9)) 60 | asserteq(me'a+b', tuple('a+b', 4)) 61 | asserteq(me'a+b b=c', tuple('a+b ', 5)) 62 | asserteq(me'{function()end}+b c', tuple('{function()end}+b ', 19)) 63 | asserteq(me'{} e', tuple('{} ', 4)) 64 | asserteq(me'() e', tuple('() ', 4)) 65 | asserteq(me'"" e', tuple('"" ', 4)) 66 | asserteq(me"'' e", tuple("'' ", 4)) 67 | asserteq(me'a[1] e', tuple('a[1] ', 6)) 68 | asserteq(me'ab.cd e', tuple('ab.cd ', 7)) 69 | asserteq(me'ab:cd() e', tuple('ab:cd() ', 9)) 70 | asserteq(me'(x) (y) z', tuple('(x) (y) ', 9)) 71 | asserteq(me'x >= y', tuple('x >= y', 7)) 72 | 73 | -- numbers 74 | asserteq(me'1e2 a', tuple('1e2 ', 5)) 75 | asserteq(me'1e+2 a', tuple('1e+2 ', 6)) 76 | asserteq(me'1.2e+2 a', tuple('1.2e+2 ', 8)) 77 | asserteq(me'.2e+2 a', tuple('.2e+2 ', 7)) 78 | 79 | -- comments 80 | asserteq(me'a+ -- b\nc', tuple('a+ -- b\nc', 10)) 81 | asserteq(me'a --[[]] b', tuple('a --[[]] ', 10)) 82 | asserteq(me'a+ --[[]] b', tuple('a+ --[[]] b', 12)) 83 | asserteq(me'a --[[]] + b', tuple('a --[[]] + b', 13)) 84 | asserteq(me'a+ --[[]] --[=[]=] b', tuple('a+ --[[]] --[=[]=] b', 21)) 85 | asserteq(me'a+ -- b\n -- b\n b c', tuple('a+ -- b\n -- b\n b ', 18)) 86 | asserteq(me'a --""+\nb', tuple('a --""+\n', 9)) 87 | 88 | -- check for exceptions giving lots of possibly not syntactically 89 | -- correct data. 90 | local text = assert(io.open'test/test.lua'):read'*a' 91 | for i=1,#text do 92 | local res = me(text,i) 93 | if res[1] == 'error' and not res[2]:match('syntax error') then 94 | error(res[2]) 95 | end 96 | end 97 | 98 | --## match_explist tests 99 | 100 | local ml = function(...) 101 | local res = wrap2(lb.match_explist)(...) 102 | res[1] = table.concat(res[1], '|') 103 | return res 104 | end 105 | asserteq(ml ' d', tuple(' ', 2)) 106 | asserteq(ml 'a+b,b*c d', tuple('a+b|b*c ', 9)) 107 | 108 | --## match_namelist tests 109 | 110 | local ml = function(...) 111 | local res = wrap2(lb.match_namelist)(...) 112 | res[1] = table.concat(res[1], '|') 113 | return res 114 | end 115 | asserteq(ml ' ', tuple('', 1)) 116 | asserteq(ml 'a b', tuple('a', 3)) 117 | asserteq(ml 'a,b d', tuple('a|b', 5)) 118 | asserteq(ml 'a,b+d', tuple('a|b', 4)) 119 | 120 | 121 | --## gsub tests 122 | 123 | local ls = lb.gsub 124 | 125 | local function f(u, s) 126 | return '[' .. u .. ':' .. s .. ']' 127 | end 128 | 129 | asserteq(ls('', f), '') 130 | asserteq(ls(' ', f), '[e: ]') 131 | asserteq(ls(' "z" ;', f), '[e: ][s:"z"][e: ;]') 132 | asserteq(ls(' --[[z]] ;', f), '[e: ][c:--[[z]]][e: ;]') 133 | asserteq(ls(' --z\n ;', f), '[e: ][c:--z\n][e: ;]') 134 | asserteq(ls(' --z', f), '[e: ][c:--z]') 135 | asserteq(ls('[][=[ ] ]=] ;', f), '[e:[]][s:[=[ ] ]=]][e: ;]') 136 | asserteq(ls('a - b --[[d]] .. "--"', f), '[e:a - b ][c:--[[d]]][e: .. ][s:"--"]') 137 | 138 | print 'DONE' 139 | 140 | -------------------------------------------------------------------------------- /test/tuple.lua: -------------------------------------------------------------------------------- 1 | -- tuple.lua 2 | -- Simple tuple implementation using tables. 3 | -- (c) 2008, David Manura, Licensed under the same terms as Lua (MIT license). 4 | 5 | local select = select 6 | local tostring = tostring 7 | local setmetatable = setmetatable 8 | local table_concat = table.concat 9 | 10 | local mt = {} 11 | local function tuple(...) 12 | local t = setmetatable({n=select('#',...), ...}, mt) 13 | return t 14 | end 15 | function mt:__tostring() 16 | local ts = {} 17 | for i=1,self.n do local v = self[i] 18 | ts[#ts+1] = type(v) == 'string' and string.format('%q', v) or tostring(self[i]) 19 | end 20 | return 'tuple(' .. table_concat(ts, ',') .. ')' 21 | end 22 | function mt.__eq(a, b) 23 | if a.n ~= b.n then return false end 24 | for i=1,a.n do 25 | if a[i] ~= b[i] then return false end 26 | end 27 | return true 28 | end 29 | 30 | return tuple 31 | 32 | -------------------------------------------------------------------------------- /util.mk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # utility commands for package maintainers 3 | 4 | VERSIONFROM:=$(shell sed -n 's,.*_VERSION \+from \+\([^ ]\+\).*,\1,p' rockspec.in) 5 | VERSION:=$(shell sed -n "s,.*_VERSION='\([^']*\)'.*,\1,p" $(VERSIONFROM))-1 6 | NAME=$(shell lua -e 'dofile"rockspec.in"; print(package)') 7 | 8 | dist : 9 | rm -fr tmp/$(NAME)-$(VERSION) tmp/$(NAME)-$(VERSION).zip 10 | for x in `cat MANIFEST`; do install -D $$x tmp/$(NAME)-$(VERSION)/$$x || exit; done 11 | sed 's,$$(_VERSION),$(VERSION),g' tmp/$(NAME)-$(VERSION)/rockspec.in > tmp/$(NAME)-$(VERSION)/$(NAME)-$(VERSION).rockspec 12 | cd tmp && zip -r $(NAME)-$(VERSION).zip $(NAME)-$(VERSION) 13 | 14 | install : dist 15 | cd tmp/$(NAME)-$(VERSION) && luarocks make 16 | 17 | test : 18 | @if [ -e test.lua ]; then lua test.lua; fi 19 | @if [ -e test/test.lua ]; then lua test/test.lua; fi 20 | 21 | tag : 22 | git tag -f v$(VERSION) 23 | 24 | version : 25 | @echo $(NAME)-$(VERSION) 26 | 27 | .PHONY : dist install test tag version 28 | --------------------------------------------------------------------------------