In questo momento è solo un'idea folle che ho avuto, ma sono stato in grado di implementare il codice e farlo funzionare correttamente. Non sono del tutto sicuro di quali potrebbero essere i casi d'uso.
Ciò che questo codice fa è creare un nuovo file di script Lua nella directory del progetto. ScriptWriter accetta come argomenti il nome del file, una tabella contenente tutti gli argomenti che lo script deve eseguire al momento della creazione e una tabella contenente le variabili di istanza da creare per impostazione predefinita. Il mio piano è estendere questo codice per creare nuove funzioni basate sugli input inviati durante la sua creazione.
Ciò che rende questo interessante è che il nuovo file è sia generato che caricato dinamicamente al volo. In teoria è possibile ottenere questo codice per generare e caricare qualsiasi script immaginabile. Un caso d'uso che posso pensare è un'intelligenza artificiale che crea script per mappare le sue funzioni e crea nuovi script per nuove situazioni o ambienti. A questo punto, questo è tutto teorico, però.
Ecco il codice di test che sta creando il nuovo script e quindi lo carica immediatamente e chiama da esso le funzioni:
function Card:doScriptWriterThing()
local scriptName = "ScriptIAmMaking"
local scripter = scriptWriter:new(scriptName, {"argumentName"}, {name = "'test'", one = 1})
scripter:makeFileForLoadedSettings()
local loadedScript = require (scriptName)
local scriptInstance = loadedScript:new("sayThis")
print(scriptInstance:get_name()) --will print test
print(scriptInstance:get_one()) -- will print 1
scriptInstance:set_one(10000)
print(scriptInstance:get_one()) -- will print 10000
print(scriptInstance:get_argumentName()) -- will print sayThis
scriptInstance:set_argumentName("saySomethingElse")
print(scriptInstance:get_argumentName()) --will print saySomethingElse
end
Ecco ScriptWriter.lua
local ScriptWriter = {}
local twoSpaceIndent = " "
local equalsWithSpaces = " = "
local newLine = "\n"
--scriptNameToCreate must be a string
--argumentsForNew and instanceVariablesToCreate must be tables and not nil
function ScriptWriter:new(scriptNameToCreate, argumentsForNew, instanceVariablesToCreate)
local instance = setmetatable({}, { __index = self })
instance.name = scriptNameToCreate
instance.newArguments = argumentsForNew
instance.instanceVariables = instanceVariablesToCreate
instance.stringList = {}
return instance
end
function ScriptWriter:makeFileForLoadedSettings()
self:buildInstanceMetatable()
self:buildInstanceCreationMethod()
self:buildSettersAndGetters()
self:buildReturn()
self:writeStringsToFile()
end
--very first line of any script that will have instances
function ScriptWriter:buildInstanceMetatable()
table.insert(self.stringList, "local " .. self.name .. " = {}" .. newLine)
table.insert(self.stringList, newLine)
end
--every script made this way needs a new method to create its instances
function ScriptWriter:buildInstanceCreationMethod()
--new() function declaration
table.insert(self.stringList, ("function " .. self.name .. ":new("))
self:buildNewArguments()
table.insert(self.stringList, ")" .. newLine)
--first line inside :new() function
table.insert(self.stringList, twoSpaceIndent .. "local instance = setmetatable({}, { __index = self })" .. newLine)
--add designated arguments inside :new()
self:buildNewArgumentVariables()
--create the instance variables with the loaded values
for key,value in pairs(self.instanceVariables) do
table.insert(self.stringList, twoSpaceIndent .. "instance." .. key .. equalsWithSpaces .. value .. newLine)
end
--close the :new() function
table.insert(self.stringList, twoSpaceIndent .. "return instance" .. newLine)
table.insert(self.stringList, "end" .. newLine)
table.insert(self.stringList, newLine)
end
function ScriptWriter:buildNewArguments()
--if there are arguments for :new(), add them
for key,value in ipairs(self.newArguments) do
table.insert(self.stringList, value)
table.insert(self.stringList, ", ")
end
if next(self.newArguments) ~= nil then --makes sure the table is not empty first
table.remove(self.stringList) --remove the very last element, which will be the extra ", "
end
end
function ScriptWriter:buildNewArgumentVariables()
--add the designated arguments to :new()
for key, value in ipairs(self.newArguments) do
table.insert(self.stringList, twoSpaceIndent .. "instance." .. value .. equalsWithSpaces .. value .. newLine)
end
end
--the instance variables need separate code because their names have to be the key and not the argument name
function ScriptWriter:buildSettersAndGetters()
for key,value in ipairs(self.newArguments) do
self:buildArgumentSetter(value)
self:buildArgumentGetter(value)
table.insert(self.stringList, newLine)
end
for key,value in pairs(self.instanceVariables) do
self:buildInstanceVariableSetter(key, value)
self:buildInstanceVariableGetter(key, value)
table.insert(self.stringList, newLine)
end
end
--code for arguments passed in
function ScriptWriter:buildArgumentSetter(variable)
table.insert(self.stringList, "function " .. self.name .. ":set_" .. variable .. "(newValue)" .. newLine)
table.insert(self.stringList, twoSpaceIndent .. "self." .. variable .. equalsWithSpaces .. "newValue" .. newLine)
table.insert(self.stringList, "end" .. newLine)
end
function ScriptWriter:buildArgumentGetter(variable)
table.insert(self.stringList, "function " .. self.name .. ":get_" .. variable .. "()" .. newLine)
table.insert(self.stringList, twoSpaceIndent .. "return " .. "self." .. variable .. newLine)
table.insert(self.stringList, "end" .. newLine)
end
--code for instance variable values passed in
function ScriptWriter:buildInstanceVariableSetter(key, variable)
table.insert(self.stringList, "function " .. self.name .. ":set_" .. key .. "(newValue)" .. newLine)
table.insert(self.stringList, twoSpaceIndent .. "self." .. key .. equalsWithSpaces .. "newValue" .. newLine)
table.insert(self.stringList, "end" .. newLine)
end
function ScriptWriter:buildInstanceVariableGetter(key, variable)
table.insert(self.stringList, "function " .. self.name .. ":get_" .. key .. "()" .. newLine)
table.insert(self.stringList, twoSpaceIndent .. "return " .. "self." .. key .. newLine)
table.insert(self.stringList, "end" .. newLine)
end
--last line of any script that will have instances
function ScriptWriter:buildReturn()
table.insert(self.stringList, "return " .. self.name)
end
function ScriptWriter:writeStringsToFile()
local fileName = (self.name .. ".lua")
file = io.open(fileName, 'w')
for key,value in ipairs(self.stringList) do
file:write(value)
end
file:close()
end
return ScriptWriter
Ed ecco ciò che il codice fornito genererà:
local ScriptIAmMaking = {}
function ScriptIAmMaking:new(argumentName)
local instance = setmetatable({}, { __index = self })
instance.argumentName = argumentName
instance.name = 'test'
instance.one = 1
return instance
end
function ScriptIAmMaking:set_argumentName(newValue)
self.argumentName = newValue
end
function ScriptIAmMaking:get_argumentName()
return self.argumentName
end
function ScriptIAmMaking:set_name(newValue)
self.name = newValue
end
function ScriptIAmMaking:get_name()
return self.name
end
function ScriptIAmMaking:set_one(newValue)
self.one = newValue
end
function ScriptIAmMaking:get_one()
return self.one
end
return ScriptIAmMaking
Tutto questo viene generato con queste chiamate:
local scripter = scriptWriter:new(scriptName, {"argumentName"}, {name = "'test'", one = 1})
scripter:makeFileForLoadedSettings()
Non sono sicuro che sia corretto che ciò possa essere utile in determinate situazioni. Quello che cerco è un feedback sulla leggibilità del codice e sulle best practice Lua. Mi piacerebbe anche sapere se questo approccio è valido e se il modo in cui ho fatto le cose sarà estensibile.