It's been a while since I've thought about this, but I recently came across
Araq's [ormin](https://github.com/Araq/ormin), and noticed something that took
me by surprise. This post isn't really a question or issue, I just wanted to
get my thoughts out there.
Ormin's encompassing macro, as far as I can tell with my uneducated knowledge
about the subject, generates a Nim source file for the client, and an AST node
for the server, meaning the Nim file managed by the user is the server.
I've had the opposite idea for multiple applications, where 1 file collects AST
from other files and operates on it. My first use case (that I never developed)
was for a state-based game:
# main.nim
macro generateStates(sharedFile, statesDir: static[string]) =
# generate StateKind enum, State object variant, pollInputs and render
procs embedding code from the state modules
generateStates("shared.nim", "states/")
const defaultState = MainMenu
proc main =
var shared: Shared
var state: State = initialize(defaultState, shared)
while not shared.endLoop:
pollInputs(state, shared)
if 1/60 second passes:
render(state, shared)
Run
# shared.nim
type Shared* = object
window*: Window
endLoop*: bool
Run
# states/main_menu.nim
# generates a ref object to put in the state object variant for the
variables and constants are gensym'd into main
var hoveredButton: int
const Buttons = ["Play", "Exit"]
texture logoTexture, "res/logo_texture.png" # can dynamically or statically
load texture, although both can be expressed without the shorthand
input:
hoveredButton = locateButton(mouseX, mouseY)
if mouseClicked:
case hoveredButton
of 0:
state = initialize(Gameplay, shared)
of 1:
shared.endLoop = true
render:
render(logoTexture, x, y, w, h)
for i, b in Buttons:
if i == hoveredButton:
strokeColor = green
else:
strokeColor = black
renderText(b, ...)
Run
This could even support substates, say the Gameplay loop would have its own
shared object and generator macro and have the states GameplayMenu,
GameplayInventory and GameplayAction in another directory like
states/gameplay/menu.nim.
Problems with this specific example include dealing with AST hygiene, code
size, performance (I would make it generate a giant function instead of using
function pointers, probably not the best idea), the code could turn into soggy
spaghetti... I could try to give another example
# main.nim
generateSubcommands("subcmds/")
var args = parse(commandLineParams())
chooseSubcommand(args)
Run
# subcmds/create_project.nim
writeFile("project.cfg", """
name = "new project"
version = 1.0""")
Run
I'm wondering if this design pattern in general is a good idea. Does it
decrease productivity or increase it? Do you guys use something similar or do
you have something to add? If I ever do something with these thoughts instead
of just talk about them I'll make sure to come back to this post and share it