From 532fce5942c385a32bb30b21f0ec65da88642e0e Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Mon, 8 Dec 2014 00:39:09 -0200 Subject: [PATCH 1/4] Update json.lua About 4x faster --- json4lua/json/json.lua | 134 +++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 65 deletions(-) diff --git a/json4lua/json/json.lua b/json4lua/json/json.lua index de03924..3e7e58f 100755 --- a/json4lua/json/json.lua +++ b/json4lua/json/json.lua @@ -65,19 +65,19 @@ function encode (v) if v==nil then return "null" end - + local vtype = base.type(v) -- Handle strings if vtype=='string' then return '"' .. encodeString(v) .. '"' -- Need to handle encoding in string end - + -- Handle booleans if vtype=='number' or vtype=='boolean' then return base.tostring(v) end - + -- Handle tables if vtype=='table' then local rval = {} @@ -100,12 +100,12 @@ function encode (v) return '{' .. table.concat(rval,',') .. '}' end end - + -- Handle null values if vtype=='function' and v==null then return 'null' end - + base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v)) end @@ -226,8 +226,8 @@ function decode_scanNumber(s,startPos) local stringLen = string.len(s) local acceptableChars = "+-0123456789.e" while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true) - and endPos<=stringLen - ) do + and endPos<=stringLen + ) do endPos = endPos + 1 end local stringValue = 'return ' .. string.sub(s,startPos, endPos-1) @@ -276,18 +276,17 @@ end -- Initialize some things used by decode_scanString -- You know, for efficiency local escapeSequences = { - ["\\t"] = "\t", - ["\\f"] = "\f", - ["\\r"] = "\r", - ["\\n"] = "\n", - ["\\b"] = "\b" + t = "\t", + f = "\f", + r = "\r", + n = "\n", + b = "\b" } -base.setmetatable(escapeSequences, {__index = function(t,k) - -- skip "\" aka strip escape - return string.sub(k,2) -end}) -- END SoniEx2 +-- 5.0, 5.1, 5.2, 5.3+ +local mod = math.mod or math.fmod or (loadstring or load)("local a,b = ... return a % b") + --- Scans a JSON string from the opening inverted comma or single quote to the -- end of the string. -- Returns the string extracted as a Lua string, @@ -299,50 +298,55 @@ end}) function decode_scanString(s,startPos) base.assert(startPos, 'decode_scanString(..) called without start position') local startChar = string.sub(s,startPos,startPos) - -- START SoniEx2 - -- PS: I don't think single quotes are valid JSON - base.assert(startChar == [["]] or startChar == [[']],'decode_scanString called for a non-string') - --base.assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart) - local t = {} - local i,j = startPos,startPos - while string.find(s, startChar, j+1) ~= j+1 do - local oldj = j - i,j = string.find(s, "\\.", j+1) - local x,y = string.find(s, startChar, oldj+1) - if not i or x < i then - base.print(s, startPos, string.sub(s,startPos,oldj)) - i,j = x,y-1 - if not x then base.print(s, startPos, string.sub(s,startPos,oldj)) end - end - table.insert(t, string.sub(s, oldj+1, i-1)) - if string.sub(s, i, j) == "\\u" then - local a = string.sub(s,j+1,j+4) - j = j + 4 - local n = base.tonumber(a, 16) - base.assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j) - -- math.floor(x/2^y) == lazy right shift - -- a % 2^b == bitwise_and(a, (2^b)-1) - -- 64 = 2^6 - -- 4096 = 2^12 (or 2^6 * 2^6) - local x - if n < 0x80 then - x = string.char(n % 0x80) - elseif n < 0x800 then - -- [110x xxxx] [10xx xxxx] - x = string.char(0xC0 + (math.floor(n/64) % 0x20), 0x80 + (n % 0x40)) + base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string') + local escaped = false + local endPos = startPos + 1 + local bEnded = false + local stringLen = string.len(s) + local stringLenAdd1 = stringLen + 1 + repeat + -- Character escaping is only used to escape the string delimiters + if not escaped then + local curChar = string.sub(s,endPos,endPos) + if curChar==[[\]] then + escaped = true else - -- [1110 xxxx] [10xx xxxx] [10xx xxxx] - x = string.char(0xE0 + (math.floor(n/4096) % 0x10), 0x80 + (math.floor(n/64) % 0x40), 0x80 + (n % 0x40)) + bEnded = curChar==startChar end - table.insert(t, x) else - table.insert(t, escapeSequences[string.sub(s, i, j)]) + -- If we're escaped, we accept the current character come what may + escaped = false end - end - table.insert(t,string.sub(j, j+1)) - base.assert(string.find(s, startChar, j+1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")") - return table.concat(t,""), j+2 - -- END SoniEx2 + endPos = endPos + 1 + base.assert(endPos <= stringLenAdd1, "String decoding failed: unterminated string") + until bEnded + --local stringValue = 'return ' .. string.sub(s, startPos, endPos-1) + --local stringEval = base.loadstring(stringValue) + --base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos) + --return stringEval(), endPos + --return table.concat(t), endPos + local str = string.sub(s, startPos+1, endPos-2) + str = string.gsub(str, "\\([^u])", "\\v***%1") -- heh + str = string.gsub(str, "\\([vu])(...(.))", + function(a,b,c) + if a == "v" then + return escapeSequences[c] or c + else + local n = base.tonumber(b, 16) + base.assert(n, "String decoding failed: bad Unicode escape") + if n < 128 then + -- [0xxx xxxx] aka ASCII + return string.char(n) + elseif n < 2048 then + -- [110x xxxx] [10xx xxxx] + return string.char(192+math.floor(n/64),128+mod(n,64)) + else + -- [1110 xxxx] [10xx xxxx] [10xx xxxx] + return string.char(224+math.floor(n/4096),128+mod(math.floor(n/64),64),128+mod(n, 64)) + end + end + end) + return str, endPos end --- Scans a JSON string skipping all whitespace from the current start position. @@ -366,18 +370,18 @@ end -- @return The string appropriately escaped. local escapeList = { - ['"'] = '\\"', - ['\\'] = '\\\\', - ['/'] = '\\/', - ['\b'] = '\\b', - ['\f'] = '\\f', - ['\n'] = '\\n', - ['\r'] = '\\r', - ['\t'] = '\\t' + ['"'] = '\\"', + ['\\'] = '\\\\', + ['/'] = '\\/', + ['\b'] = '\\b', + ['\f'] = '\\f', + ['\n'] = '\\n', + ['\r'] = '\\r', + ['\t'] = '\\t' } function encodeString(s) - return s:gsub(".", function(c) return escapeList[c] end) -- SoniEx2: 5.0 compat + return s:gsub(".", function(c) return escapeList[c] end) -- SoniEx2: 5.0 compat end -- Determines whether the given Lua type is an array or a table / dictionary. From 57f0eed445badcbbf2086e43290a814726fa3cf5 Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Mon, 8 Dec 2014 00:45:07 -0200 Subject: [PATCH 2/4] Update tests.lua Fixed unit test --- json4lua/examples/tests.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json4lua/examples/tests.lua b/json4lua/examples/tests.lua index 999bec3..9d56b1f 100755 --- a/json4lua/examples/tests.lua +++ b/json4lua/examples/tests.lua @@ -121,7 +121,7 @@ function testJSON4Lua() s = [["Test\u00A7\\"]] r,e = json._decode_scanString(s,1) - assert(r=="Test\xC2\xA7\\" and e==9) + assert(r=="Test\194\167\\\"" and e==9) print(s,r) -- Test decode_scanNumber @@ -164,7 +164,7 @@ function testJSON4Lua() s = [["Test\u00A7\\\""]] r,e = json.decode(s) - assert(r=="Test\xC2\xA7\\\"", r) + assert(r=="Test\194\167\\\"", string.format("%q expected, got %q", "Test\194\167\\\"", r)) print(s,r) -- Test decode_scanObject From 258e786077f34e7f64f1e92b9e75f051e2ec43ab Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Mon, 8 Dec 2014 00:47:18 -0200 Subject: [PATCH 3/4] Update timetrials.lua --- json4lua/examples/timetrials.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/json4lua/examples/timetrials.lua b/json4lua/examples/timetrials.lua index cbda514..e4d6ea4 100755 --- a/json4lua/examples/timetrials.lua +++ b/json4lua/examples/timetrials.lua @@ -7,6 +7,9 @@ require('json') require('os') require('table') +-- this is so we don't get shouted at for syntax errors +local mod = math.mod or math.fmod or (loadstring or load)("local a,b = ... return a % b") + local t1 = os.clock() local jstr local v @@ -26,7 +29,7 @@ end for i = 1,100 do local t = {} for j=1,500 do - local m= math.mod(j,3) + local m= mod(j,3) if (m==0) then t['a'..j] = true elseif m==1 then @@ -43,4 +46,4 @@ print (jstr) --print(type(t1)) local t2 = os.clock() -print ("Elapsed time=" .. os.difftime(t2,t1) .. "s") \ No newline at end of file +print ("Elapsed time=" .. t2 - t1 .. "s") From d918c1b0f388091f646d99343b4605bb180f43d5 Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Thu, 11 Dec 2014 16:31:05 -0200 Subject: [PATCH 4/4] Fix bugs --- json4lua/examples/example.lua | 7 +- json4lua/examples/tests.lua | 44 ++++++++- json4lua/examples/timetrials.lua | 12 ++- json4lua/json/json.lua | 153 ++++++++++++++++++------------- trunk/json/json.lua | 70 ++++++++++++-- 5 files changed, 206 insertions(+), 80 deletions(-) diff --git a/json4lua/examples/example.lua b/json4lua/examples/example.lua index 36497da..a10c083 100755 --- a/json4lua/examples/example.lua +++ b/json4lua/examples/example.lua @@ -4,6 +4,9 @@ Demonstrates the simple functionality of the json module. ]]-- json = require('json') +-- lua 5.1+ compat +local foreach = table.foreach or loadstring("local t,f = ... for k,v in pairs(t) do if f(k,v) ~= nil then break end end") + -- Object to JSON encode test = { @@ -18,6 +21,6 @@ print('JSON encoded test is: ' .. jsonTest) result = json.decode(jsonTest) print ("The decoded table result:") -table.foreach(result,print) +foreach(result, print) print ("The decoded table result.three") -table.foreach(result.three, print) +foreach(result.three, print) diff --git a/json4lua/examples/tests.lua b/json4lua/examples/tests.lua index 999bec3..d29ba59 100755 --- a/json4lua/examples/tests.lua +++ b/json4lua/examples/tests.lua @@ -162,10 +162,52 @@ function testJSON4Lua() end + -- Test string decoding s = [["Test\u00A7\\\""]] r,e = json.decode(s) - assert(r=="Test\xC2\xA7\\\"", r) + assert(r=="Test\194\167\\\"") print(s,r) + + s = [["\\u"]] + r,e = json.decode(s) + assert(r=="\\u") + print(s,r) + + s = [["\\"]] + r,e = json.decode(s) + assert(r=="\\") + print(s,r) + + s = [["\v\///"]] + r,e = json.decode(s) + assert(r=="v///") + print(s,r) + + -- Test invalid unicode escapes + s = [["\u000"]] + r,e = pcall(json.decode,s) + assert(not r and e) + print(s,r,e) + + s = [["\u00"]] + r,e = pcall(json.decode,s) + assert(not r and e) + print(s,r,e) + + s = [["\u0"]] + r,e = pcall(json.decode,s) + assert(not r and e) + print(s,r,e) + + s = [["\u"]] + r,e = pcall(json.decode,s) + assert(not r and e) + print(s,r,e) + + s = [["\uXXXX"]] + r,e = pcall(json.decode,s) + assert(not r and e) + print(s,r,e) -- Test decode_scanObject s = [[ {"one":1, "two":2, "three":"three", "four":true} ]] diff --git a/json4lua/examples/timetrials.lua b/json4lua/examples/timetrials.lua index cbda514..f94e703 100755 --- a/json4lua/examples/timetrials.lua +++ b/json4lua/examples/timetrials.lua @@ -1,12 +1,14 @@ --[[ Some Time Trails for the JSON4Lua package -]]-- +--]] require('json') require('os') require('table') +local mod = math.mod or math.fmod or (loadstring or load)("local a,b = ... return a % b") + local t1 = os.clock() local jstr local v @@ -16,7 +18,7 @@ for i=1,100 do table.insert(t,j) end for j=1,500 do - table.insert(t,"VALUE") + table.insert(t,"VALUE\n\n\"\t") end jstr = json.encode(t) v = json.decode(jstr) @@ -26,7 +28,7 @@ end for i = 1,100 do local t = {} for j=1,500 do - local m= math.mod(j,3) + local m = mod(j,3) if (m==0) then t['a'..j] = true elseif m==1 then @@ -39,8 +41,8 @@ for i = 1,100 do v = json.decode(jstr) end -print (jstr) +--print (jstr) --print(type(t1)) local t2 = os.clock() -print ("Elapsed time=" .. os.difftime(t2,t1) .. "s") \ No newline at end of file +print ("Elapsed time=" .. t2 - t1 .. "s") diff --git a/json4lua/json/json.lua b/json4lua/json/json.lua index de03924..a445551 100755 --- a/json4lua/json/json.lua +++ b/json4lua/json/json.lua @@ -38,7 +38,16 @@ local base = _G ----------------------------------------------------------------------------- -- Module declaration ----------------------------------------------------------------------------- -module("json") +local _ENV = _ENV +local hasENV = false +-- Check for Lua 5.2 +if not _ENV then + module("json") +else + -- Use the new module system + hasENV = true +end +_ENV = {} -- Public functions @@ -65,19 +74,19 @@ function encode (v) if v==nil then return "null" end - + local vtype = base.type(v) -- Handle strings if vtype=='string' then return '"' .. encodeString(v) .. '"' -- Need to handle encoding in string end - + -- Handle booleans if vtype=='number' or vtype=='boolean' then return base.tostring(v) end - + -- Handle tables if vtype=='table' then local rval = {} @@ -100,12 +109,12 @@ function encode (v) return '{' .. table.concat(rval,',') .. '}' end end - + -- Handle null values if vtype=='function' and v==null then return 'null' end - + base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v)) end @@ -226,8 +235,8 @@ function decode_scanNumber(s,startPos) local stringLen = string.len(s) local acceptableChars = "+-0123456789.e" while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true) - and endPos<=stringLen - ) do + and endPos<=stringLen + ) do endPos = endPos + 1 end local stringValue = 'return ' .. string.sub(s,startPos, endPos-1) @@ -276,18 +285,21 @@ end -- Initialize some things used by decode_scanString -- You know, for efficiency local escapeSequences = { - ["\\t"] = "\t", - ["\\f"] = "\f", - ["\\r"] = "\r", - ["\\n"] = "\n", - ["\\b"] = "\b" + t = "\t", + f = "\f", + r = "\r", + n = "\n", + b = "\b" +} +local paddingSequences = { + u = "\\u" } -base.setmetatable(escapeSequences, {__index = function(t,k) - -- skip "\" aka strip escape - return string.sub(k,2) -end}) -- END SoniEx2 +-- 5.0, 5.1, 5.2, 5.3+ +-- for _positive numbers_, math.fmod(a,b) == a % b +local mod = math.mod or math.fmod or ((base.loadstring or base.load)("return function(a,b) return a % b end")()) + --- Scans a JSON string from the opening inverted comma or single quote to the -- end of the string. -- Returns the string extracted as a Lua string, @@ -299,50 +311,58 @@ end}) function decode_scanString(s,startPos) base.assert(startPos, 'decode_scanString(..) called without start position') local startChar = string.sub(s,startPos,startPos) - -- START SoniEx2 - -- PS: I don't think single quotes are valid JSON - base.assert(startChar == [["]] or startChar == [[']],'decode_scanString called for a non-string') - --base.assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart) - local t = {} - local i,j = startPos,startPos - while string.find(s, startChar, j+1) ~= j+1 do - local oldj = j - i,j = string.find(s, "\\.", j+1) - local x,y = string.find(s, startChar, oldj+1) - if not i or x < i then - base.print(s, startPos, string.sub(s,startPos,oldj)) - i,j = x,y-1 - if not x then base.print(s, startPos, string.sub(s,startPos,oldj)) end - end - table.insert(t, string.sub(s, oldj+1, i-1)) - if string.sub(s, i, j) == "\\u" then - local a = string.sub(s,j+1,j+4) - j = j + 4 - local n = base.tonumber(a, 16) - base.assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j) - -- math.floor(x/2^y) == lazy right shift - -- a % 2^b == bitwise_and(a, (2^b)-1) - -- 64 = 2^6 - -- 4096 = 2^12 (or 2^6 * 2^6) - local x - if n < 0x80 then - x = string.char(n % 0x80) - elseif n < 0x800 then - -- [110x xxxx] [10xx xxxx] - x = string.char(0xC0 + (math.floor(n/64) % 0x20), 0x80 + (n % 0x40)) + base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string') + local escaped = false + local endPos = startPos + 1 + local bEnded = false + local stringLen = string.len(s) + local stringLenAdd1 = stringLen + 1 + repeat + if not escaped then + local curChar = string.sub(s,endPos,endPos) + if curChar==[[\]] then + escaped = true else - -- [1110 xxxx] [10xx xxxx] [10xx xxxx] - x = string.char(0xE0 + (math.floor(n/4096) % 0x10), 0x80 + (math.floor(n/64) % 0x40), 0x80 + (n % 0x40)) + bEnded = curChar==startChar end - table.insert(t, x) else - table.insert(t, escapeSequences[string.sub(s, i, j)]) + escaped = false end + endPos = endPos + 1 + base.assert(endPos <= stringLenAdd1, "unfinished string") + until bEnded + --local stringValue = 'return ' .. string.sub(s, startPos, endPos-1) + --local stringEval = base.loadstring(stringValue) + --base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos) + --return stringEval(), endPos + --return table.concat(t), endPos + local str = string.sub(s, startPos+1, endPos-2) + -- pad - we use a table here because we want to keep \u as \u + str = string.gsub(str, "\\(.)", function(x) return paddingSequences[x] or ("\\v" .. x .. "///") end) + if string.find(str, "\\u", -5) then + -- string end looks like `"...\uXXX"` or `"...\uXX"` or `"...\uX"` or even `"...\u"` + base.error("String decoding failed: bad Unicode escape") end - table.insert(t,string.sub(j, j+1)) - base.assert(string.find(s, startChar, j+1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")") - return table.concat(t,""), j+2 - -- END SoniEx2 + str = string.gsub(str, "\\([vu])((.)...)", + function(a,b,c) + if a == "v" then + return escapeSequences[c] or c + else + local n = base.tonumber(b, 16) + base.assert(n, "String decoding failed: bad Unicode escape") + if n < 128 then + -- [0xxx xxxx] aka ASCII + return string.char(n) + elseif n < 2048 then + -- [110x xxxx] [10xx xxxx] + return string.char(192+math.floor(n/64),128+mod(n,64)) + else + -- [1110 xxxx] [10xx xxxx] [10xx xxxx] + return string.char(224+math.floor(n/4096),128+mod(math.floor(n/64),64),128+mod(n, 64)) + end + end + end) + return str, endPos end --- Scans a JSON string skipping all whitespace from the current start position. @@ -366,18 +386,18 @@ end -- @return The string appropriately escaped. local escapeList = { - ['"'] = '\\"', - ['\\'] = '\\\\', - ['/'] = '\\/', - ['\b'] = '\\b', - ['\f'] = '\\f', - ['\n'] = '\\n', - ['\r'] = '\\r', - ['\t'] = '\\t' + ['"'] = '\\"', + ['\\'] = '\\\\', + ['/'] = '\\/', + ['\b'] = '\\b', + ['\f'] = '\\f', + ['\n'] = '\\n', + ['\r'] = '\\r', + ['\t'] = '\\t' } function encodeString(s) - return s:gsub(".", function(c) return escapeList[c] end) -- SoniEx2: 5.0 compat + return s:gsub(".", function(c) return escapeList[c] end) -- SoniEx2: 5.0 compat end -- Determines whether the given Lua type is an array or a table / dictionary. @@ -417,3 +437,6 @@ function isEncodable(o) return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null) end +if hasENV then + return _ENV +end diff --git a/trunk/json/json.lua b/trunk/json/json.lua index c809e11..88b2910 100755 --- a/trunk/json/json.lua +++ b/trunk/json/json.lua @@ -41,7 +41,16 @@ local base = _G ----------------------------------------------------------------------------- -- Module declaration ----------------------------------------------------------------------------- -module("json") +local _ENV = _ENV +local hasENV = false +-- Check for Lua 5.2 +if not _ENV then + module("json") +else + -- Use the new module system + hasENV = true +end +_ENV = {} -- Public functions @@ -142,6 +151,25 @@ function encodeString(s) return tostring(s):gsub('["\\\r\n\t]',qrep) end +-- START SoniEx2 +-- Initialize some things used by decodeString +-- You know, for efficiency +-- START SoniEx2 +-- Initialize some things used by decode_scanString +-- You know, for efficiency +local escapeSequences = { + t = "\t", + f = "\f", + r = "\r", + n = "\n", + b = "\b" +} +local paddingSequences = { + u = "\\u" +} +local mod = math.mod or math.fmod or (base.loadstring or base.load)("local a,b = ... return a % b") +-- END SoniEx2 + --- Decodes a given JSON string back to a Lua string -- The characters, escapable in JSON (see above function) are automatically -- escaped by Lua. Nevertheless, the "/" (slash) character CAN be escaped in @@ -149,7 +177,31 @@ end -- @param s The string to return as proper Lua string -- @return A JSON encoded string function decodeString(s) - return (base.load("return " .. tostring(s):gsub("\\/", "/"))) () + local str = tostring(s) + str = string.gsub(str, "\\(.)", function(x) return paddingSequences[x] or ("\\v" .. x .. "///") end) + if string.find(str, "\\u", -5) then + -- string end looks like `"...\uXXX"` or `"...\uXX"` or `"...\uX"` or even `"...\u"` + base.error("String decoding failed: bad Unicode escape") + end + return string.gsub(str, "\\([vu])((.)...)", + function(a,b,c) + if a == "v" then + return escapeSequences[c] or c + else + local n = base.tonumber(b, 16) + base.assert(n, "String decoding failed: bad Unicode escape") + if n < 128 then + -- [0xxx xxxx] aka ASCII + return string.char(n) + elseif n < 2048 then + -- [110x xxxx] [10xx xxxx] + return string.char(192+math.floor(n/64),128+mod(n,64)) + else + -- [1110 xxxx] [10xx xxxx] [10xx xxxx] + return string.char(224+math.floor(n/4096),128+mod(math.floor(n/64),64),128+mod(n, 64)) + end + end + end) end -- Determines whether the given Lua type is an array or a table / dictionary. @@ -394,15 +446,15 @@ do if t == c_esc then --table.insert(returnString, js_string:sub(start, pos-2)) --table.insert(returnString, escapechar[ js_string:byte(pos) ]) - local c = js_string:sub(pos, pos) - if (not charstounescape:find(c)) then - error("invalid escape sequence '\\"..c.."' ("..location()..")") - end + --local c = js_string:sub(pos, pos) + --if (not charstounescape:find(c)) then + --error("invalid escape sequence '\\"..c.."' ("..location()..")") + --end pos = pos + 1 --start = pos end -- jump over escaped chars, no matter what until t == true - return decodeString(js_string:sub(start-1, pos-1)) + return decodeString(js_string:sub(start, pos-2)) -- We consider the situation where no escaped chars were encountered separately, -- and use the fastest possible return in this case. @@ -539,3 +591,7 @@ do return r end end + +if hasENV then + return _ENV +end \ No newline at end of file