proc main =
# in my case no need for boundary check
stack = array[10, int]
p = -1
# define some operations like PUSH, POP
PUSH(1)
let v = POP
In C both PUSH and POP can be easily defined by C macro. But in Nim I'm struggling to get this syntax work, especially for POP. If I use template:
template POP =
stack[p]; dec p
it fails to compile because stack[p] is regarded as a statement (if I understand correctly) and its return value has to be discarded.
I finally come up with the inline proc workaround:
proc POP(stack=stack, p:var int = p):int {. inline .} =
result = stack[p]
dec p
which I think isn't some really nice code. So my question is, is there a way to achieve the "mechanical substitution" like C macro in Nim? Or are there better solutions for my problem here? Thank you in advance!
template push(val: int) =
stack[p] = val
p.inc()
template pop(): int =
p.dec()
stack[p]
The relevant point isn't the stack logic (p++ vs p--) but that a template that is supposed to "return" something is supposed to have that something - in this case stack[p] - as last expression. So in your pop() version the template were to "return" the decremented p (if it could).
You could make it work however if you changed it to:
template POP =
let v = stack[p]; dec p; v
Side note: in Nim identifiers that start with a capital letter are supposed to be types, so "PUSH" and "POP" while being typical for C are a poor choice in Nim.
If your stack and p are defined globally and at the beginning (before PUSH and POP procs), you can do as simple as:
var
stack: array[10, int]
p = -1
proc PUSH(v: int) =
inc p
stack[p] = v
proc POP: int =
result = stack[p]
dec p
proc main =
PUSH 1
PUSH 7
let v = POP()
echo v
main()
Did you mean "textual substitution" instead?
Also, reading manual about template should provide you ideas on how you can do with it.
template POP: int =
result = stack[p]
dec p
and realized it won't work, then I went in the wrong direction (to put it in one line)...
That solves my probem, many thanks to you all.
You are welcome.
Plus another side note: Nim templates are (what C calls) macros albeit pimped up and cleaner. One important point to remember is that templates (unlike Nim macros) are basically but smart text substitution. This also means that any variables one refers to must either be defined in the calling context or be provided as parameters. And it means that a template "returning" something (like let x = mytemplate(...) can be looked at (in the "caller" as basically just "do everything but last template expression ... and then assign last expression to x".
So in my code above, if push and pop and "called" in, say, proc foo the following happens:
template push(val: int) =
stack[p] = val
p.inc()
template pop(): int =
p.dec()
stack[p]
push(bar)
# ... some other stuff
let r = pop()
comes down to
proc foo(...) =
var stack = array[10, int]
var p = 0
var bar = 5
# push(bar)
stack[p] = bar
p.inc()
# ... some other stuff
# let r = pop()
p.dec()
let r = stack[p]