d
This commit is contained in:
@@ -1,373 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser(prog='generate v1')
|
||||
parser.add_argument('module', type=str,
|
||||
help='the name of the module to generate')
|
||||
parser.add_argument('-i', '--input', type=str,
|
||||
help='The path and name of the input file')
|
||||
parser.add_argument('-o', '--output', type=str,
|
||||
help='The path of the output files')
|
||||
|
||||
args = parser.parse_args()
|
||||
DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
MODULE = args.module
|
||||
|
||||
def isNotKeyword(str):
|
||||
keywords = [
|
||||
"core",
|
||||
"ex",
|
||||
"recent",
|
||||
"service",
|
||||
"set",
|
||||
"toggle",
|
||||
"toggleNil",
|
||||
"vm",
|
||||
"$vm",
|
||||
"world",
|
||||
]
|
||||
return str not in keywords
|
||||
|
||||
class CoreAction:
|
||||
def __init__(self, func, sink):
|
||||
self.func = func
|
||||
self.sink = sink
|
||||
|
||||
def code(self):
|
||||
return f"""
|
||||
p.ctrl.m
|
||||
.compactMap {{ {self.func}($0) }}
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink {{ [weak core = p.core] v in {self.sink} }}
|
||||
.store(in: &p.core.subscriptions)
|
||||
""".replace("\n\n", "\n")
|
||||
|
||||
class CorePipe:
|
||||
def __init__(self, name, props):
|
||||
self.name = name
|
||||
self.props = props
|
||||
self.resetMethod()
|
||||
self.resetDbg()
|
||||
self.resetSrc()
|
||||
self.resetExName()
|
||||
self.resetSteps()
|
||||
|
||||
def code(self):
|
||||
return f"""
|
||||
p.ctrl.{self.method}(
|
||||
dbg: "{self.dbg}",
|
||||
sub: &p.core.subscriptions,
|
||||
{self.src}.eraseToAnyPublisher(),
|
||||
{self.steps}
|
||||
)
|
||||
""".replace("\n\n", "\n")
|
||||
|
||||
def resetDbg(self):
|
||||
capitals = [l for l in self.name if l.isupper()]
|
||||
# Нет заглавных.
|
||||
if len(capitals) == 0:
|
||||
self.dbg = self.name
|
||||
return
|
||||
# Есть заглавные.
|
||||
firstCap = self.name.find(capitals[0])
|
||||
self.dbg = self.name[:firstCap] + "".join(capitals)
|
||||
|
||||
def resetExName(self):
|
||||
firstLetter = self.name[0].capitalize()
|
||||
self.exName = f"""ex{firstLetter}{self.name[1:]}"""
|
||||
|
||||
def resetMethod(self):
|
||||
if "toggle" in self.props:
|
||||
self.method = "pipe"
|
||||
else:
|
||||
self.method = "pipeValue"
|
||||
|
||||
def resetSrc(self):
|
||||
if "core" in self.props:
|
||||
self.src = "p.core." + self.name
|
||||
elif "world" in self.props:
|
||||
self.src = "p.world." + self.name
|
||||
elif "vm" in self.props:
|
||||
self.src = "p.core.vm." + self.name
|
||||
elif "$vm" in self.props:
|
||||
self.src = "p.core.vm.$" + self.name
|
||||
else:
|
||||
# Если это что-то неизвестное заранее, то ищем строку,
|
||||
# отличную от известных ключевых слов.
|
||||
self.src = next(filter(isNotKeyword, self.props))
|
||||
|
||||
def resetSteps(self):
|
||||
if "recent" and "ex" in self.props:
|
||||
self.steps = f"""
|
||||
{{
|
||||
$0.{self.name}.value = $1
|
||||
$0.{self.name}.isRecent = true
|
||||
}},
|
||||
{{
|
||||
$0.{self.name}.isRecent = false
|
||||
$0.{self.exName} = $1
|
||||
}}
|
||||
"""
|
||||
elif "recent" in self.props:
|
||||
self.steps = f"""
|
||||
{{
|
||||
$0.{self.name}.value = $1
|
||||
$0.{self.name}.isRecent = true
|
||||
}},
|
||||
{{ m, _ in m.{self.name}.isRecent = false }}
|
||||
"""
|
||||
elif "set" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = $1 }}
|
||||
"""
|
||||
elif "toggle" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = true }},
|
||||
{{ $0.{self.name} = false }}
|
||||
"""
|
||||
elif "toggleNil" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = $1 }},
|
||||
{{ m, _ in m.{self.name} = nil }}
|
||||
"""
|
||||
|
||||
class ServiceAction:
|
||||
def __init__(self, func, sink):
|
||||
self.func = func
|
||||
self.sink = sink
|
||||
|
||||
def code(self):
|
||||
return f"""
|
||||
sp.ctrl.m
|
||||
.compactMap {{ {self.func}($0) }}
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink {{ v in {self.sink} }}
|
||||
.store(in: &sp.service.subscriptions)
|
||||
""".replace("\n\n", "\n")
|
||||
|
||||
class ServicePipe:
|
||||
def __init__(self, name, props):
|
||||
self.name = name
|
||||
self.props = props
|
||||
self.resetMethod()
|
||||
self.resetDbg()
|
||||
self.resetSrc()
|
||||
self.resetExName()
|
||||
self.resetSteps()
|
||||
|
||||
def code(self):
|
||||
return f"""
|
||||
sp.ctrl.{self.method}(
|
||||
dbg: "{self.dbg}",
|
||||
{self.src}.eraseToAnyPublisher(),
|
||||
{self.steps}
|
||||
)
|
||||
""".replace("\n\n", "\n")
|
||||
|
||||
def resetDbg(self):
|
||||
capitals = [l for l in self.name if l.isupper()]
|
||||
# Нет заглавных.
|
||||
if len(capitals) == 0:
|
||||
self.dbg = self.name
|
||||
return
|
||||
# Есть заглавные.
|
||||
firstCap = self.name.find(capitals[0])
|
||||
self.dbg = self.name[:firstCap] + "".join(capitals)
|
||||
|
||||
def resetExName(self):
|
||||
firstLetter = self.name[0].capitalize()
|
||||
self.exName = f"""ex{firstLetter}{self.name[1:]}"""
|
||||
|
||||
def resetMethod(self):
|
||||
if "toggle" in self.props:
|
||||
self.method = "pipe"
|
||||
else:
|
||||
self.method = "pipeValue"
|
||||
|
||||
def resetSrc(self):
|
||||
if "service" in self.props:
|
||||
self.src = "sp.service." + self.name
|
||||
elif "world" in self.props:
|
||||
self.src = "sp.world." + self.name
|
||||
else:
|
||||
# Если это и не сервис, и не мир, то
|
||||
# ищем строку, отличную от известных ключевых слов.
|
||||
self.src = next(filter(isNotKeyword, self.props))
|
||||
|
||||
def resetSteps(self):
|
||||
if "recent" and "ex" in self.props:
|
||||
self.steps = f"""
|
||||
{{
|
||||
$0.{self.name}.value = $1
|
||||
$0.{self.name}.isRecent = true
|
||||
}},
|
||||
{{
|
||||
$0.{self.name}.isRecent = false
|
||||
$0.{self.exName} = $1
|
||||
}}
|
||||
"""
|
||||
elif "recent" in self.props:
|
||||
self.steps = f"""
|
||||
{{
|
||||
$0.{self.name}.value = $1
|
||||
$0.{self.name}.isRecent = true
|
||||
}},
|
||||
{{ m, _ in m.{self.name}.isRecent = false }}
|
||||
"""
|
||||
elif "set" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = $1 }}
|
||||
"""
|
||||
elif "toggle" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = true }},
|
||||
{{ $0.{self.name} = false }}
|
||||
"""
|
||||
elif "toggleNil" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = $1 }},
|
||||
{{ m, _ in m.{self.name} = nil }}
|
||||
"""
|
||||
|
||||
class State:
|
||||
isCoreAction = False
|
||||
isCoreController = False
|
||||
isServiceAction = False
|
||||
isServiceController = False
|
||||
|
||||
def generatePlatform(coreActions, corePipes, serviceActions, servicePipes):
|
||||
corePlatform = ""
|
||||
if len(coreActions) > 0 or len(corePipes) > 0:
|
||||
corePlatform = f"""
|
||||
static func setupPlatform(_ p: CoreParameters) {{
|
||||
{coreActions}
|
||||
{corePipes}
|
||||
}}
|
||||
"""
|
||||
|
||||
return f"""
|
||||
// ВНИМАНИЕ: Сгенерировано автоматом, не менять руками!
|
||||
|
||||
import Foundation
|
||||
|
||||
extension {MODULE} {{
|
||||
enum SectionGenerated {{
|
||||
{corePlatform}
|
||||
static func setupPlatform(_ sp: ServiceParameters) {{
|
||||
{serviceActions}
|
||||
{servicePipes}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
|
||||
def generateCoreAction(ln):
|
||||
parts = ln.split(": ")
|
||||
if len(parts) != 2:
|
||||
return None
|
||||
func = parts[0].lstrip()
|
||||
sink = parts[1].rstrip()
|
||||
action = CoreAction(func, sink)
|
||||
return action.code()
|
||||
|
||||
def generateCorePipe(ln):
|
||||
parts = ln.split(": [")
|
||||
if len(parts) != 2:
|
||||
return None
|
||||
name = parts[0].lstrip()
|
||||
props = parts[1][:-1].split(", ")
|
||||
pipe = CorePipe(name, props)
|
||||
return pipe.code()
|
||||
|
||||
def generateServiceAction(ln):
|
||||
parts = ln.split(": ")
|
||||
if len(parts) != 2:
|
||||
return None
|
||||
func = parts[0].lstrip()
|
||||
sink = parts[1].rstrip()
|
||||
action = ServiceAction(func, sink)
|
||||
return action.code()
|
||||
|
||||
def generateServicePipe(ln):
|
||||
parts = ln.split(": [")
|
||||
if len(parts) != 2:
|
||||
return None
|
||||
name = parts[0].lstrip()
|
||||
props = parts[1][:-1].split(", ")
|
||||
pipe = ServicePipe(name, props)
|
||||
return pipe.code()
|
||||
|
||||
def readFile(fileName):
|
||||
lines = []
|
||||
with open(fileName) as file:
|
||||
for line in file:
|
||||
lines.append(line.rstrip())
|
||||
return lines
|
||||
|
||||
def resetState(ln):
|
||||
if ln.startswith("ca:"):
|
||||
state.isCoreAction = True
|
||||
state.isCoreController = False
|
||||
state.isServiceAction = False
|
||||
state.isServiceController = False
|
||||
elif ln.startswith("cp:"):
|
||||
state.isCoreAction = False
|
||||
state.isCoreController = True
|
||||
state.isServiceAction = False
|
||||
state.isServiceController = False
|
||||
elif ln.startswith("sa:"):
|
||||
state.isCoreAction = False
|
||||
state.isCoreController = False
|
||||
state.isServiceAction = True
|
||||
state.isServiceController = False
|
||||
elif ln.startswith("sp:"):
|
||||
state.isCoreAction = False
|
||||
state.isCoreController = False
|
||||
state.isServiceAction = False
|
||||
state.isServiceController = True
|
||||
return state
|
||||
|
||||
def saveFile(fileName, data):
|
||||
with open(fileName, "w") as file:
|
||||
file.write(data)
|
||||
|
||||
def validateVersion(ln):
|
||||
if ln.startswith("version: 1") == False:
|
||||
print("ERROR: Invalid version")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Main
|
||||
state = State()
|
||||
print(f"Generating platform for module '{MODULE}'...")
|
||||
fileIn = args.input or f"{DIR}/../../../Modules/{MODULE}/{MODULE}.yml"
|
||||
fileOut = args.output or f"{DIR}/../../../Modules/{MODULE}/src"
|
||||
fileOut = os.path.join(fileOut, f'{MODULE}.SectionGenerated.swift')
|
||||
lines = readFile(fileIn)
|
||||
validateVersion(lines[0])
|
||||
coreActions = ""
|
||||
corePipes = ""
|
||||
serviceActions = ""
|
||||
servicePipes = ""
|
||||
for ln in lines:
|
||||
st = resetState(ln)
|
||||
if st.isCoreAction:
|
||||
action = generateCoreAction(ln)
|
||||
if action:
|
||||
coreActions += action
|
||||
if st.isCoreController:
|
||||
pipe = generateCorePipe(ln)
|
||||
if pipe:
|
||||
corePipes += pipe
|
||||
if st.isServiceAction:
|
||||
action = generateServiceAction(ln)
|
||||
if action:
|
||||
serviceActions += action
|
||||
if st.isServiceController:
|
||||
pipe = generateServicePipe(ln)
|
||||
if pipe:
|
||||
servicePipes += pipe
|
||||
result = generatePlatform(coreActions, corePipes, serviceActions, servicePipes)
|
||||
saveFile(fileOut, result)
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
from generation.generateStructure import *
|
||||
from generation.shortenName import *
|
||||
from generation.Result import *
|
||||
from parsing.parseLines import *
|
||||
from parsing.Structure import *
|
||||
|
||||
DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
|
||||
# Импорт из общей для всех генераторов директории.
|
||||
sys.path.append(f"{DIR}/../common")
|
||||
from modulePaths import *
|
||||
from readFile import *
|
||||
|
||||
parser = ArgumentParser(prog='generate v2')
|
||||
parser.add_argument('module', type=str,
|
||||
help='the name of the module to generate')
|
||||
parser.add_argument('-i', '--input', type=str,
|
||||
help='The path and name of the input file')
|
||||
parser.add_argument('-o', '--output', type=str,
|
||||
help='The path of the output files')
|
||||
parser.add_argument('-s', '--source', type=str,
|
||||
help='The path of the source files')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
(PATH, MODULE) = modulePaths(args.module)
|
||||
|
||||
print(f"Generating platform for module '{PATH}'...")
|
||||
|
||||
FILE_IN = args.input or f"{DIR}/../../../Modules/{PATH}/{MODULE}.yml"
|
||||
DIR_OUT = args.output or f"{DIR}/../../../Modules/{PATH}/src/"
|
||||
FILE_OUT = os.path.join(DIR_OUT, f"{MODULE}.Generated.swift")
|
||||
FILE_OUT_V1 = os.path.join(DIR_OUT, f"{MODULE}.SectionGenerated.swift")
|
||||
MODULE_SRC = args.source or f"{DIR}/../../../Modules/{PATH}/src"
|
||||
|
||||
# Удаляем первую версию генерированного файла при его наличии.
|
||||
if os.path.isfile(FILE_OUT_V1):
|
||||
os.remove(FILE_OUT_V1)
|
||||
|
||||
# Читаем файл и разбираем его на ключи-значения.
|
||||
lines = readFile(FILE_IN)
|
||||
structure = Structure()
|
||||
parseLines(lines, structure)
|
||||
|
||||
# Генерируем код.
|
||||
result = Result(DIR, PATH, MODULE, readFile, shortenName, MODULE_SRC, structure)
|
||||
generateStructure(result)
|
||||
|
||||
# Сохраняем файл.
|
||||
with open(FILE_OUT, "w") as file:
|
||||
file.write(result.file)
|
||||
@@ -1,29 +0,0 @@
|
||||
class Result:
|
||||
def __init__(self, dir, path, module, readFile, shortenName, src, structure):
|
||||
self.dir = dir
|
||||
self.path = path
|
||||
self.module = module
|
||||
self.readFile = readFile
|
||||
self.shortenName = shortenName
|
||||
self.src = src
|
||||
self.structure = structure
|
||||
|
||||
self.contextFields = ""
|
||||
self.core = ""
|
||||
self.coreSectionGenerated = ""
|
||||
self.coreSectionGeneratedActions = ""
|
||||
self.coreSectionGeneratedPipes = ""
|
||||
self.coreSectionsDestroy = ""
|
||||
self.coreSectionsSetup = ""
|
||||
self.coreVM = ""
|
||||
self.coreWindow = ""
|
||||
self.file = ""
|
||||
self.imports = ""
|
||||
self.modelFields = ""
|
||||
self.serviceCore = ""
|
||||
self.serviceSectionGenerated = ""
|
||||
self.serviceSectionGeneratedActions = ""
|
||||
self.serviceSectionGeneratedPipes = ""
|
||||
self.serviceSections = ""
|
||||
self.worldFields = ""
|
||||
self.worldParameters = ""
|
||||
@@ -1,10 +0,0 @@
|
||||
from generation.isPipeRecent import *
|
||||
|
||||
def fieldFormat(fmtPlain, fmtRecent, name, structure):
|
||||
fmt = fmtPlain
|
||||
if (
|
||||
isPipeRecent(name, structure.core) or
|
||||
isPipeRecent(name, structure.service)
|
||||
):
|
||||
fmt = fmtRecent
|
||||
return fmt
|
||||
@@ -1,18 +0,0 @@
|
||||
from generation.fieldFormat import *
|
||||
|
||||
def generateContextFields(c):
|
||||
fileName = f"{c.dir}/templates/context-field"
|
||||
lines = c.readFile(fileName)
|
||||
fmtPlain = lines[0]
|
||||
fmtRecent = lines[1]
|
||||
fields = []
|
||||
|
||||
for key in c.structure.model.fields:
|
||||
values = c.structure.model.fields[key]
|
||||
fmt = fieldFormat(fmtPlain, fmtRecent, key, c.structure)
|
||||
ln = fmt \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", values[0])
|
||||
fields.append(ln)
|
||||
|
||||
c.contextFields = "\n".join(fields)
|
||||
@@ -1,7 +0,0 @@
|
||||
def generateCore(c):
|
||||
fileName = f"{c.dir}/templates/core"
|
||||
lines = c.readFile(fileName)
|
||||
c.core = "\n".join(lines)
|
||||
|
||||
fileName = f"{c.dir}/templates/service-core"
|
||||
c.serviceCore = c.readFile(fileName)[0]
|
||||
@@ -1,4 +0,0 @@
|
||||
def generateCoreSectionGenerated(c):
|
||||
fileName = f"{c.dir}/templates/core-section-generated"
|
||||
lines = c.readFile(fileName)
|
||||
c.coreSectionGenerated = "\n".join(lines)
|
||||
@@ -1,37 +0,0 @@
|
||||
from generation.sectionGeneratedActionShouldLoad import *
|
||||
|
||||
def generateCoreSectionGeneratedActions(c):
|
||||
base = f"{c.dir}/templates/core-section-generated-action"
|
||||
fmtCommon = c.readFile(base)
|
||||
fmtInstant = c.readFile(f"{base}-instant")
|
||||
|
||||
for key in c.structure.core.actions:
|
||||
value = c.structure.core.actions[key]
|
||||
|
||||
# Шаблонные действия.
|
||||
if value == "":
|
||||
# shouldLoad*.
|
||||
shouldLoad = "shouldLoad"
|
||||
if key.startswith(shouldLoad):
|
||||
c.coreSectionGeneratedActions += sectionGeneratedActionShouldLoad(key, "core", c)
|
||||
continue
|
||||
|
||||
continue
|
||||
|
||||
|
||||
output = ""
|
||||
|
||||
action = key
|
||||
template = fmtCommon
|
||||
# Действие без receive(on:)
|
||||
if action.startswith("🚀"):
|
||||
action = action[1:]
|
||||
template = fmtInstant
|
||||
|
||||
for fmt in template:
|
||||
ln = fmt \
|
||||
.replace("%SHOULD%", action) \
|
||||
.replace("%SINK%", value)
|
||||
output += ln + "\n"
|
||||
|
||||
c.coreSectionGeneratedActions += output
|
||||
@@ -1,19 +0,0 @@
|
||||
from generation.sectionFunctions import *
|
||||
from generation.sectionNames import *
|
||||
|
||||
def generateCoreSectionsDestroy(c):
|
||||
fileName = f"{c.dir}/templates/core-section"
|
||||
fmt = c.readFile(fileName)[0]
|
||||
items = []
|
||||
|
||||
sections = sectionNames(c)
|
||||
for name in sections:
|
||||
# Пропускаем секции, не относящиеся к удалению ядра.
|
||||
funcs = sectionFunctions(name, c)
|
||||
if "destroyCore" not in funcs:
|
||||
continue
|
||||
|
||||
ln = fmt.replace("%NAME%", name)
|
||||
items.append(ln)
|
||||
|
||||
c.coreSectionsDestroy = "\n".join(items)
|
||||
@@ -1,27 +0,0 @@
|
||||
from generation.hasSectionGenerated import *
|
||||
from generation.sectionFunctions import *
|
||||
from generation.sectionNames import *
|
||||
|
||||
def generateCoreSectionsSetup(c):
|
||||
fileName = f"{c.dir}/templates/core-section"
|
||||
lines = c.readFile(fileName)
|
||||
fmtCore = lines[1]
|
||||
fmtPlatform = lines[2]
|
||||
items = []
|
||||
|
||||
sections = sectionNames(c)
|
||||
for name in sections:
|
||||
# Пропускаем секции, не относящиеся к установке ядра.
|
||||
funcs = sectionFunctions(name, c)
|
||||
if "setupCore" not in funcs:
|
||||
continue
|
||||
|
||||
ln = fmtCore.replace("%NAME%", name)
|
||||
items.append(ln)
|
||||
|
||||
# Генерированная секция.
|
||||
# Должна быть добавлена последней.
|
||||
if hasSectionGenerated(c.structure.core):
|
||||
items.append(fmtPlatform)
|
||||
|
||||
c.coreSectionsSetup = "\n".join(items)
|
||||
@@ -1,11 +0,0 @@
|
||||
from generation.sectionNames import *
|
||||
|
||||
def generateCoreWindow(c):
|
||||
fileName = f"{c.dir}/templates/core-window"
|
||||
lines = c.readFile(fileName)
|
||||
fmtV = lines[0]
|
||||
fmtW = lines[1]
|
||||
sections = sectionNames(c)
|
||||
if "UI" in sections:
|
||||
c.coreVM = fmtV
|
||||
c.coreWindow = fmtW
|
||||
@@ -1,28 +0,0 @@
|
||||
def generateFile(c):
|
||||
fileName = f"{c.dir}/templates/file"
|
||||
fmt = c.readFile(fileName)
|
||||
|
||||
for ln in fmt:
|
||||
newLine = ln \
|
||||
.replace("%CONTEXT_FIELDS%", c.contextFields) \
|
||||
.replace("%CORE%", c.core) \
|
||||
.replace("%CORE_SECTION_GENERATED%", c.coreSectionGenerated) \
|
||||
.replace("%CORE_SECTION_GENERATED_ACTIONS%", c.coreSectionGeneratedActions) \
|
||||
.replace("%CORE_SECTION_GENERATED_PIPES%", c.coreSectionGeneratedPipes) \
|
||||
.replace("%CORE_SECTIONS_DESTROY%", c.coreSectionsDestroy) \
|
||||
.replace("%CORE_SECTIONS_SETUP%", c.coreSectionsSetup) \
|
||||
.replace("%CORE_VM%", c.coreVM) \
|
||||
.replace("%CORE_WINDOW%", c.coreWindow) \
|
||||
.replace("%IMPORTS%", c.imports) \
|
||||
.replace("%MODEL_FIELDS%", c.modelFields) \
|
||||
.replace("%SERVICE_CORE%", c.serviceCore) \
|
||||
.replace("%SERVICE_SECTION_GENERATED%", c.serviceSectionGenerated) \
|
||||
.replace("%SERVICE_SECTION_GENERATED_ACTIONS%", c.serviceSectionGeneratedActions) \
|
||||
.replace("%SERVICE_SECTION_GENERATED_PIPES%", c.serviceSectionGeneratedPipes) \
|
||||
.replace("%SERVICE_SECTIONS%", c.serviceSections) \
|
||||
.replace("%WORLD_CONSTRUCTOR%", c.worldConstructor) \
|
||||
.replace("%WORLD_FIELDS%", c.worldFields) \
|
||||
.replace("%WORLD_PARAMETERS%", c.worldParameters) \
|
||||
.replace("%MODULE%", c.module) \
|
||||
.replace("%MODULE_SHORT%", c.shortenName(c.module)) # Замены %MODULE*% должны быть в конце.
|
||||
c.file += newLine + "\n"
|
||||
@@ -1,19 +0,0 @@
|
||||
def generateImports(c):
|
||||
fileName = f"{c.src}/../{c.module}.podspec"
|
||||
# Для сборного модуля используем путь к корневому podspec.
|
||||
if c.path != c.module:
|
||||
parent = c.path.split("/")[0]
|
||||
fileName = fileName.replace(f"{c.module}/src/../{c.module}", parent)
|
||||
# Для обычного модуля с окончание X учитываем такое же название podspec.
|
||||
if c.path[-1] == "X":
|
||||
fileName = fileName.replace(f"{c.module}.podspec", f"{c.module}X.podspec")
|
||||
lines = c.readFile(fileName)
|
||||
items = ["Combine", "Foundation", "UIKit"]
|
||||
|
||||
prefix = "s.dependency '"
|
||||
for ln in lines:
|
||||
if ln.startswith(prefix):
|
||||
name = ln[len(prefix):-1]
|
||||
items.append(name)
|
||||
|
||||
c.imports = "import " + "\nimport ".join(sorted(items))
|
||||
@@ -1,19 +0,0 @@
|
||||
from generation.fieldFormat import *
|
||||
|
||||
def generateModelFields(c):
|
||||
fileName = f"{c.dir}/templates/model-field"
|
||||
lines = c.readFile(fileName)
|
||||
fmtPlain = lines[0]
|
||||
fmtRecent = lines[1]
|
||||
fields = []
|
||||
|
||||
for key in c.structure.model.fields:
|
||||
values = c.structure.model.fields[key]
|
||||
fmt = fieldFormat(fmtPlain, fmtRecent, key, c.structure)
|
||||
ln = fmt \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", values[0]) \
|
||||
.replace("%DEFAULT%", values[1])
|
||||
fields.append(ln)
|
||||
|
||||
c.modelFields = "\n".join(fields)
|
||||
@@ -1,4 +0,0 @@
|
||||
def generateServiceSectionGenerated(c):
|
||||
fileName = f"{c.dir}/templates/service-section-generated"
|
||||
lines = c.readFile(fileName)
|
||||
c.serviceSectionGenerated = "\n".join(lines)
|
||||
@@ -1,79 +0,0 @@
|
||||
from generation.sectionGeneratedActionShouldLoad import *
|
||||
|
||||
def generateServiceSectionGeneratedActions(c):
|
||||
base = f"{c.dir}/templates/service-section-generated-action"
|
||||
fmtBusModel = c.readFile(f"{base}-bus-model")
|
||||
fmtCommon = c.readFile(base)
|
||||
fmtInstant = c.readFile(f"{base}-instant")
|
||||
fmtRelay = c.readFile(f"{base}-relay")
|
||||
fmtInstantDelay = c.readFile(f"{base}-instant-delay")
|
||||
fmtInstantRelay = c.readFile(f"{base}-instant-relay")
|
||||
fmtInstantModel = c.readFile(f"{base}-instant-model")
|
||||
fmtInstantShouldResetCore = c.readFile(f"{base}-instant-shouldResetCore")
|
||||
fmtModel = c.readFile(f"{base}-model")
|
||||
fmtShouldResetCore = c.readFile(f"{base}-shouldResetCore")
|
||||
|
||||
canRelay = "🚀model" in c.structure.service.actions
|
||||
if canRelay:
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtRelay + [''])
|
||||
|
||||
for key in c.structure.service.actions:
|
||||
value = c.structure.service.actions[key]
|
||||
|
||||
# Шаблонные действия.
|
||||
if value == "":
|
||||
# busModel.
|
||||
if key == "busModel":
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtBusModel) + "\n"
|
||||
continue
|
||||
|
||||
# instant model.
|
||||
if key == "🚀model":
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtInstantModel) + "\n"
|
||||
continue
|
||||
|
||||
# model.
|
||||
if key == "model":
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtModel) + "\n"
|
||||
continue
|
||||
|
||||
# shouldLoad*.
|
||||
shouldLoad = "shouldLoad"
|
||||
if key.startswith(shouldLoad):
|
||||
c.serviceSectionGeneratedActions += sectionGeneratedActionShouldLoad(key, "service", c)
|
||||
continue
|
||||
|
||||
# instant shouldResetCore.
|
||||
if key == "🚀shouldResetCore":
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtInstantShouldResetCore) + "\n"
|
||||
continue
|
||||
|
||||
# shouldResetCore.
|
||||
if key == "shouldResetCore":
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtShouldResetCore) + "\n"
|
||||
continue
|
||||
|
||||
continue
|
||||
|
||||
output = ""
|
||||
|
||||
action = key
|
||||
template = fmtCommon
|
||||
# Действие без receive(on:)
|
||||
if action.startswith("🚀"):
|
||||
action = action[1:]
|
||||
template = fmtInstantRelay if canRelay else fmtInstant
|
||||
|
||||
# Действие c .delay(on:)
|
||||
if action.startswith("⏳"):
|
||||
action = action[1:]
|
||||
template = fmtInstantDelay
|
||||
|
||||
for fmt in template:
|
||||
ln = fmt \
|
||||
.replace("%SHOULD%", action) \
|
||||
.replace("%SINK%", value) \
|
||||
.replace("%MODULE%", c.module)
|
||||
output += ln + "\n"
|
||||
|
||||
c.serviceSectionGeneratedActions += output
|
||||
@@ -1,27 +0,0 @@
|
||||
from generation.hasSectionGenerated import *
|
||||
from generation.sectionFunctions import *
|
||||
from generation.sectionNames import *
|
||||
|
||||
def generateServiceSections(c):
|
||||
fileName = f"{c.dir}/templates/service-section"
|
||||
lines = c.readFile(fileName)
|
||||
fmtService = lines[0]
|
||||
fmtPlatform = lines[1]
|
||||
items = []
|
||||
|
||||
sections = sectionNames(c)
|
||||
for name in sections:
|
||||
# Пропускаем секции, не относящиеся к сервису.
|
||||
funcs = sectionFunctions(name, c)
|
||||
if "setupService" not in funcs:
|
||||
continue
|
||||
|
||||
ln = fmtService.replace("%NAME%", name)
|
||||
items.append(ln)
|
||||
|
||||
# Генерированная секция.
|
||||
# Должна быть добавлена последней.
|
||||
if hasSectionGenerated(c.structure.service):
|
||||
items.append(fmtPlatform)
|
||||
|
||||
c.serviceSections= "\n".join(items)
|
||||
@@ -1,43 +0,0 @@
|
||||
from generation.generateContextFields import *
|
||||
from generation.generateCore import *
|
||||
from generation.generateCoreSectionGenerated import *
|
||||
from generation.generateCoreSectionGeneratedActions import *
|
||||
from generation.generateCoreSectionsDestroy import *
|
||||
from generation.generateCoreSectionsSetup import *
|
||||
from generation.generateCoreWindow import *
|
||||
from generation.generateFile import *
|
||||
from generation.generateImports import *
|
||||
from generation.generateModelFields import *
|
||||
from generation.generateServiceSectionGenerated import *
|
||||
from generation.generateServiceSectionGeneratedActions import *
|
||||
from generation.generateServiceSections import *
|
||||
from generation.generateWorldConstructor import *
|
||||
from generation.generateWorldFields import *
|
||||
from generation.generateWorldParameters import *
|
||||
from generation.hasSectionGenerated import *
|
||||
from generation.sectionGeneratedPipes import *
|
||||
|
||||
def generateStructure(c):
|
||||
generateContextFields(c)
|
||||
generateImports(c)
|
||||
generateModelFields(c)
|
||||
# Генерируем ядро лишь при наличии инструкций в YML.
|
||||
if hasSectionGenerated(c.structure.core):
|
||||
generateCore(c)
|
||||
generateCoreSectionsDestroy(c)
|
||||
generateCoreSectionsSetup(c)
|
||||
generateCoreWindow(c)
|
||||
generateCoreSectionGenerated(c)
|
||||
generateCoreSectionGeneratedActions(c)
|
||||
c.coreSectionGeneratedPipes = sectionGeneratedPipes(c.structure.core, "&core.subscriptions", c)
|
||||
generateServiceSections(c)
|
||||
# Генерируем секцию сервиса лишь при наличии инструкций в YML.
|
||||
if hasSectionGenerated(c.structure.service):
|
||||
generateServiceSectionGenerated(c)
|
||||
generateServiceSectionGeneratedActions(c)
|
||||
c.serviceSectionGeneratedPipes = sectionGeneratedPipes(c.structure.service, "nil", c)
|
||||
generateWorldConstructor(c)
|
||||
generateWorldFields(c)
|
||||
generateWorldParameters(c)
|
||||
# Файл обязательно генерировать последним: зависит от остальных.
|
||||
generateFile(c)
|
||||
@@ -1,18 +0,0 @@
|
||||
def generateWorldConstructor(c):
|
||||
fileName = f"{c.dir}/templates/world-constructor"
|
||||
fmt = c.readFile(fileName)[0]
|
||||
|
||||
items = []
|
||||
|
||||
for key in c.structure.world.fields:
|
||||
values = c.structure.world.fields[key]
|
||||
|
||||
if (
|
||||
"init" in values or
|
||||
key == "model" or
|
||||
key == "net"
|
||||
):
|
||||
ln = fmt.replace("%NAME%", key)
|
||||
items.append(ln)
|
||||
|
||||
c.worldConstructor = "\n".join(items)
|
||||
@@ -1,77 +0,0 @@
|
||||
from generation.worldFieldTypeInit import *
|
||||
from generation.worldFieldTypePS import *
|
||||
|
||||
def generateWorldFields(c):
|
||||
fileName = f"{c.dir}/templates/world-field"
|
||||
lines = c.readFile(fileName)
|
||||
fmtInitType = lines[0]
|
||||
fmtCVS = lines[1]
|
||||
fmtInit = lines[2]
|
||||
fmtModel = lines[3]
|
||||
fmtNet = lines[4]
|
||||
fmtPS = lines[5]
|
||||
fmtVar = lines[6]
|
||||
fields = []
|
||||
|
||||
for key in c.structure.world.fields:
|
||||
values = c.structure.world.fields[key]
|
||||
|
||||
# [TYPE, DEFAULT, cvs] -> CurrentValueSubject
|
||||
if "cvs" in values:
|
||||
type = values[0]
|
||||
default = values[1]
|
||||
ln = fmtCVS \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type) \
|
||||
.replace("%DEFAULT%", default)
|
||||
fields.append(ln)
|
||||
|
||||
# [escape, init], [TYPE, escape, init] -> let TYPE
|
||||
elif "escape" in values and "init" in values:
|
||||
type = worldFieldTypeInit(key, c.structure)
|
||||
fmt = fmtInit
|
||||
if len(values) == 3:
|
||||
fmt = fmtInitType
|
||||
ln = fmt \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type)
|
||||
fields.append(ln)
|
||||
|
||||
# [init], [TYPE, init] -> let TYPE
|
||||
elif "init" in values:
|
||||
type = worldFieldTypeInit(key, c.structure)
|
||||
fmt = fmtInit
|
||||
if len(values) == 2:
|
||||
fmt = fmtInitType
|
||||
ln = fmt \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type)
|
||||
fields.append(ln)
|
||||
|
||||
# model -> PassthroughSubject<Model>
|
||||
elif key == "model":
|
||||
fields.append(fmtModel)
|
||||
|
||||
# net -> Net.Publisher
|
||||
elif key == "net":
|
||||
fields.append(fmtNet)
|
||||
|
||||
# [ps], [TYPE, ps] -> PassthroughSubject
|
||||
elif "ps" in values:
|
||||
type = worldFieldTypePS(key, c.structure)
|
||||
ln = fmtPS \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type)
|
||||
fields.append(ln)
|
||||
|
||||
# [TYPE, DEFAULT, var] -> var TYPE
|
||||
elif "var" in values:
|
||||
type = values[0]
|
||||
default = values[1]
|
||||
ln = fmtVar \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type) \
|
||||
.replace("%DEFAULT%", default)
|
||||
fields.append(ln)
|
||||
|
||||
c.worldFields = "\n".join(fields)
|
||||
@@ -1,35 +0,0 @@
|
||||
from generation.worldFieldTypeInit import *
|
||||
|
||||
def generateWorldParameters(c):
|
||||
fileName = f"{c.dir}/templates/world-parameter"
|
||||
lines = c.readFile(fileName)
|
||||
fmtInitType = lines[0]
|
||||
fmtEscInitType = lines[1]
|
||||
fmtInit = lines[2]
|
||||
fmtModel = lines[3]
|
||||
fmtNet = lines[4]
|
||||
|
||||
params = []
|
||||
|
||||
for key in c.structure.world.fields:
|
||||
values = c.structure.world.fields[key]
|
||||
|
||||
if "init" in values:
|
||||
type = worldFieldTypeInit(key, c.structure)
|
||||
fmt = fmtInit
|
||||
if "escape" in values:
|
||||
fmt = fmtEscInitType
|
||||
elif len(values) > 1:
|
||||
fmt = fmtInitType
|
||||
ln = fmt \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type)
|
||||
params.append(ln)
|
||||
|
||||
elif key == "model":
|
||||
params.append(fmtModel)
|
||||
|
||||
elif key == "net":
|
||||
params.append(fmtNet)
|
||||
|
||||
c.worldParameters = ",\n".join(params)
|
||||
@@ -1,2 +0,0 @@
|
||||
def hasSectionGenerated(entity):
|
||||
return len(entity.actions) or len(entity.pipes) or entity.isPresent
|
||||
@@ -1,11 +0,0 @@
|
||||
def isNotKeyword(str):
|
||||
keywords = [
|
||||
"ex",
|
||||
"recent",
|
||||
"set",
|
||||
"toggle",
|
||||
"toggleNil",
|
||||
"vm",
|
||||
"$vm"
|
||||
]
|
||||
return str not in keywords
|
||||
@@ -1,5 +0,0 @@
|
||||
def isPipeRecent(name, entity):
|
||||
if name in entity.pipes:
|
||||
props = entity.pipes[name]
|
||||
return "recent" in props
|
||||
return False
|
||||
@@ -1,14 +0,0 @@
|
||||
def isToggle(name, structure):
|
||||
# Проверяем наличие `toggle` в свойствах канала ядра.
|
||||
if name in structure.core.pipes:
|
||||
props = structure.core.pipes[name]
|
||||
if "toggle" in props:
|
||||
return True
|
||||
|
||||
# Проверяем наличие `toggle` в свойствах канала сервиса.
|
||||
if name in structure.service.pipes:
|
||||
props = structure.service.pipes[name]
|
||||
if "toggle" in props:
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -1,5 +0,0 @@
|
||||
def pipeBusSource(name, entity, busKey, structure, fmt):
|
||||
valueType = structure.model.fields[name][0]
|
||||
return fmt \
|
||||
.replace("%BUS_KEY%", busKey) \
|
||||
.replace("%BUS_VALUE_TYPE%", valueType)
|
||||
@@ -1,14 +0,0 @@
|
||||
def pipeFormat(fmtExRecent, fmtRecent, fmtSet, fmtToggle, fmtToggleNil, name, entity):
|
||||
props = entity.pipes[name]
|
||||
if "recent" and "ex" in props:
|
||||
return fmtExRecent
|
||||
elif "recent" in props:
|
||||
return fmtRecent
|
||||
elif "set" in props:
|
||||
return fmtSet
|
||||
elif "toggle" in props:
|
||||
return fmtToggle
|
||||
elif "toggleNil" in props:
|
||||
return fmtToggleNil
|
||||
|
||||
return "НИНАЮ"
|
||||
@@ -1,20 +0,0 @@
|
||||
from generation.isNotKeyword import *
|
||||
|
||||
def pipeSource(name, entity):
|
||||
props = entity.pipes[name]
|
||||
if "vm" in props:
|
||||
return "core.vm." + name
|
||||
elif "$vm" in props:
|
||||
return "core.vm.$" + name
|
||||
else:
|
||||
# Если это что-то неизвестное заранее, то ищем строку,
|
||||
# отличную от известных ключевых слов для инструкции pipe.
|
||||
default = "world"
|
||||
src = next(filter(isNotKeyword, props), default)
|
||||
# Прямое обращение к VM.
|
||||
if src.startswith("vm."):
|
||||
src = "core." + src
|
||||
# Значение по умолчанию.
|
||||
elif src == default:
|
||||
return default + "." + name
|
||||
return src
|
||||
@@ -1,14 +0,0 @@
|
||||
def sectionFunctions(name, c):
|
||||
path = f"{c.src}/{c.module}.Section{name}.swift"
|
||||
lines = c.readFile(path)
|
||||
items = []
|
||||
|
||||
for ln in lines:
|
||||
if ln.startswith(" static func destroyCore"):
|
||||
items.append("destroyCore")
|
||||
elif ln.startswith(" static func setupCore"):
|
||||
items.append("setupCore")
|
||||
elif ln.startswith(" static func setupService"):
|
||||
items.append("setupService")
|
||||
|
||||
return items
|
||||
@@ -1,14 +0,0 @@
|
||||
def sectionGeneratedActionShouldLoad(key, sub, c):
|
||||
lines = c.readFile(f"{c.dir}/templates/section-generated-action-shouldLoad")
|
||||
name = key[len("shouldLoad"):]
|
||||
request = name[0].lower() + name[1:]
|
||||
output = ""
|
||||
|
||||
for fmt in lines:
|
||||
ln = fmt \
|
||||
.replace("%NAME%", name) \
|
||||
.replace("%REQUEST%", request) \
|
||||
.replace("%SUB%", sub)
|
||||
output += ln + "\n"
|
||||
|
||||
return output
|
||||
@@ -1,47 +0,0 @@
|
||||
from generation.shortenName import *
|
||||
from generation.pipeBusSource import *
|
||||
from generation.pipeFormat import *
|
||||
from generation.pipeSource import *
|
||||
|
||||
def sectionGeneratedPipes(entity, sub, c):
|
||||
fmtBusPipe = c.readFile(f"{c.dir}/templates/section-generated-pipe-src-bus")[0]
|
||||
fmtExRecent = c.readFile(f"{c.dir}/templates/section-generated-pipe-ex-recent")
|
||||
fmtRecent = c.readFile(f"{c.dir}/templates/section-generated-pipe-recent")
|
||||
fmtSet = c.readFile(f"{c.dir}/templates/section-generated-pipe-set")
|
||||
fmtToggle = c.readFile(f"{c.dir}/templates/section-generated-pipe-toggle")
|
||||
fmtToggleNil = c.readFile(f"{c.dir}/templates/section-generated-pipe-toggleNil")
|
||||
output = ""
|
||||
|
||||
for key in entity.pipes:
|
||||
values = entity.pipes[key]
|
||||
|
||||
# EX_NAME.
|
||||
firstLetter = key[:1].capitalize()
|
||||
exName = f"""ex{firstLetter}{key[1:]}"""
|
||||
|
||||
# PIPE.
|
||||
pipe = "pipeValue"
|
||||
if "toggle" in values:
|
||||
pipe = "pipe"
|
||||
|
||||
# SHORT_SRC.
|
||||
shortSrc = shortenName(key)
|
||||
|
||||
# SRC.
|
||||
src = pipeSource(key, entity)
|
||||
# Bus.
|
||||
if src.startswith("K."):
|
||||
src = pipeBusSource(key, entity, src, c.structure, fmtBusPipe)
|
||||
|
||||
fmtPipe = pipeFormat(fmtExRecent, fmtRecent, fmtSet, fmtToggle, fmtToggleNil, key, entity)
|
||||
for fmt in fmtPipe:
|
||||
ln = fmt \
|
||||
.replace("%EX_NAME%", exName) \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%PIPE%", pipe) \
|
||||
.replace("%SHORT_SRC%", shortSrc) \
|
||||
.replace("%SRC%", src) \
|
||||
.replace("%SUB%", sub)
|
||||
output += ln + "\n"
|
||||
|
||||
return output
|
||||
@@ -1,19 +0,0 @@
|
||||
import os
|
||||
|
||||
def sectionNames(c):
|
||||
prefix = "Section"
|
||||
ending = ".swift"
|
||||
fileNames = os.listdir(c.src)
|
||||
items = []
|
||||
|
||||
for fileName in sorted(fileNames):
|
||||
# Пропускаем файлы, не являющиеся секциями.
|
||||
id = fileName.find(prefix)
|
||||
if id == -1:
|
||||
continue
|
||||
|
||||
# Вычленяем имя секции из имени файла.
|
||||
name = fileName[id + len(prefix):-len(ending)]
|
||||
items.append(name)
|
||||
|
||||
return items
|
||||
@@ -1,24 +0,0 @@
|
||||
def shortenName(text):
|
||||
capitals = [l for l in text if l.isupper()]
|
||||
# Нет заглавных.
|
||||
if len(capitals) == 0:
|
||||
return text
|
||||
|
||||
capId = 0
|
||||
# Первая - заглавная.
|
||||
if text[0].isupper():
|
||||
capId = 1
|
||||
|
||||
# Заглавная лишь первая.
|
||||
if (
|
||||
capId == 1 and
|
||||
len(capitals) == 1
|
||||
):
|
||||
return text
|
||||
|
||||
# Убираем первое заглавное слово.
|
||||
if capId == 1:
|
||||
capitals = capitals[1:]
|
||||
# Есть ещё заглавные.
|
||||
firstCap = text.find(capitals[0])
|
||||
return text[:firstCap] + "".join(capitals)
|
||||
@@ -1,12 +0,0 @@
|
||||
from generation.isToggle import *
|
||||
|
||||
_keywords = { "init", "var", "escape" }
|
||||
|
||||
def worldFieldTypeInit(key, structure):
|
||||
values = structure.world.fields[key]
|
||||
values = [v for v in values if v not in _keywords]
|
||||
if len(values) == 1:
|
||||
return values[0]
|
||||
if isToggle(key, structure):
|
||||
return "Void"
|
||||
return structure.model.fields[key][0]
|
||||
@@ -1,9 +0,0 @@
|
||||
from generation.isToggle import *
|
||||
|
||||
def worldFieldTypePS(key, structure):
|
||||
values = structure.world.fields[key]
|
||||
if len(values) == 2:
|
||||
return values[0]
|
||||
if isToggle(key, structure):
|
||||
return "Void"
|
||||
return structure.model.fields[key][0]
|
||||
@@ -1,39 +0,0 @@
|
||||
from parsing.readKeyValue import *
|
||||
from parsing.valueArray import *
|
||||
|
||||
class CoreService:
|
||||
def __init__(self):
|
||||
self.actions = {}
|
||||
self.isPresent = False
|
||||
self.pipes = {}
|
||||
|
||||
def parseAction(self, line):
|
||||
kv = readKeyValue(line)
|
||||
|
||||
# В действии всегда должен быть хотя бы ключ.
|
||||
if kv is None:
|
||||
return
|
||||
|
||||
# Лишь ключ.
|
||||
if kv[1] is None:
|
||||
self.actions[kv[0]] = ""
|
||||
return
|
||||
|
||||
# Ключ и значение.
|
||||
self.actions[kv[0]] = kv[1]
|
||||
|
||||
def parsePipe(self, line):
|
||||
kv = readKeyValue(line)
|
||||
# В канале всегда должны быть и ключ, и значение.
|
||||
if (
|
||||
kv is None or
|
||||
kv[1] is None
|
||||
):
|
||||
return
|
||||
|
||||
values = valueArray(kv[1])
|
||||
# В канале каждое значение должно быть массивом.
|
||||
if values is None:
|
||||
return
|
||||
|
||||
self.pipes[kv[0]] = values
|
||||
@@ -1,31 +0,0 @@
|
||||
class Mode:
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def parseLine(self, line):
|
||||
if line.startswith("model:"):
|
||||
self.reset()
|
||||
self.isModel = True
|
||||
elif line.startswith("core:"):
|
||||
self.reset()
|
||||
self.isCore = True
|
||||
elif line.startswith("service:"):
|
||||
self.reset()
|
||||
self.isService = True
|
||||
elif line.startswith("world:"):
|
||||
self.reset()
|
||||
self.isWorld = True
|
||||
elif line.startswith(" actions:"):
|
||||
self.isAction = True
|
||||
self.isPipe = False
|
||||
elif line.startswith(" pipes:"):
|
||||
self.isAction = False
|
||||
self.isPipe = True
|
||||
|
||||
def reset(self):
|
||||
self.isAction = False
|
||||
self.isCore = False
|
||||
self.isModel = False
|
||||
self.isPipe = False
|
||||
self.isService = False
|
||||
self.isWorld = False
|
||||
@@ -1,24 +0,0 @@
|
||||
from parsing.readKeyValue import *
|
||||
from parsing.valueArray import *
|
||||
|
||||
class ModelWorld:
|
||||
def __init__(self):
|
||||
self.fields = {}
|
||||
|
||||
def parseLine(self, line):
|
||||
kv = readKeyValue(line)
|
||||
# В модели/мире всегда должен быть хотя бы ключ.
|
||||
if kv is None:
|
||||
return
|
||||
|
||||
# Лишь ключ.
|
||||
if kv[1] is None:
|
||||
self.fields[kv[0]] = []
|
||||
return
|
||||
|
||||
values = valueArray(kv[1])
|
||||
# В модели/мире указанное значение должно быть массивом.
|
||||
if values is None:
|
||||
return
|
||||
|
||||
self.fields[kv[0]] = values
|
||||
@@ -1,9 +0,0 @@
|
||||
from parsing.CoreService import *
|
||||
from parsing.ModelWorld import *
|
||||
|
||||
class Structure:
|
||||
def __init__(self):
|
||||
self.core = CoreService()
|
||||
self.model = ModelWorld()
|
||||
self.service = CoreService()
|
||||
self.world = ModelWorld()
|
||||
@@ -1,42 +0,0 @@
|
||||
from parsing.CoreService import *
|
||||
from parsing.Mode import *
|
||||
from parsing.ModelWorld import *
|
||||
|
||||
def parseLines(lines, structure):
|
||||
mode = Mode()
|
||||
for line in lines:
|
||||
# Определяем режим строки.
|
||||
mode.parseLine(line)
|
||||
ln = line.strip()
|
||||
|
||||
# Игнорируем пустую строку.
|
||||
if len(ln) == 0:
|
||||
continue
|
||||
|
||||
# Игнорируем комментарий.
|
||||
if ln.startswith("#"):
|
||||
continue
|
||||
|
||||
# model.
|
||||
if mode.isModel:
|
||||
structure.model.parseLine(ln)
|
||||
|
||||
# core.
|
||||
elif mode.isCore:
|
||||
structure.core.isPresent = True
|
||||
if mode.isAction:
|
||||
structure.core.parseAction(ln)
|
||||
elif mode.isPipe:
|
||||
structure.core.parsePipe(ln)
|
||||
|
||||
# service.
|
||||
elif mode.isService:
|
||||
structure.service.isPresent = True
|
||||
if mode.isAction:
|
||||
structure.service.parseAction(ln)
|
||||
elif mode.isPipe:
|
||||
structure.service.parsePipe(ln)
|
||||
|
||||
# world.
|
||||
elif mode.isWorld:
|
||||
structure.world.parseLine(ln)
|
||||
@@ -1,15 +0,0 @@
|
||||
def readKeyValue(line):
|
||||
parts = line.split(": ")
|
||||
# Ключ со значением.
|
||||
if len(parts) == 2:
|
||||
return (parts[0], parts[1])
|
||||
|
||||
# Ключ без значения.
|
||||
if (
|
||||
line.endswith(":") == False and
|
||||
len(parts) == 1
|
||||
):
|
||||
return (parts[0], None)
|
||||
|
||||
# Не является ключом со значением.
|
||||
return None
|
||||
@@ -1,12 +0,0 @@
|
||||
def valueArray(value):
|
||||
# Удостоверяем наличие скобок.
|
||||
if not (
|
||||
value.startswith("[") and
|
||||
value.endswith("]")
|
||||
):
|
||||
return None
|
||||
|
||||
# Исключаем скобки.
|
||||
noBrackets = value[1:-1]
|
||||
parts = noBrackets.split(", ")
|
||||
return parts
|
||||
@@ -1,2 +0,0 @@
|
||||
var %NAME%: %TYPE% { get }
|
||||
var %NAME%: MPAK.Recent<%TYPE%> { get }
|
||||
@@ -1,17 +0,0 @@
|
||||
// MARK: - Core
|
||||
|
||||
final class Core {
|
||||
%CORE_VM%
|
||||
var subscriptions = [AnyCancellable]()
|
||||
%CORE_WINDOW%
|
||||
|
||||
deinit {
|
||||
/**/aelog("😟 %MODULE_SHORT%Core.deinit")
|
||||
%CORE_SECTIONS_DESTROY%
|
||||
}
|
||||
|
||||
init(_ ctrl: %MODULE%.Controller, _ world: %MODULE%.World) {
|
||||
/**/aelog("😀 %MODULE_SHORT%Core.init")
|
||||
%CORE_SECTIONS_SETUP%
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
Section%NAME%.destroyCore(self)
|
||||
Section%NAME%.setupCore(self, ctrl, world)
|
||||
SectionGenerated.setupPlatform(self, ctrl, world)
|
||||
@@ -1,15 +0,0 @@
|
||||
// MARK: - SectionGenerated Core
|
||||
|
||||
static func setupPlatform(
|
||||
_ core: Core,
|
||||
_ ctrl: Controller,
|
||||
_ world: World
|
||||
) {
|
||||
// MARK: - SectionGenerated Core Actions
|
||||
|
||||
%CORE_SECTION_GENERATED_ACTIONS%
|
||||
|
||||
// MARK: - SectionGenerated Core Pipes
|
||||
|
||||
%CORE_SECTION_GENERATED_PIPES%
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak core] v in %SINK% }
|
||||
.store(in: &core.subscriptions)
|
||||
@@ -1,4 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.sink { [weak core] v in %SINK% }
|
||||
.store(in: &core.subscriptions)
|
||||
@@ -1,2 +0,0 @@
|
||||
let vm = VM()
|
||||
var wnd: UIWindow?
|
||||
@@ -1,68 +0,0 @@
|
||||
// ВНИМАНИЕ Сгенерировано автоматом из файла %MODULE%.yml
|
||||
// ВНИМАНИЕ Не менять руками!
|
||||
|
||||
%IMPORTS%
|
||||
|
||||
// MARK: - Context
|
||||
|
||||
public protocol %MODULE%Context {
|
||||
%CONTEXT_FIELDS%
|
||||
}
|
||||
|
||||
// MARK: - Controller
|
||||
|
||||
extension %MODULE% {
|
||||
final class Controller: MPAK.Controller<%MODULE%.Model> {
|
||||
init() {
|
||||
super.init(
|
||||
%MODULE%.Model(),
|
||||
debugClassName: "%MODULE_SHORT%Ctrl",
|
||||
debugLog: { aelog($0) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
%CORE%
|
||||
|
||||
// MARK: - Model
|
||||
|
||||
public struct Model: %MODULE%Context {
|
||||
%MODEL_FIELDS%
|
||||
}
|
||||
|
||||
// MARK: - Service
|
||||
|
||||
public final class Service {
|
||||
let ctrl = Controller()
|
||||
let world: World
|
||||
%SERVICE_CORE%
|
||||
var subscriptions = [AnyCancellable]()
|
||||
static private(set) weak var singleton: Service?
|
||||
|
||||
public init(_ world: World) {
|
||||
self.world = world
|
||||
Self.singleton = self
|
||||
%SERVICE_SECTIONS%
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - World
|
||||
|
||||
public struct World {
|
||||
%WORLD_FIELDS%
|
||||
|
||||
public init(
|
||||
%WORLD_PARAMETERS%
|
||||
) {
|
||||
%WORLD_CONSTRUCTOR%
|
||||
}
|
||||
}
|
||||
|
||||
enum SectionGenerated {
|
||||
|
||||
%CORE_SECTION_GENERATED%
|
||||
|
||||
%SERVICE_SECTION_GENERATED%
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
public var %NAME%: %TYPE% = %DEFAULT%
|
||||
public var %NAME%: MPAK.Recent<%TYPE%> = .init(%DEFAULT%)
|
||||
@@ -1,8 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { shouldLoad%NAME%($0) }
|
||||
.flatMap { v -> AnyPublisher<Net.Result%NAME%, Never> in
|
||||
world.net().%REQUEST%(v).eraseToAnyPublisher()
|
||||
}
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { v in world.result%NAME%.send(v) }
|
||||
.store(in: &%SUB%.subscriptions)
|
||||
@@ -1,13 +0,0 @@
|
||||
ctrl.%PIPE%(
|
||||
dbg: "%SHORT_SRC%",
|
||||
sub: %SUB%,
|
||||
%SRC%.eraseToAnyPublisher(),
|
||||
{
|
||||
$0.%NAME%.value = $1
|
||||
$0.%NAME%.isRecent = true
|
||||
},
|
||||
{
|
||||
$0.%NAME%.isRecent = false
|
||||
$0.%EX_NAME% = $1
|
||||
}
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
ctrl.%PIPE%(
|
||||
dbg: "%SHORT_SRC%",
|
||||
sub: %SUB%,
|
||||
%SRC%.eraseToAnyPublisher(),
|
||||
{
|
||||
$0.%NAME%.value = $1
|
||||
$0.%NAME%.isRecent = true
|
||||
},
|
||||
{ m, _ in m.%NAME%.isRecent = false }
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
ctrl.%PIPE%(
|
||||
dbg: "%SHORT_SRC%",
|
||||
sub: %SUB%,
|
||||
%SRC%.eraseToAnyPublisher(),
|
||||
{ $0.%NAME% = $1 }
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
Bus.events.compactMap { Bus.convertKeyValue(%BUS_KEY%, $0) }.map { (k: String, v: %BUS_VALUE_TYPE%) in v }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
ctrl.%PIPE%(
|
||||
dbg: "%SHORT_SRC%",
|
||||
sub: %SUB%,
|
||||
%SRC%.eraseToAnyPublisher(),
|
||||
{ $0.%NAME% = true },
|
||||
{ $0.%NAME% = false }
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
ctrl.%PIPE%(
|
||||
dbg: "%SHORT_SRC%",
|
||||
sub: %SUB%,
|
||||
%SRC%.eraseToAnyPublisher(),
|
||||
{ $0.%NAME% = $1 },
|
||||
{ m, _ in m.%NAME% = nil }
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
var core: Core?
|
||||
@@ -1,2 +0,0 @@
|
||||
Section%NAME%.setupService(ctrl, self, world)
|
||||
SectionGenerated.setupPlatform(ctrl, self, world)
|
||||
@@ -1,15 +0,0 @@
|
||||
// MARK: - SectionGenerated Service
|
||||
|
||||
static func setupPlatform(
|
||||
_ ctrl: Controller,
|
||||
_ service: Service,
|
||||
_ world: World
|
||||
) {
|
||||
// MARK: - SectionGenerated Service Actions
|
||||
|
||||
%SERVICE_SECTION_GENERATED_ACTIONS%
|
||||
|
||||
// MARK: - SectionGenerated Service Pipes
|
||||
|
||||
%SERVICE_SECTION_GENERATED_PIPES%
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { v in %SINK% }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,3 +0,0 @@
|
||||
ctrl.m
|
||||
.sink { v in Bus.send("%MODULE%", v) }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,4 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.sink { v in %SINK% }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,5 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.delay(for: .seconds(0.3), scheduler: RunLoop.main)
|
||||
.sink { v in %SINK% }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,3 +0,0 @@
|
||||
ctrl.m
|
||||
.sink { v in world.model.send(v); modelRelay.send(v) }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,4 +0,0 @@
|
||||
modelRelay
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.sink { v in %SINK% }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,7 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { shouldResetCore($0) }
|
||||
.sink { v in
|
||||
service.core = v ? Core(ctrl, world) : nil
|
||||
world.isCoreRunning.send(v)
|
||||
}
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,4 +0,0 @@
|
||||
ctrl.m
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { v in world.model.send(v) }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1 +0,0 @@
|
||||
let modelRelay = PassthroughSubject<Model, Never>()
|
||||
@@ -1,8 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { shouldResetCore($0) }
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { v in
|
||||
service.core = v ? Core(ctrl, world) : nil
|
||||
world.isCoreRunning.send(v)
|
||||
}
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1 +0,0 @@
|
||||
self.%NAME% = %NAME%
|
||||
@@ -1,7 +0,0 @@
|
||||
let %NAME%: %TYPE%
|
||||
let %NAME% = CurrentValueSubject<%TYPE%, Never>(%DEFAULT%)
|
||||
let %NAME%: AnyPublisher<%TYPE%, Never>
|
||||
let model: PassthroughSubject<%MODULE%.Model, Never>
|
||||
let net: () -> Net.Publisher
|
||||
let %NAME% = PassthroughSubject<%TYPE%, Never>()
|
||||
var %NAME%: %TYPE% = %DEFAULT%
|
||||
@@ -1,5 +0,0 @@
|
||||
_ %NAME%: %TYPE%
|
||||
_ %NAME%: @escaping %TYPE%
|
||||
_ %NAME%: AnyPublisher<%TYPE%, Never>
|
||||
_ model: PassthroughSubject<%MODULE%.Model, Never>
|
||||
_ net: @escaping () -> Net.Publisher
|
||||
@@ -1,15 +0,0 @@
|
||||
class Mode:
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def parseLine(self, line):
|
||||
if line.startswith("src:"):
|
||||
self.reset()
|
||||
self.isSource = True
|
||||
elif line.startswith("replace:"):
|
||||
self.reset()
|
||||
self.isReplacement = True
|
||||
|
||||
def reset(self):
|
||||
self.isSource = False
|
||||
self.isReplacement = False
|
||||
@@ -1,12 +0,0 @@
|
||||
from readKeyValue import *
|
||||
|
||||
class Replace:
|
||||
def __init__(self):
|
||||
self.items = { }
|
||||
|
||||
def parseLine(self, line):
|
||||
kv = readKeyValue(line)
|
||||
# Игнорируем всё, что не является ключом со значением.
|
||||
if kv is None:
|
||||
return
|
||||
self.items[kv[0]] = kv[1]
|
||||
@@ -1,5 +0,0 @@
|
||||
class Result:
|
||||
def __init__(self, structure):
|
||||
self.structure = structure
|
||||
|
||||
self.file = ""
|
||||
@@ -1,13 +0,0 @@
|
||||
from readKeyValue import *
|
||||
|
||||
class Source:
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
|
||||
def parseLine(self, line):
|
||||
kv = readKeyValue(line)
|
||||
# Игнорируем всё, что не является ключом со значением.
|
||||
if kv is None:
|
||||
return
|
||||
|
||||
self.name = kv[1]
|
||||
@@ -1,8 +0,0 @@
|
||||
from Replace import *
|
||||
from Source import *
|
||||
|
||||
class Structure:
|
||||
def __init__(self):
|
||||
self.orig = ""
|
||||
self.replace = Replace()
|
||||
self.src = Source()
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
from generateStructure import *
|
||||
from parseLines import *
|
||||
from readModuleSrc import *
|
||||
from Structure import *
|
||||
|
||||
DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
|
||||
# Импорт из общей для всех генераторов директории.
|
||||
sys.path.append(f"{DIR}/../common")
|
||||
from modulePaths import *
|
||||
from readFile import *
|
||||
|
||||
parser = ArgumentParser(prog='generate v3')
|
||||
parser.add_argument('module', type=str,
|
||||
help='the name of the module to generate')
|
||||
parser.add_argument('-i', '--input', type=str,
|
||||
help='The path and name of the input file')
|
||||
parser.add_argument('-o', '--output', type=str,
|
||||
help='The path of the output files')
|
||||
parser.add_argument('-s', '--source', type=str,
|
||||
help='The path of the source files')
|
||||
|
||||
args = parser.parse_args()
|
||||
(PATH, MODULE) = modulePaths(args.module)
|
||||
|
||||
print(f"Generating platform for module '{PATH}'...")
|
||||
|
||||
FILE_IN = args.input or f"{DIR}/../../../Modules/{PATH}/{MODULE}.yml"
|
||||
DIR_OUT = args.output or f"{DIR}/../../../Modules/{PATH}/src/"
|
||||
FILE_OUT = os.path.join(DIR_OUT, f"{MODULE}.Generated.swift")
|
||||
|
||||
# Читаем файл и разбираем его на ключи-значения.
|
||||
lines = readFile(FILE_IN)
|
||||
structure = Structure()
|
||||
parseLines(lines, structure)
|
||||
ORIG_SRC = args.source or f"{DIR}/../../../Modules/{structure.src.name}/src"
|
||||
structure.orig = readModuleSrc(ORIG_SRC, readFile)
|
||||
|
||||
# Генерируем код.
|
||||
output = generateStructure(structure)
|
||||
|
||||
# Сохраняем файл.
|
||||
with open(FILE_OUT, "w") as file:
|
||||
file.write(output)
|
||||
@@ -1,9 +0,0 @@
|
||||
def generateStructure(structure):
|
||||
output = f"""// ВНИМАНИЕ Сгенерировано автоматом из файла {structure.src.name}.yml
|
||||
// ВНИМАНИЕ Не менять руками!
|
||||
"""
|
||||
output += structure.orig
|
||||
for key in structure.replace.items:
|
||||
value = structure.replace.items[key]
|
||||
output = output.replace(key, value)
|
||||
return output
|
||||
@@ -1,23 +0,0 @@
|
||||
from Mode import *
|
||||
|
||||
def parseLines(lines, structure):
|
||||
mode = Mode()
|
||||
for line in lines:
|
||||
# Определяем режим строки.
|
||||
mode.parseLine(line)
|
||||
ln = line.strip()
|
||||
|
||||
# Игнорируем пустую строку.
|
||||
if len(ln) == 0:
|
||||
continue
|
||||
|
||||
# Игнорируем комментарий.
|
||||
if ln.startswith("#"):
|
||||
continue
|
||||
|
||||
# replace.
|
||||
if mode.isReplacement:
|
||||
structure.replace.parseLine(ln)
|
||||
# src.
|
||||
elif mode.isSource:
|
||||
structure.src.parseLine(ln)
|
||||
@@ -1,8 +0,0 @@
|
||||
def readKeyValue(line):
|
||||
parts = line.split(": ")
|
||||
# Ключ со значением.
|
||||
if len(parts) == 2:
|
||||
return (parts[0], parts[1])
|
||||
|
||||
# Не является ключом со значением.
|
||||
return None
|
||||
@@ -1,12 +0,0 @@
|
||||
import os
|
||||
|
||||
def readModuleSrc(dir, readFile):
|
||||
fileNames = os.listdir(dir)
|
||||
contents = []
|
||||
|
||||
for fileName in sorted(fileNames):
|
||||
path = dir + "/" + fileName
|
||||
lines = readFile(path)
|
||||
contents.extend(lines)
|
||||
|
||||
return "\n".join(contents)
|
||||
@@ -1,13 +0,0 @@
|
||||
from readKeyValue import *
|
||||
|
||||
class FeatureToggle:
|
||||
def __init__(self):
|
||||
self.link = None
|
||||
|
||||
def parseLine(self, line):
|
||||
kv = readKeyValue(line)
|
||||
# Игнорируем всё, что не является ключом со значением.
|
||||
if kv is None:
|
||||
return
|
||||
|
||||
self.link = kv[1]
|
||||
@@ -1,11 +0,0 @@
|
||||
class Mode:
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def parseLine(self, line):
|
||||
if line.startswith("featureToggle:"):
|
||||
self.reset()
|
||||
self.isFeatureToggle = True
|
||||
|
||||
def reset(self):
|
||||
self.isFeatureToggle = False
|
||||
@@ -1,7 +0,0 @@
|
||||
from FeatureToggle import *
|
||||
|
||||
class Structure:
|
||||
def __init__(self):
|
||||
self.dir = ""
|
||||
self.module = ""
|
||||
self.featureToggle = FeatureToggle()
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
from Structure import *
|
||||
from generateFeatureToggle import *
|
||||
from parseLines import *
|
||||
|
||||
DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
MODULE = sys.argv[1]
|
||||
|
||||
# Импорт из общей для всех генераторов директории.
|
||||
sys.path.append(f"{DIR}/../common")
|
||||
from readFile import *
|
||||
|
||||
print(f"Генерируем сборный модуль '{MODULE}'...")
|
||||
|
||||
MODULE_DIR = f"{DIR}/../../../Modules/{MODULE}"
|
||||
FILE_IN = f"{MODULE_DIR}/{MODULE}.yml"
|
||||
|
||||
# Читаем файл и разбираем его на ключи-значения.
|
||||
lines = readFile(FILE_IN)
|
||||
structure = Structure()
|
||||
parseLines(lines, structure)
|
||||
structure.moduleDir = MODULE_DIR
|
||||
structure.module = MODULE
|
||||
|
||||
# Генерируем модуль FeatureToggle.
|
||||
generateFeatureToggle(structure)
|
||||
@@ -1,28 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
def generateFeatureToggle(s):
|
||||
# Пропускаем генерацию FeatureToggle, если не указана ссылка ivcsdbg
|
||||
if s.featureToggle.link is None:
|
||||
return
|
||||
|
||||
# Создаём директории модуля.
|
||||
dirs = f"{s.moduleDir}/{s.module}FeatureToggle/src"
|
||||
Path(dirs).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Создаём YML для генератора-3.
|
||||
content = f"""version: 3
|
||||
|
||||
# ВНИМАНИЕ Сгенерировано автоматом из файла {s.module}.yml
|
||||
# ВНИМАНИЕ Не менять руками!
|
||||
|
||||
src: ChatsFeatureToggle
|
||||
replace:
|
||||
ChatsFeatureToggle: {s.module}FeatureToggle
|
||||
ChatsFTCtrl: {s.module}FTCtrl
|
||||
chats: {s.featureToggle.link}
|
||||
"""
|
||||
|
||||
# Сохраняем YML.
|
||||
fileName = f"{s.moduleDir}/{s.module}FeatureToggle/{s.module}FeatureToggle.yml"
|
||||
with open(fileName, "w") as file:
|
||||
file.write(content)
|
||||
@@ -1,20 +0,0 @@
|
||||
from Mode import *
|
||||
|
||||
def parseLines(lines, structure):
|
||||
mode = Mode()
|
||||
for line in lines:
|
||||
# Определяем режим строки.
|
||||
mode.parseLine(line)
|
||||
ln = line.strip()
|
||||
|
||||
# Игнорируем пустую строку.
|
||||
if len(ln) == 0:
|
||||
continue
|
||||
|
||||
# Игнорируем комментарий.
|
||||
if ln.startswith("#"):
|
||||
continue
|
||||
|
||||
# featureToggle.
|
||||
if mode.isFeatureToggle:
|
||||
structure.featureToggle.parseLine(ln)
|
||||
@@ -1,8 +0,0 @@
|
||||
def readKeyValue(line):
|
||||
parts = line.split(": ")
|
||||
# Ключ со значением.
|
||||
if len(parts) == 2:
|
||||
return (parts[0], parts[1])
|
||||
|
||||
# Не является ключом со значением.
|
||||
return None
|
||||
@@ -1 +0,0 @@
|
||||
MeetupIdX
|
||||
@@ -1,10 +0,0 @@
|
||||
def modulePaths(name):
|
||||
parts = name.split("/")
|
||||
if len(parts) == 2:
|
||||
return (name, parts[1])
|
||||
else:
|
||||
module = name
|
||||
if name[-1] == "X":
|
||||
module = name[:-1]
|
||||
return (name, module)
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
def readFile(fileName):
|
||||
lines = []
|
||||
with open(fileName) as file:
|
||||
for line in file:
|
||||
lines.append(line.rstrip())
|
||||
return lines
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('module', type=str,
|
||||
help='the name of the module to generate')
|
||||
parser.add_argument('-i', '--input', type=str,
|
||||
help='The path and name of the input file')
|
||||
|
||||
args, _ = parser.parse_known_args()
|
||||
DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
|
||||
# Импорт из общей для всех генераторов директории.
|
||||
sys.path.append(f"{DIR}/common")
|
||||
from modulePaths import *
|
||||
|
||||
|
||||
(PATH, MODULE) = modulePaths(args.module)
|
||||
|
||||
FILE_IN = args.input or f"{DIR}/../../Modules/{PATH}/{MODULE}.yml"
|
||||
|
||||
# Запускаем указанную в файле YML версию генератора.
|
||||
with open(FILE_IN) as file:
|
||||
ln = file.readline().rstrip()
|
||||
version = ln[-1]
|
||||
cmd = f"{DIR}/{version}/generate"
|
||||
subprocess.call(args=[cmd] + sys.argv[1:])
|
||||
@@ -1,87 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from hashlib import sha256
|
||||
from tempfile import TemporaryDirectory
|
||||
from shutil import copyfile
|
||||
|
||||
FILENAME = 'Platfile'
|
||||
LOCKFILE = FILENAME + '.lock'
|
||||
VERSION = '2.0'
|
||||
|
||||
def initialize(dir: str):
|
||||
print('Initializing ', FILENAME)
|
||||
dir_modules = os.path.join(dir, '..', '..', 'Modules')
|
||||
path = os.path.join(dir, FILENAME)
|
||||
with open(path, 'w') as file:
|
||||
entries = os.listdir(dir_modules)
|
||||
for entry in entries:
|
||||
if os.path.isfile(os.path.join(dir_modules, entry, entry + '.yml')):
|
||||
file.write(entry + '\n')
|
||||
|
||||
def generate(dir: str, generator: str):
|
||||
path = os.path.join(dir, FILENAME)
|
||||
with open(path) as file:
|
||||
modules = file.readlines()
|
||||
modules = set(m.strip() for m in modules if m.strip())
|
||||
|
||||
modules = sorted(modules)
|
||||
dir_modules = os.path.join(dir, '..', '..', 'Modules')
|
||||
|
||||
print(f'Generating modules: {len(modules)}')
|
||||
generator = generator or os.path.join(dir, 'generate-platform')
|
||||
|
||||
for module in modules:
|
||||
src_dir = os.path.join(dir_modules, module, 'src')
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
subprocess.call([generator, module, '-o', tmpdir])
|
||||
_merge_changes(tmpdir, src_dir)
|
||||
|
||||
if os.path.exists(LOCKFILE):
|
||||
os.remove(LOCKFILE)
|
||||
|
||||
|
||||
def _merge_changes(src_dir: str, dst_dir: str):
|
||||
for file_name in os.listdir(src_dir):
|
||||
src_file = os.path.join(src_dir, file_name)
|
||||
src_hash = _hash_file(src_file)
|
||||
|
||||
dst_file = os.path.join(dst_dir, file_name)
|
||||
if os.path.exists(dst_file):
|
||||
dst_hash = _hash_file(dst_file)
|
||||
if src_hash == dst_hash:
|
||||
return
|
||||
|
||||
copyfile(src_file, dst_file)
|
||||
print('Updated file: ', file_name)
|
||||
|
||||
def _hash_file(path: str):
|
||||
alg = sha256()
|
||||
with open(path, 'rb') as file:
|
||||
for line in file.readlines():
|
||||
if line:
|
||||
alg.update(line)
|
||||
return alg.hexdigest()
|
||||
|
||||
def main():
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser(
|
||||
prog='GeneratePlatforms',
|
||||
description='Automatic code generator'
|
||||
)
|
||||
parser.add_argument('-i', '--init', action='store_true')
|
||||
parser.add_argument('-g', '--generator', type=str)
|
||||
|
||||
args = parser.parse_args()
|
||||
dir = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
if args.init:
|
||||
initialize(dir)
|
||||
else:
|
||||
generate(dir, args.generator)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user