|
 |
what would it take to enable the creation or referencing of object identifiers
in povray? Something like the ability to cycle through "index_01" to "index_99"
with a #for loop, or to be able to create those identifiers and assign them
values using a #for loop?
Copilot said:
Copilot
Reasoned for 1s
Considering identifier creation constraints
Exploring dynamic identifiers in SDL
identifiers at parse time. The practical, supported way is to use arrays (or
generated include files) instead of synthesizing names. To make dynamic names
possible natively would require parser & symbol‑table changes (details
below).
identifiers by name.
Workable patterns you can use now (arrays; auto‑generated includes).
What it would take to add dynamic identifiers to POV‑Ray (concrete options
and the scope of work, with rough effort).
and directives like #declare, #local, #ifdef, #undef expect literal identifier
tokens (not strings).
povray
+1
The official language directives list includes #declare, #local, #macro, #while,
File I/O directives (#fopen/#read/#write) also expect literals in the file being
string into an identifier to be resolved dynamically.
povray
Consequence: a loop like
Plain Text
#for(I, 1, 99) // (not a real SDL directive)
#declare index_?? = ... // cannot build ?? from I
#end
``
2) Workable patterns you can use right now
Plain Text
#declare A1D_INDEX = array[100];
#declare I = 1;
#while (I <= 99)
#declare A1D_INDEX[I] = some_value(I); // whatever you compute
#declare I = I + 1;
#end
// Using:
object { A1D_INDEX[42] } // or scalar/vector/etc. depending on what you store
Arrays are first‑class in SDL and are the idiomatic substitute for a
family of numbered variables. You can nest arrays to emulate 2‑D
When you truly need separate identifiers (for human readability or library
conventions), auto‑generate an include file that contains the declarations
with literal names, then #include it.
Two ways:
Inside SDL: #fopen + #write to create a .inc file, then #include it. The file
you write must contain valid SDL with literal identifiers; on the next parse
pass, they exist.
povray
Outside SDL: use Pre_Frame_Command (INI) to run a script that writes a .inc (or
modifies inputs) before each frame is parsed; then #include it from your scene.
stackoverflow
+1
Example (SDL‑only):
Plain Text
#fopen OUT "gen_index.inc" write
#declare I = 1;
#while (I <= 99)
#write (OUT, "#declare index_", str(I, -2, 0), " = ", I*I, ";\n")
#declare I = I + 1;
#end
#fclose OUT
#include "gen_index.inc" // now index_01 .. index_99 exist as identifiers
Note: str(I, -2, 0) is just formatting; the key is that the generated file
contains literal index_01 tokens. The dynamic part happens before the parser
sees them.
github
3) What it would take to add dynamic identifiers to POV‑Ray
A) Add built‑ins for indirection (narrow change; low surface risk)
Introduce new functions that take strings and access the symbol table:
getvar("index_01") → returns the current value of that identifier
(scalar/vector/string/object handle).
setvar("index_"+str(I,-2,0), Value) → creates/updates the named identifier
in the current scope.
Optional: exists("name"), undef("name").
What changes:
Parser: recognize calls to these built‑ins and route them to a safe
symbol‑table API.
Symbol table: add LookupByString, AssignByString with scope rules matching
#declare/#local.
Type system: either return a tagged union (existing value representation) or
restrict to scalar/vector/string first.
error handling, tests, docs). This avoids new syntax and is easy to
version‑gate (e.g., require #version 3.9).
hws
+1
B) Allow computed identifiers in #declare/#local (bigger parser change)
Syntax idea:
Plain Text
#declare @("index_"+str(I,-2,0)) = Value;
#local @NameString = Value; // where NameString is a string variable
What changes:
Tokenizer: keep as is (the @(...) starts an expression).
Parser: for #declare/#local, accept either an identifier or the @(<string-expr>)
form; evaluate the string, then call the same symbol‑table API as in A.
Update #undef/#ifdef to optionally accept @(<string>).
consistent behavior with scope). Make sure #ifdef short‑circuits safely
and retains good error recovery.
mankier
C) Add associative containers (maps) to SDL (clean, but new concept)
Rather than touching identifiers, add a first‑class map type:
Plain Text
#declare M = map();
#declare I = 1;
#while (I <= 99)
M["index_"+str(I,-2,0)] = compute(I); // store values by string key
#declare I = I + 1;
#end
object { M["index_42"] } // retrieve later
What changes:
Value system: new container type.
Parser: literals & indexing syntax for maps; probably reuse string indexing
syntax as shown.
Runtime: storage, copy/clone semantics.
global namespace, but more intrusive.
Compatibility & safety considerations
Version‑gate everything behind #version (and Version= in INI) so older
scenes are unaffected. Versioned parsing behavior is already standard in
POV‑Ray.
stackoverflow
has clear notions of these scopes.)
line/column; carry that through.
programmersought
Recommendation
For a minimal, robust feature that unlocks your use case:
If human‑readable variable names are required, generate includes (Option B
engine.
povray
+1
If you want to propose a core enhancement, Option A (getvar/setvar/exists) is
the least invasive, easiest to review, and can ship behind #version without
breaking compatibility.
If you want, I can provide a tiny proof‑of‑concept patch sketch
(parser entry points and symbol‑table calls) for a getvar/setvar design
and a suite of SDL tests that mimic your index_01..index_99 loop.
parser.h
--- a/source/parser/parser.h
+++ b/source/parser/parser.h
@@
+#define POV_ENABLE_NAME_INDIRECTION 1 // PoC switch
Symbol table
--- a/source/parser/symboltable.h
+++ b/source/parser/symboltable.h
@@
class SymbolTable {
public:
// existing API ...
+ // LOOKUP by raw string (UTF-8 accepted; convert to internal as needed)
+ bool LookupByString(const std::string& name, Value& out) const;
+ // ASSIGN into current scope; behavior mirrors #declare/#local resolution
+ bool AssignByString(const std::string& name, const Value& v, bool
localPreferred);
+ // EXISTS?
+ bool ExistsByString(const std::string& name) const;
+ // UNDEF
+ bool UndefByString(const std::string& name);
};
--- a/source/parser/symboltable.cpp
+++ b/source/parser/symboltable.cpp
@@
bool SymbolTable::LookupByString(const std::string& name, Value& out) const
{
// NOTE: convert to internal key type if needed
const Identifier* id = FindIdentifierByAsciiName(name); // or create a helper
if(!id) return false;
return GetValueForIdentifier(*id, out);
}
bool SymbolTable::AssignByString(const std::string& name, const Value& v, bool
localPreferred)
{
Identifier* id = GetOrCreateIdentifierByAsciiName(name, localPreferred);
if(!id) return false;
return SetValueForIdentifier(*id, v); // follows existing assign rules
}
bool SymbolTable::ExistsByString(const std::string& name) const
{
const Identifier* id = FindIdentifierByAsciiName(name);
return id != nullptr && HasValue(*id);
}
bool SymbolTable::UndefByString(const std::string& name)
{
Identifier* id = FindIdentifierByAsciiName(name);
if(!id) return false;
return UndefIdentifier(*id);
}
parser.cpp
--- a/source/parser/parser.cpp
+++ b/source/parser/parser.cpp
@@
Value Parser::ParseFunctionOrIdentifier()
{
// ... existing code that parsed an identifier name into `name` and saw '('
if(IsFunctionCall()) {
std::string fname = CurrentIdentifierText(); // e.g., "getvar"
if( LanguageVersion() >= 3.9
&& POV_ENABLE_NAME_INDIRECTION
&& (fname == "getvar" || fname == "setvar" || fname == "exists" ||
fname == "undefvar"))
{
return ParseNameIndirectionBuiltin(fname);
}
// else fall through to regular function parsing
}
// normal path...
}
``
--- a/source/parser/parser.cpp
+++ b/source/parser/parser.cpp
@@
Value Parser::ParseNameIndirectionBuiltin(const std::string& fname)
{
ExpectToken('(');
// parse first arg: any expression that must reduce to string *now*
Value nameVal = ParseExpression();
if(!nameVal.IsString())
Error("First argument to %s(...) must be a string.", fname.c_str());
std::string varName = nameVal.AsStdString();
if(fname == "getvar") {
ExpectMaybeCommaThen(')');
Value out;
if(!symtab.LookupByString(varName, out))
Error("getvar(\"%s\"): undefined identifier.", varName.c_str());
return out; // may be scalar, vector, string, object ref, etc.
}
else if(fname == "exists") {
ExpectMaybeCommaThen(')');
bool ok = symtab.ExistsByString(varName);
return Value::FromFloat(ok ? 1.0 : 0.0);
}
else if(fname == "undefvar") {
ExpectMaybeCommaThen(')');
bool ok = symtab.UndefByString(varName);
return Value::FromFloat(ok ? 1.0 : 0.0);
}
else if(fname == "setvar") {
ExpectToken(',');
Value rhs = ParseExpression(); // any kind of value
ExpectToken(')');
// localPreferred: true if inside a macro scope; you can refine rule
bool localPreferred = InLocalScope();
bool ok = symtab.AssignByString(varName, rhs, localPreferred);
if(!ok)
Error("setvar(\"%s\", ...): assignment failed.", varName.c_str());
// setvar returns the assigned value (convenient for chaining)
return rhs;
}
// unreachable
}
Post a reply to this message
|
 |