#ifndef __JetParser_h__ #define __JetParser_h__ #include "JetAST.h" #include #include #include namespace jetc { // Standalone parser for a subset of the JET template grammar. Mirrors the // character-level grammar implemented by JetCore's Tag::parseContainer / // Tag::Tag (attribute scanning, same-tag-name nesting depth, the // split) closely enough for well-formed templates, but is a fresh // implementation - it does not reuse JetCore's Tag classes, since those // fuse parsing and execution together (a real parse/execute split there // is a separate, larger refactor). Anything outside the modeled subset // (set/if/for/while/$[...]/literal text) is captured as an exact raw // source span and left for the existing interpreter (Tag::interpretSpan) // to run at compiled-program runtime, so unsupported constructs degrade // gracefully instead of failing to compile. class Parser { public: // topLevel is false for any nested sub-parse (an // // body, via parseSub) and true for the file's own top-level flow and // for anything spliced in from an (which inherits the // includer's own topLevel, since it's meant to read as if written // inline at that point - see parseInclude). Only used to decide // whether a definition is eligible for compilation // (see parseTagDef) - one found at topLevel==false (nested inside // another construct) stays FALLBACK, unchanged, matching __tag.cpp's // own locally-scoped-registration semantics that only a true // top-level definition is guaranteed to always be registered by the // time any invocation could reach it. explicit Parser(const std::string &src, std::string baseDir = "", int includeDepth = 0, bool topLevel = true) : s(src), pos(0), baseDir(baseDir), includeDepth(includeDepth), topLevel(topLevel) {} std::vector parseNodes(); private: std::string s; size_t pos; size_t nextId = 0; std::string baseDir; int includeDepth; bool topLevel; bool eof() const { return pos >= s.size(); } bool starts(const std::string &lit) const; static bool isNameChar(char c); void skipWs(); std::string readName(); std::string peekTagName(size_t at) const; bool matchTagOpen(const std::string &name, size_t at) const; bool matchTagClose(const std::string &name, size_t at) const; // p points at '<' of some tag's opening; advances p past its '>' or // "/>", skipping quoted attribute values. Returns true if self-closing. bool skipOpenTag(size_t &p); void skipQuotedValue(size_t &p); void skipEmbeddedTag(size_t &p); std::map parseAttrs(bool &hasContainer); // Scans forward from bodyStart (just after the opening tag's '>') for // tagName's matching close, tracking same-tag-name nesting depth and // (if lookForElse) a depth==1 "" split point. // Returns {closePos, elsePos} (elsePos == npos if not found/not sought). std::pair scanBody(size_t bodyStart, const std::string &tagName, bool lookForElse); std::vector parseSub(size_t start, size_t end); // is spliced in at compile time - read the // referenced file (resolved relative to baseDir) and parse ITS // content as if it had been written inline here, so everything in // it (custom definitions included) goes through this real // compiler, not the separate runtime interpreter (which has its own, // independently-buggy nested-tag handling). Returns the parsed nodes // to be spliced into the caller's own node list. std::vector parseInclude(); NodePtr parseVar(); NodePtr parseSet(); NodePtr parseIf(); NodePtr parseFor(); NodePtr parseWhile(); NodePtr parseFallback(); // ... at true top level (see topLevel above) - // compiles the definition body with a nested (topLevel=false) parse // instead of falling back to raw text, so jetc can generate a real // render function for it (see JetCodegen). Falls back to an ordinary // FALLBACK node (same as today) if the definition has no container // or no literal name= - degrades gracefully rather than failing to // compile. NodePtr parseTagDef(); // (any case/attributes/closing style, always void - see // isVoidTag) - a leaf node; the actual rendering logic lives in // Tag::renderContainer, called from generated code exactly as the // interpreter's own dispatch calls it today. NodePtr parseContainerSlot(); std::string newId(const std::string &prefix) { return prefix + std::to_string(nextId++) + "_"; } }; } #endif