Hello, it's me again!
I haven't done much with macros in Nim, so I need some help.
I need to generate an if statement on runtime with data passed by the user, since I don't really want the user to have to modify the source code of my program each time they need to run it with a different file.
In the file I'm reading, there are two kinds of particles, "balls" and "minerals", and they all have IDs. Everything in a certain range is a ball, and everything in another range is a mineral. Based of the IDs, I need my function needs to determine the kind of collision that happened in each line of the file, be it BallBall, BallMineral, MineralMineral, or None as an error case. Simple enough.
I've got this function which is the basis of the logic of my program, where the give parameters say that everything under 700_000 is a ball, and that everything above 700_000 is a mineral:
proc collisionType(ids: array[2, float]): Collision =
result =
if (ids[0] < 700_000) and (ids[1] < 700_000):
BallBall
elif ((ids[0] < 700_000) and (ids[1] > 700_000)) or
((ids[0] > 700_000) and (ids[1] < 700_000)):
BallMineral
elif (ids[0] > 700_000) and (ids[1] > 700_000):
MineralMineral
else:
None # This case should never happen, raises an expression if later on if it does.
The problem is that most recent simulations have several layers of materials, so you'll have a layer of balls under a layer of minerals under another layer of balls under another layer of minerals, etc. So collisionType has to be tailored to the specific file it's reading with input from the user, since the user knows the ranges of the IDs.
For example, let's say that we have Balls at 1..< 700_000, Minerals at 700_001..<1_400_000, Balls again at 1_400_001..<2_100_000, and Minerals from 2_100_001 onward. This is how collisionType would have to look like:
proc collisionType(ids: array[2, float]): Collision =
## This is horrible.
result =
if ((ids[0] < 700_000) and (ids[1] < 700_000)) or
((ids[0] < 700_000) and (ids[1] in 1_400_001..<2_100_000)) or
((ids[0] in 1_400_001..<2_100_000) and (ids[1] < 700_000)) or
((ids[0] in 1_400_001..<2_100_000) and (ids[1] in 1_400_001..<2_100_000)):
BallBall
elif ((ids[0] < 700_000) and (ids[1] in 700_001..<1_400_000)) or
((ids[0] in 700_001..<1_400_000) and (ids[1] < 700_000)) or
((ids[0] in 1_400_001..<2_100_000) and (ids[1] in 700_001..<1_400_000)) or
((ids[0] in 700_001..<1_400_000) and (ids[1] in 1_400_001..<2_100_000)) or
((ids[0] < 700_000) and (ids[1] > 2_100_000)) or
((ids[0] > 2_100_000) and (ids[1] < 700_000)) or
((ids[0] in 1_400_001..<2_100_000) and (ids[1] > 20_100_000)) or
((ids[0] > 20_100_000) and (ids[1] 1_400_001..<2_100_000)):
BallMineral
elif ((ids[0] in 700_001..<1_400_000) and (ids[1] in 700_001..<1_400_000)) or
((ids[0] in 700_001..<1_400_000) and (ids[1] > 2_100_001)) or
((ids[0] > 2_100_001) and (ids[1] in 700_001..<1_400_000)) or
((ids[0] > 2_100_001) and (ids[1] > 2_100_001)):
MineralMineral
else:
None
Not only is this horrible and easy to mess up (the exact reason why I don't want the user to edit the code every time they need to change the file they're reading), but I also don't know how many layers there will be in each simulation nor what the ranges will be, only the user does. So I believe I'll need to have a macro that receives the input from the user and does the dirty work for them (that is, generating a version of collisionType tailored to the file they're working with, based on their input).
Does anyone have any ideas on how I could pull this off?
Macros do not work at runtime, but I believe I wrote something that solves your issue.
import strscans
let input ="1,700000"
type Collision = enum
none, ball, mineral
proc collisionType(ids: array[2, float], userInput: string): (Collision, Collision) =
var start, increment: int
if userInput.scanf("$i,$i", start, increment):
var steps = 0
while result[0] == none or result[1] == none:
let rng = start.float..(start.float + increment.float)
if ids[0] in rng:
case steps.mod(2):
of 1:
result[0] = mineral
of 0:
result[0] = ball
else: discard
if ids[1] in rng:
case steps.mod(2):
of 1:
result[1] = mineral
of 0:
result[1] = ball
else: discard
inc steps
start += increment
echo collisionType([10.5, 300], input)
echo collisionType([700001.0, 1000000.0], input)
echo collisionType([2.0, 100000000.0], input)
Oh well, there goes my plan out the window...
The problem is that the increments aren't always the same between the ranges :(
Oh gosh, I feel so silly. I could've come up with that on my own...
I'll have to do a few modifications, but this is a good start.
Thank you so much for your help!