two real, previously-latent bugs found while building this Custom tag definitions used to be entirely flat: every <tag name="..."> - regardless of where in the script it appeared - registered into a single global.tags map, visible everywhere for the rest of the script. Now a <tag> defined directly inside another custom tag's own invocation (i.e. nested inside a __dotag instance's own expansion, not just sitting inside an <if>/<for>/<while> that happens to be there - those remain transparent) is scoped to that invocation: visible only for the rest of that specific expansion, gone once it finishes. Definitions at the true top level (or inside control-flow tags that are themselves at the top level) still register globally, unchanged. Implementation: Tag gained isCustomTagBoundary (true only on __dotag instances) and localTags (populated only on boundary instances) - both public rather than protected, since __tag.cpp needs to read/write them through an arbitrary ancestor Tag* found by walking parent, and C++ protected access doesn't extend to another object reached through a base-typed pointer. findTagDefinition() walks from `this` up through `parent` looking for the nearest boundary whose localTags has the name, falling back to global.tags - used by both ifTagDefined() (is <name> visible right here?) and __dotag's own constructor (fetch the definition to expand). __tag.cpp's registration does the mirror walk starting from `parent` (skip self, a <tag> definition is never itself a boundary). Two real bugs found and fixed along the way, neither introduced by this feature, both confirmed via a hung/broken real jet-2.0 run before diagnosis rather than assumed: 1. __tag.cpp's own base Tag(...) call passed (this, this) as its own parent/local instead of the parent/local it actually received - made the new scope-walk loop on itself forever (since <tag>'s own `parent` would just point at itself). Fixed to pass through the real received parent/local; nothing else in this class depended on the old self-referential values (a <tag> definition's own container is never evaluated at definition time - evaluate=false - so parent/local were never used for anything else here). 2. __dotag never overrode hasContainer, which Tag::processContainer gates real rendering on - and the base constructor sets hasContainer from whether THIS INVOCATION was self-closing (<name /> vs <name>...</name>), which is the right question for capturing an invocation's own nested content, but the wrong one for whether the tag's OWN DEFINITION should render. Net effect, confirmed via a real run: <name /> always silently rendered nothing for ANY custom tag - only the <name></name> form ever worked. Fixed by forcing hasContainer=true in __dotag's constructor - by the time it's constructed a valid definition is guaranteed (dispatch only builds __dotag after ifTagDefined() already confirmed one exists), so there's always something to render regardless of invocation syntax. <dump> updated to match: its "DEFINED TAGS" section now renders the real tree currently in effect (global at the root, each enclosing custom-tag invocation nested one level deeper, outermost first) instead of a single flat list - if there's no active local scope, that's just the root with nothing nested under it. Verified via real jet-2.0 runs, not just compilation: a tag defined inside another tag's body is invokable from within that body and correctly NOT recognized (falls through to literal-text echo) outside it; the same holds through 3 levels of nesting; the dump tree correctly shows nested levels indented under their own "(inside X)" header. Full regression pass on <try>/<catch>, <if>/<else>, <while>, and dump's own flat top-level output - all still work unchanged.
73 lines
2.9 KiB
C++
73 lines
2.9 KiB
C++
#include "__dump.h"
|
|
#include "Exception.h"
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <vector>
|
|
|
|
namespace jet {
|
|
|
|
__dump::__dump(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) {
|
|
if(!keywordDefined("file"))
|
|
throw coreutils::Exception("file must be sppecified for dump tag.");
|
|
|
|
std::ofstream outFile(keywords["file"].str());
|
|
|
|
if(global.cgi) {
|
|
outFile << "*** CGI VARIABLES ***" << std::endl;
|
|
// for (auto i = global.cgiVariables.begin(); i != global.cgiVariables.end(); i++)
|
|
// outFile << i->first << "=[" << i->second << "]" << std::endl;
|
|
}
|
|
|
|
outFile << "*** GLOBAL VARIABLES ***" << std::endl;
|
|
for (auto i = global.variables.begin(); i != global.variables.end(); i++)
|
|
outFile << i->first << "=[" << i->second << "]" << std::endl;
|
|
|
|
outFile << "*** LOCAL VARIABLES ***" << std::endl;
|
|
for (auto i = local->variables.begin(); i != local->variables.end(); i++)
|
|
outFile << i->first << "=[" << i->second << "]" << std::endl;
|
|
|
|
outFile << "*** KEYWORD VALUES ***" << std::endl;
|
|
for (auto i = parent->keywords.begin(); i != parent->keywords.end(); i++)
|
|
outFile << i->first << "=[" << i->second << "]" << std::endl;
|
|
|
|
outFile << "*** COOKIES ***" << std::endl;
|
|
for (auto i = global.cookies.data.begin(); i != global.cookies.data.end(); i++)
|
|
outFile << i->first << "=[" << i->second << "]" << std::endl;
|
|
|
|
// Custom <tag name="..."> definitions now have real scoping - one
|
|
// defined directly inside another custom tag's own invocation is
|
|
// only visible for the rest of that invocation (see Tag.h's
|
|
// isCustomTagBoundary/localTags doc comment). Render the actual
|
|
// tree currently in effect at this point in the script: global at
|
|
// the root, then each enclosing custom-tag invocation nested one
|
|
// level deeper, outermost first - if there's no active local scope
|
|
// (this <dump> isn't running inside any custom tag's expansion),
|
|
// that's just the root with nothing nested under it.
|
|
outFile << "*** DEFINED TAGS ***" << std::endl;
|
|
outFile << "(global)" << std::endl;
|
|
for (auto i = global.tags.begin(); i != global.tags.end(); i++)
|
|
outFile << " " << i->first << std::endl;
|
|
|
|
std::vector<Tag *> scopeChain;
|
|
for (Tag *scope = this; scope; scope = scope->parent)
|
|
if (scope->isCustomTagBoundary)
|
|
scopeChain.push_back(scope);
|
|
|
|
int depth = 1;
|
|
for (auto it = scopeChain.rbegin(); it != scopeChain.rend(); ++it) {
|
|
Tag *boundary = *it;
|
|
coreutils::MString indent;
|
|
for (int d = 0; d < depth; d++)
|
|
indent << " ";
|
|
outFile << indent << "(inside " << boundary->name << ")" << std::endl;
|
|
for (auto i = boundary->localTags.begin(); i != boundary->localTags.end(); i++)
|
|
outFile << indent << " " << i->first << std::endl;
|
|
depth++;
|
|
}
|
|
|
|
outFile.close();
|
|
|
|
}
|
|
|
|
}
|