EM CONSTRUÇÃO
- Procurando documentos detalhados sobre Lua? Confira o manual de referência Lua na mediawiki.org!
Scribunto é uma extensão que permite que linguagens de script sejam usadas dentro do MediaWiki. Sem o Scribunto, os editores precisariam tentar usar o wikitexto como linguagem de script, o que resulta em baixo desempenho e código ilegível.
Scribunto usa Lua, uma linguagem rápida e poderosa que é comumente vista como uma linguagem incorporada em jogos, como Garry's Mod e World of Warcraft.
Antes de começar com o Lua no MediaWiki, é melhor ter um conhecimento básico da Lua. O livro oficial Programação em Lua é um bom recurso para aprender Lua se você já tem alguma experiência em programação (já conhecendo uma linguagem como JavaScript é bastante útil, pois há muitas semelhanças), caso contrário, os tutoriais em lua-users.org] e Lua para iniciantes da Wikipedia pode ser útil.
Este guia cobrirá o uso geral do Scribunto, em vez de usar Lua em si. Todos os exemplos serão salvos em Módulo:Exemplo e Predefinição:Scribunto exemplo (e suas subpáginas). Use o manual de referência para uso detalhado de todas as funções.
Antes de começar[]
Se você quiser usar o Scribunto pela primeira vez em sua wiki, você deve solicitar que a extensão Scribunto seja habilitada. Para verificar se o Scribunto já está habilitado, você pode procurá-lo em Special:Version em sua wiki. Se não estiver listada, siga as instruções em Solicitando extensões.
Se você estiver editando módulos no navegador, você pode solicitar a Extension:CodeEditor também. Se você planeja programar bastante, provavelmente é melhor editar no Notepad++ ou SublimeText ou algum outro editor de texto localmente e copiar e colar no seu navegador, ou usar o plugin Mediawiker para SublimeText.
Observe que o código padrão (
e local p = {}
) não será repetido em exemplos após o primeiro, a menos que seja relevante.
return p
Começando[]
O Scribunto armazena scripts no Módulo namespace. Esses módulos podem então ser usados em páginas wiki usando a
função parser (referida como "invocar" ou "chamar" o módulo) . Os scripts não podem ser escritos diretamente no wikitexto por design, e é recomendado chamar todos os módulos de uma predefinição em vez de usar a função parser {{#invoke:}}
em uma página, para reduzir a confusão do wikitexto.
{{#invoke:}}
Os scripts Scribunto que serão invocados devem retornar uma tabela. Essa tabela deve conter algumas funções que podem ser referidas em instruções
. Essas funções devem retornar uma string wikitexto que é o que realmente é enviado para a página. Consulte este script de trabalho mais básico:
{{#invoke:}}
local p = {}
p.helloWorld = function()
return 'Hello, world!'
end
return p
Which would be called like this:
and result in this: Erro de comando: Não existe nenhum módulo "Example".
{{#invoke: Example | helloWorld }}
Este script cria (e depois retorna) uma tabela chamada p
e adiciona uma função chamada helloWorld
à tabela, que retorna o texto Hello, world!
que é exibido na página.
Obtendo argumentos[]
Agora, isso não seria muito útil sem ser capaz de enviar argumentos para o script como você faz com as predefinições. O Scribunto armazena os argumentos em um "quadro analisador". Este quadro é basicamente uma tabela que contém algumas funções úteis relacionadas ao analisador de wikitexto, bem como a tabela que contém os argumentos. Ele está disponível como o primeiro parâmetro da função no script e também pode ser recuperado com
função.
mw.getCurrentFrame()
Argumentos diretos[]
Argumentos diretos ou argumentos "normais" são aqueles definidos na função parser
. Aqui está um exemplo que usa argumentos:
{{#invoke:}}
p.helloName = function( f )
local args = f.args
return 'Hello, ' .. args.name .. '!'
end
Which would be called like this:
: Erro de comando: Não existe nenhum módulo "Example".{{#invoke: Example | helloName | name = John Doe }}
Or in a template like this:
, {{#invoke: Example | helloName | name = {{{name|}}} }}
: Predefinição:Scribunto example
{{Scribunto example|name=John Doe}}
Este script atribui a variável f
ao quadro, depois recupera a tabela args do quadro e a atribui à variável args
e, finalmente, retorna a entrada de texto no name
argumento com Hello,
e !
enrolados nele. Argumentos numerados ou anônimos (por exemplo:
) também estão disponíveis, como {{{1}}}
.
args[1]
Os argumentos são strings sempre. Assim como acontece com as predefinições, os argumentos nomeados e numerados têm espaços em branco cortados, enquanto os argumentos anônimos não; e os argumentos especificados, mas sem valor, serão strings vazias, em vez de nil
.
Argumentos pais[]
Com as predefinições que usam uma sub-predefinição, é comum precisar passar argumentos recebidos da predefinição pai para a sub-predefinição.
{{Modelo|{{{1}}}|arg1={{{arg1|}}}|arg2={{{arg2|}}}}}
Código como isso pode ficar bastante confuso e ter problemas de desempenho, pois todos os argumentos serão analisados, independentemente da predefinição a usar.
O Scribunto fornece uma maneira de acessar esses "argumentos pais" diretamente, sem a necessidade de passá-los manualmente. Isto é muito útil, pois quase sempre você estará chamando um script a partir de uma predefinição, e permite que você tenha um número infinito de argumentos possíveis, algo que não era possível com predefinições tradicionais.
Para acessar os argumentos da predefinição pai, você precisa do quadro pai. Usar a função
no quadro atual retorna o quadro da predefinição pai, que pode então ser usada na mesma maneira como o quadro atual.
f:getParent()
O conceito de um "quadro pai" pode ser difícil de pegar no começo, então aqui está o exemplo anterior usando-o:
p.parentHello = function( f )
local args = f:getParent().args
return 'Hello, ' .. args.name .. '!'
end
Porém, não podemos simplesmente chamar isso diretamente, pois não estamos mais lendo os argumentos do quadro atual. Em vez disso, inserimos a função parser
em uma predefinição e a usaremos a partir daí. Observe a falta de argumentos da predefinição sendo transmitidos.{{#invoke:}}
, {{#invoke: Exemple | parentHello }}
: Predefinição:Scribunto example/Parent hello{{Scribunto example/Parent hello|name=John Doe}}
Funciona exatamente como no exemplo anterior, apesar do nome arg não ter sido passado manualmente para a função parser
.
{{#invoke:}}
Para um único argumento como este, não representa uma grande melhoria. Mas pense em predefinições onde muitos argumentos são passados, como uma navbox. Normalmente, essas predefinições têm um limite na quantidade de linhas que suportam, simplesmente porque são tantos argumentos quanto os que foram configurados manualmente. Usar o quadro pai diretamente permitiria uma navbox sem limite de linhas e não precisaria verificar cada linha até seu limite para ver se alguma delas tem um valor. Isso não apenas é mais rápido, mas também produz um código muito melhor e menos repetitivo.
Suportando ambos[]
Usando ambos argumentos diretos e argumentos pai para propósitos separados é bem simples:
p.makeConfigGreeting = function( f )
local args = f.args
local parentArgs = f:getParent().args
return args.greeting .. ', ' .. parentArgs.name .. '!'
end
Os argumentos diretos são usados como uma "configuração" para o tipo de saudação a ser usado, e os argumentos pai são usados para definir quem está sendo saudado.
, {{#invoke: Example | makeConfigGreeting | greeting = Hello }}
: Predefinição:Scribunto example/Config hello{{Scribunto example/Config hello|name=John Doe}}
, {{#invoke: Example | makeConfigGreeting | greeting = G'day }}
: Predefinição:Scribunto example/Config g'day{{Scribunto example/Config g'day|name=John Doe}}
Aqui, existem duas predefinições chamando o mesmo módulo e usando os seus argumentos diretos para configurá-lo para usar saudações diferentes. Em seguida, as predefinições são transcluídas normalmente e os argumentos pais das predefinições são usadas para o nome da pessoa que está sendo cumprimentada.
Para que uma predefinição seja capaz de usar argumentos diretos ou pais, você só precisa verificar se alguma das tabelas possui algum valor:
p.makeFlexableGreeting = function( f )
local args = f.args
local parentArgs = f:getParent().args
for _ in pairs( parentArgs ) do
args = parentArgs
break
end
return args.greeting .. ', ' .. args.name .. '!'
end
Este módulo obtém os argumentos diretos e os argumentos pai e inicia um loop sobre os argumentos pai. Se a tabela args pai estiver vazia, o código dentro do loop não será executado e, portanto, os argumentos diretos permanecerão atribuídos à variável args
. Caso contrário, a variável args
será reatribuída à tabela pai de args e então o loop será interrompido, pois basta saber a existência de um valor.
Para que um módulo seja capaz de usar argumentos diretos e argumentos pai, você poderia simplesmente fazer algo assim:
p.makeFlexableConfigGreeting = function( f )
local args = f.args
local parentArgs = f:getParent().args
local greeting = parentArgs.greeting or args.greeting
local name = parentArgs.name or args.name
return greeting .. ', ' .. name .. '!'
end
O que funciona bem para um exemplo simples como este. No entanto, isso será confuso para módulos com muitos argumentos. A maneira correta de fazer isso é iterar as duas tabelas e mesclá-las em uma:
p.makeMergedGreeting = function( f )
local directArgs = f.args
local parentArgs = f:getParent().args
local args = {}
for _, argType in ipairs{ directArgs, parentArgs } do
for key, val in pairs( argType ) do
args[key] = val
end
end
return args.greeting .. ', ' .. args.name .. '!'
end
Este módulo itera em ambas as tabelas de argumentos, mesclando-as e substituindo os argumentos diretos pelos argumentos pai. Isso é útil para configurações mais complexas, onde uma predefinição define configurações padrão e, em seguida, essas configurações podem ser substituídas conforme apropriado ao transcluir a predefinição.
Ambos os exemplos podem apresentar problemas onde argumentos pais "vazios" substituem os argumentos diretos, já que Lua considera strings vazias como valores "verdadeiros". Isso pode ser corrigido cortando os espaços em branco dos valores (
) e verificando se eles são iguais a uma string vazia antes de definir o valor.
mw.text.trim()
Usando wikitexto[]
Os scripts podem gerar wikitexto, assim como as predefinições comuns, porém apenas o estágio final da parser será executado neste wikitexto. Isso significa que predefinições, funções parser, tags de extensão e qualquer outra coisa que possa gerar wikitexto não serão processados quando usados na saída. Para utilizar esses recursos de maneira adequada, o Scribunto fornece algumas funções para expandi-los até seu wikitexto final. Outras coisas que parecem não funcionar, como variáveis (
) e opções de comportamento (< code>__NOTOC__), do funcionam porque não geram wikitexto e, portanto, não requerem processamento extra.
{{PAGENAME}}
f:preprocess()
[]
f:preprocess()
Esta é a função de pré-processamento mais básica. Ele executa manualmente o estágio de pré-processamento do parser em qualquer texto fornecido. Teoricamente, você poderia executar isso em qualquer wikitexto antes de devolvê-lo para (quase) garantir que todo o wikitexto funcionará. (Observe que ele não converte wikitexto básico em HTML como '''formatação de texto'''
ou [[links]]< /nowiki></code>.) O uso desta função não é recomendado, não apenas por causa das implicações de desempenho da execução desnecessária do parser, e pelo fato de ser um wikitexto completo e, portanto, estar sujeito às mesmas limitações do wikitexto completo, mas porque é uma abordagem de força bruta. Você provavelmente sempre desejará usar uma das funções mais especializadas abaixo.
=== <code><source lang="lua" enclose="none">f:expandTemplate()</source></code> ===
Esta função é mais rápida e menos propensa a erros do que construir manualmente uma transclusão de wikitexto para usar na função acima. Você provavelmente está familiarizado com predefinições como <code><nowiki>{{!}}
, que permitem que caracteres especiais do wikitexto sejam ignorados pelo pré-processador. A função
não está sujeita a essas limitações. Algo assim funcionaria bem:
f:expandTemplate()
f:expandTemplate{ title = 'Example', args = { 'unnamed value 1', 'kittens are cute', named_arg_1 = 'Pipe characters? | No problem! =)' }}
Isso é equivalente ao seguinte, você pode escrever argumentos sem nome de qualquer maneira:
f:expandTemplate{ title = 'Example', args = { [1] = 'unnamed value 1', [2] = 'kittens are cute', named_arg_1 = 'Pipe characters? | No problem! =)' }}
Considerando que com uma transclusão de predefinição normal você teria que fazer isso:
{{Example|arg1=Pipe characters? {{!}} Need escaping! {{=}}(}}
Como seu equivalente wikitexto, isso pode transcluir páginas em qualquer namespace (ou no namespace principal prefixando o título com :
).
f:callParserFunction()
[]
f:callParserFunction()
A mesma coisa da função anterior, mas esta é para funções parser. Não use isso para chamar funções parser onde há um equivalente Lua, como
({{urlencode:}}
). O equivalente Lua será sempre mais rápido e confiável.
mw.uri.encode()
f:extensionTag()
[]
f:extensionTag()
Este é para tags de extensão, como <nowiki/>
(mas não use para isso, use
). Este é praticamente um alias para mw.text.nowiki()
com o f:callParserFunction()
conjunto de funções parser e o conteúdo da tag anexado aos argumentos.
{{#tag}}
Módulos modulares[]
Os módulos podem ser usados em outros módulos usando a função
. Quaisquer variáveis globais no módulo requerido estarão disponíveis globalmente, e o valor de retorno do módulo requerido será retornado por require()
require
.
Aqui está um módulo de exemplo simples que será necessário; observe o uso de variáveis globais.
name = 'John Doe'
constructHello = function( person )
return 'Hello, ' .. person .. '!'
end
Now to require it:
p.acquireGlobals = function( f )
require( 'Module:Example/AcquireGlobals' )
return constructHello( name )
end
: Erro de comando: Não existe nenhum módulo "Example".
{{#invoke: Example | acquireGlobals }}
Embora isso funcione, os globais em geral não são recomendados e, como o módulo necessário não retorna uma tabela de funções, ele não pode ser invocado diretamente, o que o torna menos útil e também difícil de depurar. Recomenda-se usar variáveis locais e retornar qualquer variável que você queira que requeira acesso por scripts, pois isso é mais flexível e facilita a depuração. Formatar scripts require
d de maneira semelhante aos módulos invocados (retornando uma tabela de funções e talvez outros valores) é ainda melhor, pois é bastante simples ajustar o script para poder ser requerido e invocado .
Este script está mais próximo do estilo típico de invocação:
local p = {}
p.name = 'John Doe'
p.constructHello = function( person )
return 'Hello, ' .. person .. '!'
end
return p
p.requiredHello = function( f )
local helloModule = require( 'Module:Example/Hello' )
local name = helloModule.name
return helloModule.constructHello( name )
end
: Erro de comando: Não existe nenhum módulo "Example".
{{#invoke: Example | requiredHello }}
Aqui está uma forma simples de configurar um módulo que pode ser requerido ou chamado por predefinições:
local p = {}
p.constructHello = function( f )
local args = f
if f == mw.getCurrentFrame() then
args = f:getParent().args
else
f = mw.getCurrentFrame()
end
return 'Hello, ' .. args.name .. '!'
end
return p
Isso começa salvando tudo o que f
contém em args
. Depois verifica se f
é um quadro; se for, o módulo está sendo invocado e, portanto, ele obtém os argumentos pai do quadro e os salva em args
. Caso contrário, o módulo está sendo requerido de outro módulo e, portanto, f
contém os argumentos, e é por isso que foi atribuído a args
no início. Ele é então reatribuído ao quadro atual, para que possa ser usado para suas funções úteis, como se tivesse sido invocado. Se o módulo não estiver usando nenhuma das funções do quadro, você poderá pular a reatribuição dele.
Isso poderia então ser chamado a partir de uma predefinição da maneira usual ou de um módulo como este:
p.requiredInvoke = function( f )
local helloModule = require( 'Module:Example/FlexableHello' )
return helloModule.constructHello{ name = 'John Doe' }
end
: Erro de comando: Não existe nenhum módulo "Example".{{#invoke: Example | requiredInvoke }}
Observe como o módulo está passando uma tabela de valores diretamente para a função do módulo que está exigindo.
Carregando grandes tabelas de dados[]
É provável que em algum momento você queira ter uma grande tabela de dados referenciada por vários scripts. Analisar repetidamente esta grande tabela, que sempre será a mesma, centenas de vezes para scripts individuais em uma única página é uma perda de tempo e memória. O Scribunto fornece a função
exatamente para esse propósito. Esta função é semelhante a mw.loadData()
, exceto que o módulo necessário deve retornar uma tabela contendo apenas dados estáticos. Sem funções, sem metatabelas. Quaisquer chamadas subsequentes para require()
para o mesmo módulo em qualquer script na página retornarão a tabela já analisada.
mw.loadData()
A tabela é somente leitura e tentar
a tabela resultará na clonagem do sinalizador somente leitura também; embora atualmente isso resulte em um erro Lua dizendo que a tabela de dados é somente leitura. Se precisar modificar a tabela, você deve voltar para mw.clone()
ou iterar sobre a tabela, construindo uma nova a partir de seus valores.
require()
return {
name = 'John Doe'
-- ... and lots of other data
}
p.bigData = function( f )
local data = mw.loadData( 'Module:Example/Data' )
return 'Hello, ' .. data.name .. '!'
end
: Erro de comando: Não existe nenhum módulo "Example".
{{#invoke: Example | bigData }}
Debugging[]
Na verdade, o Scribunto impedirá o salvamento de um módulo com erros de sintaxe, a menos que a opção "Permitir salvar código com erros" esteja marcada (para módulos em andamento). No entanto, outros erros poderão ser salvos e, portanto, precisarão ser debugados.
Erro de Script[]
Erro de comando: Não existe nenhum módulo "Example". Quando um módulo quebra em uma página, ele gera um erro de script como o acima. Clicando nele mostrará a mensagem de erro e, se for possível executar pelo menos parcialmente, um backtrace para onde o erro ocorreu. A página também será adicionada à categoria de rastreamento páginas com erros de script.
Console de Debug[]
Provavelmente é melhor encontrar os erros antes de salvar suas alterações e, para esse propósito, um console de debug é fornecido abaixo da área de edição. Usar o console não é muito óbvio para começar, pois ele age mais como se o módulo fosse necessário, em vez de invocado, portanto as funções de quadro não funcionarão, a menos que você passe manualmente um quadro para ele.
Para um módulo que não usa nenhuma função de quadro, usar o console de debug é razoavelmente simples. Passe uma tabela contendo a tabela args com o nome "args" para a função:
E para um módulo que já está configurado para ser requesitado é ainda mais fácil:
No entanto, um módulo que não está configurado para ser necessário e usa funções de quadro é um pouco mais difícil de debugar. Primeiro você precisa obter o quadro, depois definir os argumentos no quadro e, em seguida, passar o quadro para a função:
Observe como as variáveis são retidas entre vários comandos do console. O estado só é apagado quando o módulo é alterado (será exibida uma mensagem indicando isso), ou quando o botão limpar for pressionado.
Um módulo que aceita apenas argumentos pai terá que ser editado primeiro para suportar adequadamente a exigência; você também pode comentar temporariamente a lógica original e atribuir os argumentos ao quadro:
Quaisquer chamadas para
e mw.log()
no módulo também serão exibidas no console, antes do valor de retorno, desde que o módulo não encontre nenhum erro.
As funções mw.logObject()
Problemas conhecidos e soluções[]
Comportamento inesperado[]
mw.clone() falha nas tabelas feitas pelo mw.loadData()[]
As tabelas de dados retornadas por mw.loadData()
não podem ser modificadas e isso resultará em um erro. Estranhamente, esse mesmo erro é causado pelo uso do mw.clone()
, provavelmente devido à "mágica" da metatabela usada pelos resultados de loadData()
para torná-los imutáveis. A solução pode ser require()
o módulo de dados, mas isso não é recomendado; mw.loadData()
é um pouco mais eficiente, pois o módulo é carregado apenas uma vez no processo de geração de uma página, em vez de uma vez em cada #invoke
. Se você precisar de uma versão diferente de um módulo de dados (como com um layout diferente), você pode criar e carregar com loadData()
um módulo de dados separado que chama loadData()
no primeiro módulo, cria manualmente a tabela derivada e depois a retorna. (Os módulos de dados podem conter qualquer código, os requisitos são apenas sobre o que eles retornam.)
A ordem de interação em "pairs()" não é especificada[]
Se a sua predefinição é suposta para pegar um número variado de argumentos não numéricos como "image1", "image2", "image3", "image4", "image5", etc., você descobrirá que usar pairs()
para iterar na tabela args não fornecerá esses argumentos na ordem de seus sufixos numéricos. Além disso, não há como acessar a ordem em que os argumentos foram especificados no wikitexto.
O problema com parâmetros de string com sufixo numérico pode ser contornado escrevendo um código personalizado. Por exemplo, estas podem ser usadas como funções de biblioteca:
--[=[
Extraia a sequencia de valores de "someTable" que tenha um prefixo de string "prefix"
e um sufixo integral. Se "allowNumberless" for especificado como verdadeiro, o valor sem um prefixo é tratado como aquele que tem o prefixo 1.
Exemplos:
```
local t = { arg1 = "one", arg2 = "two", arg3 = "three" }
local t2 = p.extractPrefixedSequence(t, "arg")
-- t2 = { "one", "two", "three" }
```
Se "allowNumberless" for verdadeiro e "someTable" tiver um valor sem sufixo e
um com sufixo 1, a função pode retornar qualquer um dos valores.
```
local t = { arg = "one", arg1 = "also one", arg2 = "two", arg3 = "three" }
local t2 = p.extractPrefixedSequence(t, "arg", true)
-- depending on the implementation, t2[1] may be "one" or "also one"
```
A sequência produzida pode conter furos, o que pode causar problemas com o operador #
e pares.
```
local t = { arg1 = "one", arg2 = "two", arg4 = "suddenly four" }
local t2 = p.extractPrefixedSequence(t, "arg")
-- t2 = { "one", "two", nil, "suddenly four" }
```
]=]
function p.extractPrefixedSequence(someTable, prefix, allowNumberless)
local values = {}
if allowNumberless and someTable[prefix] then
values[1] = someTable[prefix]
end
local prefixPattern = "^" .. prefix .. "(%d+)$";
for key, value in pairs(someTable) do
local index = tonumber(key:match(prefixPattern))
if index and index > 0 then
values[index] = value
end
end
return values
end
--[=[
Atua como pares para uma sequencia com um prefixo.
Esse código:
```
for k, v in p.ipairsWithPrefix(someTable, prefix, allowNumberless) do
-- ...
end
```
deve ser o mesmo que esse código:
```
for k, v in ipairs(p.extractPrefixedSequence(someTable, prefix, allowNumberless)) do
-- ...
end
```
no entanto, no caso extremo, quando "allowNumberless" for verdadeiro, e tanto um valor sem número quanto um valor com sufixo 1 estiver presente, as funções podem retornar valores diferentes para o índice 1.
]=]
function p.ipairsWithPrefix(someTable, prefix, allowNumberless)
local i = 0
return function()
i = i + 1
local value = someTable[prefix .. tostring(i)]
if i == 1 and allowNumberless and someTable[prefix] ~= nil then
value = someTable[prefix]
end
if value ~= nil then
return i, value
end
end
end
O operador # tem comportamento não especificado para sequências com "buracos"[]
Se uma tabela de sequência tiver "buracos" – valores nulos precedendo valores não nulos, – o operador #
poderá tratar qualquer "buraco" como o final da sequência. Isso pode ser um problema, por exemplo, se você estiver aplicando-o à tabela de argumentos de um módulo depois de usar uma biblioteca de processamento de argumentos. Essas bibliotecas geralmente substituem argumentos insubstanciais (vazios ou somente espaços em branco) por valores nulos.
A função iteradora ipairs
irá parar no primeiro buraco.
Se você precisar usar #
ou ipairs
em uma sequência que pode conter lacunas, você também pode querer usar table.maxn()
em seu código. Esta função retornará o índice numérico mais alto com um valor diferente de nulo associado a ele. Se necessário, você também pode preencher as lacunas com valores de espaço reservado depois de iterar na tabela usando a função maxn
para obter o índice final.
local leaky_table = {1, 2, 3, 4, 5, nil, 7, 8, 9, nil, 11, 12}
for index = 1, table.maxn(leaky_table) do
if leaky_table[index] == nil then
leaky_table[index] = index -- insert whatever you need, of course
end
end
-- leaky_table is now {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
Detalhes de implementação altamente técnicos: A razão por trás do comportamento não especificado de #
é provavelmente que as tabelas Lua possuem internamente uma parte de "sequência" para arrays de números e uma parte de "hash" para todos os outros dados. Usar apenas a parte "sequência" para todos os índices numéricos faria com que matrizes esparsas (especialmente matrizes muito esparsas com índices muito grandes) fossem ineficientes em termos de memória, e é por isso que depois de alguns "buracos" os elementos subsequentes indexados por um número são armazenados na "hash" parte em vez disso. Por exemplo, { [1000000000] = "not Google Chrome" }
não aloca gigabytes de espaço vazio.
Os números Lua são flutuantes de precisão dupla e arredondam silenciosamente números inteiros muito grandes[]
Predefinição:See
Na versão do Lua usada pelo Scribunto, todos os números não são do tipo integral, mas de um tipo chamado número de ponto flutuante de precisão dupla (ou simplesmente double
). Este tipo tem peculiaridades que não serão óbvias para programadores Lua iniciantes. Por exemplo, depois de algum ponto, nem todos os números inteiros podem ser armazenados como duplos. Para duplas, o menor número inteiro positivo não representável é 9 007 199 254 740 992. Tais integrações são arredondadas, e como resultado, a expressão tonumber("9007199254740993") == tonumber("9007199254740992")
é avaliada como true
. Isso normalmente não é um problema, mas, por exemplo, concatenar muitos números inteiros grandes e tentar interpretar o resultado como um número pode causar um comportamento inesperado.
Dificilmente existe uma solução melhor, mas pode ser útil estar ciente do problema e escrever os seus módulos para que eles não o causem. Embora o Lua 5.3 suporte números inteiros de 64 bits com a capacidade de representar exatamente mais números inteiros, o Scribunto é baseado em 5.1 e é improvável que implemente completamente todos os recursos do 5.2.
binser não deve ser usado para armazenar números[]
Se a sua wiki tiver a biblioteca "binser" habilitada, você não deve tentar usar callParserFunction
com "Extension:Variables" #vardefine
enquanto passa a saída da serialização do binser função. "Bin" em "binser" significa "binário" e a serialização pode resultar em qualquer byte. Ao mesmo tempo, #vardefine
espera um texto adequado. Por exemplo, um usuário teve um erro ao serializar 5
usando binser resultando em um caractere de espaço, que callParserFunction
, como no wikitexto típico, foi cortado em um valor vazio. A serialização do número 256
resultou em bytes que não são UTF-8 válidos, e o texto armazenado acabou sendo dois caracteres de substituição.
Como solução, use o mw.text.jsonEncode
e mw.text.jsonDecode
para lidar com variáveis. Isso funciona não apenas com tabelas, mas com outros tipos (como números e strings), e o valor decodificado já deve ser do tipo correto.
Performance[]
mw.text.split está muito lento[]
Em alguns testes, essa função acabou sendo 60 vezes mais lenta que uma reimplementação personalizada. Se possível, use a biblioteca string
para dividir as suas strings usando padrões Lua.
Por exemplo:
--[[
Splits a string `str` using a pattern `pattern` and returns a sequence
table with the parts.
Much faster than `mw.text.split`, which it is inspired by, but does
not work if the pattern needs to be Unicode-aware.
]]
function split(str, pattern)
local out = {}
local i = 1
local split_start, split_end = string.find(str, pattern, i)
while split_start do
out[#out+1] = string.sub(str, i, split_start - 1)
i = split_end + 1
split_start, split_end = string.find(str, pattern, i)
end
out[#out+1] = string.sub(str, i)
return out
end
mw.text.trim é lento[]
Semelhante ao exemplo acima, a função trim
também é bastante lenta. Além disso, consome muita memória e pode resultar em erros do Scribunto se usado em strings muito grandes. Se você não precisa lidar com espaços em branco não-ASCII, você pode usar algo assim:
local function trim( s )
return (s:gsub( '^[\t\r\n\f ]+', '' ):gsub( '[\t\r\n\f ]+$', '' ))
end
Observe que esta implementação usa intencionalmente duas chamadas gsub
em vez de uma com um grupo de captura. Os testes mostraram que a versão com um grupo de captura usaria mais memória e teria um desempenho um pouco menor. A camada extra de parênteses ao redor da expressão é relevante porque gsub
retorna dois valores, e a função deve retornar apenas um.
Erro Lua: Internal error: O interpretador foi encerrado com um sinal "24".[]
Esse problema ocorre quando o interpretador está sendo executado por muito tempo e é encerrado. Por exemplo, ter um loop infinito no seu módulo pode causar isso.
Este erro também pode ocorrer quando módulos longos ou que consomem muitos recursos são executados e o servidor onde a wiki está localizada está sob carga pesada. Talvez seja possível evitar esse erro melhorando o módulo para que ele funcione mais rápido.
Destacamento de Sintaxe[]
A ativação do Scribunto também permitirá o destaque de sintaxe de páginas css e js no namespace MediaWiki (MediaWiki:common.css, etc). Para ativar o destaque de sintaxe no namespace do Módulo, uma configuração deve ser definida na wiki ($wgScribuntoUseGeSHi
). Um gerente de wiki pode fazer isso por você.
Veja também[]