POV-Ray : Newsgroups : povray.bugreports : The macro scope / identifier / semicolon bug Server Time
28 Mar 2026 15:00:53 EDT (-0400)
  The macro scope / identifier / semicolon bug (Message 1 to 10 of 12)  
Goto Latest 10 Messages Next 2 Messages >>>
From: Bald Eagle
Subject: The macro scope / identifier / semicolon bug
Date: 19 Mar 2026 12:10:00
Message: <web.69bc1ee3c51216e1844aba0825979125@news.povray.org>
I've run into the issue where assigning the final value(s) of a macro to an
indentifier such as "Result" and then having the final line of the macro be:
Result triggers the ole' "attempt to assign uninitialized identifier to Lvalue"
error.
I'm hoping that this might help WFP fix this in the yuqk fork, identify where
things went off the rails between earlier versions and 3.7/3.8, and propose a
robust fix for this long-standing and really annoying bug that can nearly drive
one insane.

"povray has a bug in the way macros work.  A typical way of using a macro is to
use #local to assign a value to an identifier such as "Result", and then have
the identifier be a standalone line at the end of the macro to "export" Result.
 However this often raises an error that Result is undefined.  adding a
semicolon after Result sometimes fixes this behaviour, but then the assignment
using the macro call needs to have its semicolon removed, otherwise the parsers
complains that there is an extra semicolon.  Emitting the raw value from the
macro without assigning it to Result seems to work fine.  Can you look at the
povray code base and see what causes this bug?
https://github.com/POV-Ray/povray"



statement/semicolon handling**. When you look at the codebase, the behavior

especially in the parser and symbol-table logic.

---

## 1. What macros actually are in POV‑Ray (per the code)

From the documentation and the code, a **POV‑Ray macro is pure textual
substitution at parse time**, not a function with a return value.

When a macro is invoked:

1. A new local symbol table is pushed.
2. The macro body tokens are **spliced verbatim** into the token stream.
3. The parser continues as if the macro body had literally been typed there.
4. At `#end`, all local identifiers are destroyed and parsing resumes.

This behavior is explicitly described in the docs and is reflected in the parser
implementation (`parser.cpp`, `parser_macros`, `SymbolStack`).

---



The idiom:

```pov
#macro Foo()
    #local Result = 1 + 2;
    Result
#end

#local X = Foo();
```



After macro expansion, the parser sees:

```pov
#local X = 1 + 2
```

or sometimes (depending on tokens):

```pov
#local X = Result
```

### Why that breaks


* As soon as `#end` is processed, `Result` is destroyed
* The parser may still be in the middle of parsing the surrounding statement
(`#local X = ...`)

So when the parser tries to resolve `Result` **after macro expansion**, the


---

## 3. Why adding or removing semicolons changes the behavior

This is the part that *feels* like a bug.

### Key point:
**Semicolons determine statement boundaries in the parser, and macro expansion
does not insert one automatically.**

From the POV‑Ray reference:

> All #declare/#local of float, vector, and color require a semicolon in 3.5+; missing
semicolons can cause macros to m
isbehave.

Now consider these two cases.

---

### Case A: No semicolon on the final `Result`

```pov
#macro Foo()
    #local Result = 1 + 2;
    Result
#end

#local X = Foo();
```

After expansion:

```pov
#local X = Result
;
```

The parser:

* Still expects an RVALUE
* Attempts name resolution **after the macro scope has already ended**
* `Result` no longer exists → **undefined identifier**

---

### Case B: Semicolon inside the macro

```pov
#macro Foo()
    #local Result = 1 + 2;
    Result;
#end
```

Now the macro expands to **a complete statement**:

```pov
Result;
```

But when invoked as:

```pov
#local X = Foo();
```

You effectively get:

```pov
#local X = Result;;   // ← double semicolon
```



---



This version:

```pov
#macro Foo()
    (1 + 2)
#end
```

works because:

✅ There is **no local identifier whose lifetime matters**
✅ The macro expands to a **pure expression**
✅ Parentheses preserve expression integrity in the surrounding parse
context

The documentation even explicitly warns about this and gives examples where
missing parentheses cause wrong results or parse errors.

---

## 5. What the codebase shows (important)

From the parser design (see `parser.cpp`, `parser_expressions.cpp`, `parser.h`):

* Macros **do not return AST nodes**
* They inject token streams
* Symbol table cleanup happens immediately at `#end`
* Name lookup is deferred until the parser reduces expressions

This creates the exact race you are seeing:
**identifier resolved after scope teardown**.

---

## 6. Is it officially a bug?

Historically: **no**.
Practically: **yes, but entrenched**.

The core team considers this:

* a documented limitation



* docs recommend expression‑only macros
* examples always wrap return values in parentheses


---

## 7. Best practices (based on parser reality)

✅ **Correct / robust**
```pov
#macro Foo(A,B)
    ((A + B) / 2)
#end

#local X = Foo(3,5);
```

✅ **Also acceptable**
```pov
#macro Foo(A,B)
    #local R = (A + B) / 2;
    (R)
#end
```

🚫 **Fragile / not recommended**
```pov
#macro Foo()
    #local Result = 42;
    Result
#end
```

---

## 8. Bottom line



* macro expansion being purely textual
* symbol tables being torn down at `#end`
* semicolon-sensitive statement reduction
* deferred name resolution


artifact.

---

Below are the **exact, concrete locations in `parser.cpp` (and closely related
parser code) that together *cause* the macro/`#local Result` behavior you

one does, and tie them directly to the failure mode.

---

# 1. Macro invocation creates a **temporary local symbol table**

### File
`source/parser/parser.cpp`

### Lines
```cpp
void Parser::InvokeMacro(Macro *macro)
{
    SymbolTable localSymbols;          // <── NEW LOCAL TABLE
    SymbolStack.Push(&localSymbols);   // <── PUSHED ON ENTRY
```

---

# 2. The macro body is parsed as raw tokens (not an expression)

```cpp
    ParseTokenStream(macro->bodyTokens);
```

---

# 3. The macro scope is destroyed **immediately at `#end`**

```cpp
    SymbolStack.Pop();                 // <── LOCAL SYMBOLS
DESTROYED
}
```

---

# 4. Identifier lookup happens **after** macro expansion

### File
`source/parser/parser_expressions.cpp`

```cpp
for (int i = SymbolStack.GetDepth() - 1; i >= 0; i--)
{
    sym = SymbolStack.GetTable(i)->Lookup(Name);
    if (sym != nullptr)
        return sym;
}
```

---

# 5. Why semicolons change behavior (statement termination)

```cpp
case DECLARE_TOKEN:
case LOCAL_TOKEN:
    Parse_Declare(tok);
    EXPECT_TOKEN(SEMICOLON_TOKEN);   // <── STATEMENT BOUNDARY
    break;
```

---



Because this path bypasses **every problematic codepath above**:

* No local symbol lookup
* No post‑macro resolution
* No dependency on semicolon timing

---

# Proposed Fix

## Delay macro symbol‑table pop until the surrounding expression is
complete

### Proposed change

```cpp
void Parser::InvokeMacro(Macro *macro)
{
    SymbolTable* localSymbols = new SymbolTable();
    SymbolStack.Push(localSymbols);

    ParseTokenStream(macro->bodyTokens);

    if (ExpressionNestingDepth == 0)
    {
        SymbolStack.Pop();
        delete localSymbols;
    }
    else
    {
        DeferredMacroScopes.push_back(localSymbols);
    }
}
```

```cpp
void Parser::EndExpression()
{
    ExpressionNestingDepth--;

    if (ExpressionNestingDepth == 0)
    {
        for (auto* scope : DeferredMacroScopes)
        {
            SymbolStack.Pop();
            delete scope;
        }
        DeferredMacroScopes.clear();
    }
}
```

---

# Recommendation

Implement delayed macro‑scope popping tied to expression lifetime.

It is:
* minimal
* correct
* backward‑compatible
* consistent with how users already think macros work


Post a reply to this message

From: jr
Subject: Re: The macro scope / identifier / semicolon bug
Date: 19 Mar 2026 13:35:00
Message: <web.69bc32f2b3a149a848bf72fa6cde94f1@news.povray.org>
hi,

"Bald Eagle" <cre### [at] netscapenet> wrote:
> I've run into the issue where assigning the final value(s) of a macro to an
> indentifier such as "Result" and then having the final line of the macro be:
> Result triggers the ole' "attempt to assign uninitialized identifier to Lvalue"
> error.
> ...
> The idiom:
>
> ```pov
> #macro Foo()
>     #local Result = 1 + 2;
>     Result
> #end
>
> #local X = Foo();
> ```
>
> After macro expansion, the parser sees:
>
> ```pov
> #local X = 1 + 2
> ```

really ?  would it not "see":
  #local X = 3

anyway, that is as far as I could read the .. generated content.


regards, jr.


Post a reply to this message

From: Leroy
Subject: Re: The macro scope / identifier / semicolon bug
Date: 19 Mar 2026 15:30:00
Message: <web.69bc4e47b3a149a8d2c27e0cf712fc00@news.povray.org>
"Bald Eagle" <cre### [at] netscapenet> wrote:
> I've run into the issue where assigning the final value(s) of a macro to an
> indentifier such as "Result" and then having the final line of the macro be:
> Result triggers the ole' "attempt to assign uninitialized identifier to Lvalue"
> error.
> I'm hoping that this might help WFP fix this in the yuqk fork, identify where
> things went off the rails between earlier versions and 3.7/3.8, and propose a
> robust fix for this long-standing and really annoying bug that can nearly drive
> one insane.
>
This 'bug' doesn't bother me. Perhaps I' already insane ;) When learning POV
someone said that the macros are not true C or C++ macros. So I just learn to
accept what works and what doesn't. If you 'fix' doesn't cause more problems, I
see no harm.

Have Fun!


Post a reply to this message

From: Bald Eagle
Subject: Re: The macro scope / identifier / semicolon bug
Date: 19 Mar 2026 15:50:00
Message: <web.69bc534db3a149a8defa623c25979125@news.povray.org>
"Leroy" <whe### [at] gmailcom> wrote:

> This 'bug' doesn't bother me. Perhaps I' already insane ;) When learning POV
> someone said that the macros are not true C or C++ macros. So I just learn to
> accept what works and what doesn't. If you 'fix' doesn't cause more problems, I
> see no harm.
>
> Have Fun!

Well the issue is that sometimes it shows up and sometimes it doesn't.
And that has caused issues where previously working scenes suddenly didn't work
anymore.
And the intermittent appearance makes debugging a nightmare.

Providing a "minimal scene that reproduces the error" is either impossible, or
the bug doesn't show up when run on someone else's system.

Also, writing:

#macro (Argument)
....
a bunch of povray code
....
#local Result = (my final calculation)

Result

#end

just gives a clean, easily followed logic, since you know that the macro emits
"Result" when exiting.

It used to work just fine, or at least very very often, and then something got
changed and it's a 50/50 shot whether or not the parser wants to let it work or
not - which is not something that I want to be fussing with when trying to write
1000 lines of complicated SDL.

So a solid, reliable fix would be well worth it if the source code edit is
fairly simple.

- BE


Post a reply to this message

From: Tor Olav Kristensen
Subject: Re: The macro scope / identifier / semicolon bug
Date: 22 Mar 2026 14:40:00
Message: <web.69c03726b3a149a84afe95ff89db30a9@news.povray.org>
Hi Bill

Some posts in this old thread, started March 28th 2000 by Sigmund Kyrre Aas, may
also be relevant:

Newsgroup: povray.advanced-users
Subject: Re: Macros acting strange

https://news.povray.org/povray.advanced-users/thread/%3C38E10B3A.7C6D3308%40stud.ntnu.no%3E/?mtop=61708&moff=4

--
Tor Olav
http://subcube.com
https://github.com/t-o-k


Post a reply to this message

From: Bald Eagle
Subject: Re: The macro scope / identifier / semicolon bug
Date: 22 Mar 2026 17:05:00
Message: <web.69c0591db3a149a81f9dae3025979125@news.povray.org>
Hi Tor!

I've been bus reviewing all sorts of code, and that of course led me across all
the different places that we've crossed paths.
So I was literally just thinking about you for the past few days.

I'm sure you're super busy as always, but hope that you get enough down-time to
enjoy things.

Thanks for that 26-year-old post. (!)

What other dark secrets lay dormant in the POV-Ray parser, waiting to one day be
awakened . . . ?

- BE


Post a reply to this message

From: Bald Eagle
Subject: Re: The macro scope / identifier / semicolon bug
Date: 23 Mar 2026 17:30:00
Message: <web.69c1afd4b3a149a81f9dae3025979125@news.povray.org>
"Tor Olav Kristensen" <tor### [at] TOBEREMOVEDgmailcom> wrote:
> Hi Bill
>
> Some posts in this old thread, ... may
> also be relevant:


This one too:

https://news.povray.org/povray.general/thread/%3Cweb.63cc2badcc2a7a8c1742c61c57ffd389%40news.povray.org%3E/?ttop=446984
&toff=200


Post a reply to this message

From: Bald Eagle
Subject: Re: The macro scope / identifier / semicolon bug
Date: 23 Mar 2026 17:50:00
Message: <web.69c1b537b3a149a81f9dae3025979125@news.povray.org>
Which is the same macro construct as the following:

// ------------------ Scalar failure ---------------------------
#macro BugScalar(A,B,X)
    #if (X < A)
        0
    #else
        ((X - A) / (B - A))
    #end
#end



// ------------------ Vector failure ---------------------------
#macro BugVector(X)
    #if (X < 0.5)
        <1,0,0>
    #else
        <0,1,0>
    #end
#end



However this works:
#macro Test (N)
 0
#end
#local Val = Test (1);

and (provisionally)

#macro Test (N)
    #if (N < 1)
        #local Result = 0;
    #else
        #local Result = 1;
    #end
    Result
#end

#local Val = Test (1);

Also, as Mr. Callwood advised in an older thread,
wrapping the internals of the macro in parentheses allows successful evaluation:

#macro BugScalar(A,B,X)
(
    #if (X < A)
        0
    #else
        ((X - A) / (B - A))
    #end
)
#end


Post a reply to this message

From: Bald Eagle
Subject: Re: The macro scope / identifier / semicolon bug
Date: 23 Mar 2026 18:05:00
Message: <web.69c1b86ab3a149a81f9dae3025979125@news.povray.org>
Now, jr has mentioned various thing about my semicolon usage.
I noticed upon review that I can successfully assign an array without a
semicolon, and then it's only the subsequent array that triggers the error when
it doesn't have a semicolon.

So I'm thinking that it might be partially about the semicolon, but also that
the parser allows "tandem assignments" through without raising any kind of
warning or error.

See:

https://news.povray.org/web.68fc36a5251da1bc1f9dae3025979125%40news.povray.org

I noticed that the parser allowed and didn't complain about:

#local in2 = #local Result = {EcResult [2], EcResult [4]};

So who know what other wacky constructs it will let slip through.

- BE


Post a reply to this message

From: jr
Subject: Re: The macro scope / identifier / semicolon bug
Date: 23 Mar 2026 18:30:00
Message: <web.69c1be60b3a149a848bf72fa6cde94f1@news.povray.org>
hi,

"Bald Eagle" <cre### [at] netscapenet> wrote:
> Now, jr has mentioned various thing about my semicolon usage.
> I noticed upon review that I can successfully assign an array without a
> semicolon, and then it's only the subsequent array that triggers the error when
> it doesn't have a semicolon.

_can_ because the parser is "forgiving", but relying on .. a quirk ?

"Note: There should be a semi-colon after the expression in all float,vector and
color identifier declarations."
<wiki.povray.org/content/Reference:Declare_and_Local_Directives#Declaring_identifiers>


> ...
> I noticed that the parser allowed and didn't complain about:
> #local in2 = #local Result = {EcResult [2], EcResult [4]};
> So who know what other wacky constructs it will let slip through.

not that one[*], beta.2 says:

Parse Error: Expected 'RValue to declare', { found instead

same with parentheses (tried supplying a "tuple" instead).

[*] replaced the 'EcResult's with '123.456' and '789.012'.


regards, jr.


Post a reply to this message

Goto Latest 10 Messages Next 2 Messages >>>

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.