mediawiki-extensions-Scribunto/engines/LuaCommon/lualib/stringtools/stringreader.lua
tstarling cebe775ee8 Added more Lua environment features
Package library:

* Added a simulation of the Lua 5.1 package library.
* Removed mw.import(), replaced it with a package loader. Packages can be
  retrieved from the wiki, using require('Module:Foo'), or from files
  distributed with Scribunto, using require('foo'). The "Module:" prefix allows
  for source compatibility with existing Lua code.
* Added a couple of libraries from LuaForge: luabit and stringtools.
* Made fetchModuleFromParser() return null on error instead of throwing an
  exception, to more easily support the desired behaviour of the package loader,
  which needs to return null on error.
* Renamed mw.setupEnvironment() to mw.setup() since it is setting up things
  other than the environment now.
* In MWServer:handleRegisterLibrary(), remove the feature which interprets dots
  in library names, since LuaSandbox doesn't support this.

Improved module isolation and related refactoring:

* Expose restricted versions of getfenv() and setfenv() to user Lua code.
  Requires luasandbox r114952.
* Don't cache the export list returned by module execution for later function
  calls. This breaks isolation of #invoke calls, since the local variables are
  persistent.
* Removed ScribuntoFunctionBase and its children, since it doesn't really have
  a purpose if it can't cache anything. Instead, invoke functions using a module
  method called invoke().
* Removed Module::initialize(), replaced it with a validate() function. This is
  a more elegant interface and works better with the new module caching scheme.
* Use a Status object for the return value of Engine::validate() instead of an
  array. Use the formatting facilities of the Status class.

Other:

* Removed "too many returns" error, doesn't fit in with Lua conventions.
* Use the standalone engine by default, so that the extension will work without
  configuration for more people.
* Added an accessor for $engine->interpreter
* Fix mw.clone() to correctly clone metatables
* If the standalone interpreter exits due to an error, there are some contexts
  where the initial error will be caught and ignored, and the user will see the
  error from checkValid() instead. In this case, rethrow the original error for
  a more informative message.
* Load mw.lua into the initial standalone environment, to reduce code
  duplication between mw.lua and MWServer.lua.
* Fixed a bug in Scribunto_LuaStandaloneInterpreter::handleCall() for functions
  that return no results.
* Fixed a bug in encodeLuaVar() for strings with "\r". Added test case.
* In MWServer.lua, don't call error() for internal errors, instead just print
  the error and exit. This avoids a protocol violation when an error is
  encountered from within handleCall().
* Added lots of documentation. Lua doc comments are in LuaDoc format.

Change-Id: Ie2fd572c362bedf02f45d3fa5352a5280e034740
2012-04-18 13:46:18 +10:00

137 lines
3.3 KiB
Lua

local strfind, strsub, strmatch, gmatch
= string.find, string.sub, string.match, string.gmatch
local floor
= math.floor
local tostring, setmetatable, select
= tostring, setmetatable, select
local cowrap, yield
= coroutine.wrap, coroutine.yield
local _G = _G
local function expectmatch_aux(sr, start, stop, ...)
if not start then
sr.lastmatch = nil
return false
else
sr._pos = stop + 1
if select('#', ...) > 1 then
sr.lastmatch = {select(2, ...)}
return select(2, ...)
else
sr.lastmatch = {...}
return ...
end
end
end
local stringreader_meta = {
__index = {
expect = function(self, patt, exact)
local pos = self._pos
if exact then
if strsub(self._source_text, pos, pos+#patt-1) == patt then
self._pos = pos+#patt
self.lastmatch = patt
return patt
else
return false
end
end
if (strsub(patt,1,1) == '^') then
if (pos > 1) then
return nil
end
patt = strsub(patt,2)
end
patt = '^(' .. patt .. ')'
if (strsub(patt,-2) == '$)') and (strsub(patt,-3,-3) ~= '%') then
patt = strsub(patt,1,-3) .. ')$'
end
return expectmatch_aux(self, strfind(self._source_text, patt, pos))
end;
endofinput = function(self)
return self._pos > #self._source_text;
end;
skipwhitespace = function(self)
self._pos = strmatch(self._source_text, '%s*()', self._pos)
end;
skipto = function(self, patt, exact)
local new_pos = strfind(self._source_text, patt, self._pos, exact)
if new_pos then
self._pos = new_pos
return true
else
return false
end
end;
readchar = function(self)
local pos = self._pos
local c = strsub(self._source_text,pos,pos)
self._pos = pos + 1
return c
end;
peekchar = function(self)
local pos = self._pos
return strsub(self._source_text,pos,pos)
end;
pos = function(self, new_pos, relative)
if new_pos then
self._pos = relative and (self._pos+new_pos) or new_pos
return self
else
return self._pos
end
end;
getlines = function(self)
local lines = self._lines
if not lines then
lines = {1}
for linestart in gmatch(self._source_text, '\n()') do
lines[#lines+1] = linestart
end
self._lines = lines
end
return lines
end;
pos2d = function(self, pos, relative)
pos = pos and (relative and (self._pos+pos) or pos) or self._pos
if (pos < 1) then return nil; end
local lines = self:getlines()
local low, high = 1, #lines
while true do
local line_num = floor((low + high) / 2)
local line_start = lines[line_num]
if (line_start > pos) then
high = line_num - 1
else
local next_line_start = lines[line_num+1]
if (next_line_start == nil) or (next_line_start > pos) then
return line_num, pos - line_start + 1
end
low = line_num + 1
end
end
end;
source_text = function(self, new_value)
if new_value then
self._source_text = new_value
else
return self._source_text
end
end;
rest = function(self)
return strsub(self._source_text, self._pos)
end;
}
}
local function stringreader(source_text)
return setmetatable({_source_text=tostring(source_text), _pos=1}, stringreader_meta)
end
_G.stringreader = stringreader
return stringreader