Commit a187141056

Loris Cro <kappaloris@gmail.com>
2023-07-14 16:27:09
Autodoc tokenizer (#16409)
* autodoc: init work to refactor exprName * autodoc: Implement more expressions in exprName refactor * autodoc: more work * autodoc: More exprName to ex refactoring * autodoc: Remove whitespace flag from renderer; Add pre tags in value and variable drawing in renderContainer * autodoc: add inline styling to pre blocks * autodoc: move renderer code to main.js * autodoc: More exprName to ex refactoring; Fn signatures rendered with new code * autodoc: Fix function rendering. Add more things to ex * autodoc: nuke exprName --------- Co-authored-by: Krzysztof Wolicki <der.teufel.mail@gmail.com>
1 parent 3022c52
Changed files (4)
lib/docs/index.html
@@ -297,6 +297,13 @@
         overflow-x: auto;
       }
 
+      .docs pre.inline {
+        background-color: var(--bg-color);
+        padding: 0;
+        display: inline;
+      }
+
+
       .docs code {
         font-family: var(--mono);
         font-size: 1em;
@@ -685,15 +692,23 @@
   </style>
 
   <style>
-     pre{
+      pre {
         --zig-keyword: #333;
-        --zig-identifier: 'black'; 
+        --zig-builtin: #0086b3;
+        --zig-identifier: 'black';
+        --zig-string-literal: #d14;
+        --zig-type: #458;
+        --zig-fn: #900;
       }
       
       @media (prefers-color-scheme: dark) { 
         pre {
-          --zig-keyword: 'white';
-          --zig-identifier: 'purple'; 
+          --zig-keyword: #eee;
+          --zig-builtin: #ff894c;
+          --zig-identifier: 'purple';
+          --zig-string-literal: #2e5;
+          --zig-type: #68f;
+          --zig-fn: #e33;
         }
       }
     
@@ -748,7 +763,7 @@
     .zig_keyword_anytype,
     .zig_keyword_fn
     {
-      color: #333;
+      color: var(--zig-keyword);
       font-weight: bold;
     }
 
@@ -757,12 +772,12 @@
     .zig_multiline_string_literal_line,
     .zig_char_literal
     {
-      color: #d14;
+      color: var(--zig-string-literal);
     }
 
     .zig_builtin
     {
-      color: #0086b3;
+      color: var(--zig-builtin);
     }
 
     .zig_doc_comment,
@@ -772,15 +787,24 @@
     }
 
     .zig_identifier {
-      color: white;
+      color: var(--zig-identifier);
       font-weight: bold;
     }
 
-    .zig_number_literal {
-      color: #008080;
+    .zig_number_literal,
+    .zig_special {
+      color: #ff8080;
     }
 
+    .zig_type {
+      color: var(--zig-type);
+      font-weight: bold;
+    }
 
+    .zig_fn {
+      color: var(--zig-fn);
+      font-weight: bold;
+    }
    
     </style>
   </head>
lib/docs/main.js
@@ -7,7 +7,7 @@ const NAV_MODES = {
   GUIDES: "#G;",
 };
 
-(function () {
+(function() {
   const domBanner = document.getElementById("banner");
   const domMain = document.getElementById("main");
   const domStatus = document.getElementById("status");
@@ -59,7 +59,7 @@ const NAV_MODES = {
   const domDocs = document.getElementById("docs");
   const domGuidesSection = document.getElementById("guides");
   const domActiveGuide = document.getElementById("activeGuide");
-  
+
   const domListSearchResults = document.getElementById("listSearchResults");
   const domSectSearchNoResults = document.getElementById("sectSearchNoResults");
   const domSectInfo = document.getElementById("sectInfo");
@@ -133,7 +133,7 @@ const NAV_MODES = {
   // map of decl index to list of comptime fn calls
   // let nodesToCallsMap = indexNodesToCalls();
 
-  let guidesSearchIndex = {}; 
+  let guidesSearchIndex = {};
   window.guideSearch = guidesSearchIndex;
   parseGuides();
 
@@ -248,7 +248,7 @@ const NAV_MODES = {
     } else if (type.kind === typeKinds.Union) {
       name = "union";
     } else {
-      console.log("TODO: unhalndled case in typeShortName");
+      console.log("TODO: unhandled case in typeShortName");
       return null;
     }
 
@@ -313,8 +313,8 @@ const NAV_MODES = {
 
   function resolveGenericRet(genericFunc) {
     if (genericFunc.generic_ret == null) return null;
-    let result = resolveValue({expr: genericFunc.generic_ret});
-    
+    let result = resolveValue({ expr: genericFunc.generic_ret });
+
     let i = 0;
     while (true) {
       i += 1;
@@ -437,32 +437,32 @@ const NAV_MODES = {
     const section_list = zigAnalysis.guide_sections;
     resizeDomList(domGuidesList, section_list.length, '<div><h2><span></span></h2><ul class="modules"></ul></div>');
     for (let j = 0; j < section_list.length; j += 1) {
-        const section = section_list[j];
-        const domSectionName = domGuidesList.children[j].children[0].children[0];
-        const domGuides = domGuidesList.children[j].children[1];
-        domSectionName.textContent = section.name;
-        resizeDomList(domGuides, section.guides.length, '<li><a href="#"></a></li>');
-        for (let i = 0; i < section.guides.length; i += 1) {
-          const guide = section.guides[i];
-          let liDom = domGuides.children[i];
-          let aDom = liDom.children[0];
-          aDom.textContent = guide.title;
-          aDom.setAttribute("href", NAV_MODES.GUIDES + guide.name);
-          if (guide.name === curNav.activeGuide) {
-            aDom.classList.add("active");
-          } else {
-            aDom.classList.remove("active");
-          }
+      const section = section_list[j];
+      const domSectionName = domGuidesList.children[j].children[0].children[0];
+      const domGuides = domGuidesList.children[j].children[1];
+      domSectionName.textContent = section.name;
+      resizeDomList(domGuides, section.guides.length, '<li><a href="#"></a></li>');
+      for (let i = 0; i < section.guides.length; i += 1) {
+        const guide = section.guides[i];
+        let liDom = domGuides.children[i];
+        let aDom = liDom.children[0];
+        aDom.textContent = guide.title;
+        aDom.setAttribute("href", NAV_MODES.GUIDES + guide.name);
+        if (guide.name === curNav.activeGuide) {
+          aDom.classList.add("active");
+        } else {
+          aDom.classList.remove("active");
         }
+      }
     }
-  
+
     if (section_list.length > 0) {
       domGuidesMenu.classList.remove("hidden");
     }
 
-    
+
     if (curNavSearch !== "") {
-          return renderSearchGuides();
+      return renderSearchGuides();
     }
 
     // main content
@@ -476,8 +476,8 @@ const NAV_MODES = {
           break outer;
         }
       }
-    }        
-    
+    }
+
     if (activeGuide == undefined) {
       const root_file_idx = zigAnalysis.modules[zigAnalysis.rootMod].file;
       const root_file_name = getFile(root_file_idx).name;
@@ -564,7 +564,7 @@ Happy writing!
     renderModList();
 
     if (curNavSearch !== "") {
-          return renderSearchAPI();
+      return renderSearchAPI();
     }
 
     let rootMod = zigAnalysis.modules[zigAnalysis.rootMod];
@@ -660,7 +660,8 @@ Happy writing!
     if (!decl.decltest) return;
     const astNode = getAstNode(decl.decltest);
     domSectDocTests.classList.remove("hidden");
-    domDocTestsCode.innerHTML = generate_html_for_src(astNode.code);
+    domDocTestsCode.innerHTML = renderTokens(
+      DecoratedTokenizer(astNode.code, decl));
   }
 
   function renderUnknownDecl(decl) {
@@ -707,11 +708,7 @@ Happy writing!
     console.assert("type" in value.expr);
     let typeObj = getType(value.expr.type);
 
-    domFnProtoCode.innerHTML = exprName(value.expr, {
-      wantHtml: true,
-      wantLink: true,
-      fnDecl,
-    });
+    domFnProtoCode.innerHTML = renderTokens(ex(value.expr, { fnDecl: fnDecl }));
 
     domFnSourceLink.innerHTML = "[<a target=\"_blank\" href=\"" + sourceFileLink(fnDecl) + "\">src</a>]";
 
@@ -840,15 +837,21 @@ Happy writing!
 
       let value = typeObj.params[i];
       let preClass = docsNonEmpty ? ' class="fieldHasDocs"' : "";
-      let html = "<pre" + preClass + ">" + escapeHtml(fieldNode.name) + ": ";
-      if (isVarArgs && i === typeObj.params.length - 1) {
-        html += "...";
-      } else {
-        let name = exprName(value, { wantHtml: false, wantLink: false });
-        html += '<span class="tok-kw">' + name + "</span>";
-      }
+      let html = "<pre" + preClass + ">" + renderTokens((function*() {
+        yield Tok.identifier(fieldNode.name);
+        yield Tok.colon;
+        yield Tok.space;
+        if (isVarArgs && i === typeObj.params.length - 1) {
+          yield Tok.period;
+          yield Tok.period;
+          yield Tok.period;
+        } else {
+          yield* ex(value, {});
+        }
+        yield Tok.comma;
+      }()));
 
-      html += ",</pre>";
+      html += "</pre>";
 
       if (docsNonEmpty) {
         html += '<div class="fieldDocs">' + markdown(docs) + "</div>";
@@ -932,7 +935,7 @@ Happy writing!
       domSectMainMod.classList.remove("hidden");
     }
 
-    list.sort(function (a, b) {
+    list.sort(function(a, b) {
       return operatorCompare(a.name.toLowerCase(), b.name.toLowerCase());
     });
 
@@ -1054,823 +1057,578 @@ Happy writing!
     return walkResultTypeRef(resolved);
   }
 
-  function exprName(expr, opts) {
-    switch (Object.keys(expr)[0]) {
-      default:
-        throw "this expression is not implemented yet";
-      case "bool": {
-        if (expr.bool) {
-          return "true";
+  function* DecoratedTokenizer(src, context) {
+    let tok_it = Tokenizer(src);
+    for (let t of tok_it) {
+      if (t.tag == Tag.identifier) {
+        const link = detectDeclPath(t.src, context);
+        if (link) {
+          t.link = link;
         }
-        return "false";
-      }
-      case "&": {
-        return "&" + exprName(zigAnalysis.exprs[expr["&"]], opts);
       }
-      case "compileError": {
-        let compileError = expr.compileError;
-        return "@compileError(" + exprName(zigAnalysis.exprs[compileError], opts) + ")";
-      }
-      case "enumLiteral": {
-        let literal = expr.enumLiteral;
-        return "." + literal;
-      }
-      case "void": {
-        return "void";
+
+      yield t;
+    }
+  }
+
+
+  function renderSingleToken(t) {
+
+    if (t.tag == Tag.whitespace) {
+      return t.src;
+    }
+
+    let src = t.src;
+    // if (t.tag == Tag.identifier) {
+    //     src = escapeHtml(src);
+    // }
+    let result = "";
+    if (t.tag == Tag.identifier && isSimpleType(t.src)) {
+      result = `<span class="zig_type">${src}</span>`;
+    } else if (t.tag == Tag.identifier && isSpecialIndentifier(t.src)) {
+      result = `<span class="zig_special">${src}</span>`;
+    } else if (t.tag == Tag.identifier && t.fnDecl) {
+      result = `<span class="zig_fn">${src}</span>`;
+    } else {
+      result = `<span class="zig_${t.tag}">${src}</span>`;
+    }
+
+    if (t.link) {
+      result = `<a href="${t.link}">` + result + "</a>";
+    }
+
+    return result;
+  }
+
+  function renderTokens(tok_it) {
+    var html = [];
+
+    const max_iter = 100000;
+    let i = 0;
+    for (const t of tok_it) {
+      i += 1;
+      if (i > max_iter)
+        throw "too many iterations";
+
+      if (t.tag == Tag.eof)
+        break;
+
+      html.push(renderSingleToken(t));
+    }
+
+    return html.join("");
+  }
+
+  function* ex(expr, opts) {
+    switch (Object.keys(expr)[0]) {
+      default:
+        throw "this expression is not implemented yet: " + Object.keys(expr)[0];
+      case "comptimeExpr": {
+        const src = zigAnalysis.comptimeExprs[expr.comptimeExpr].code;
+        yield* DecoratedTokenizer(src);
+        return;
       }
-      case "unreachable": {
-        return "unreachable";
+      case "declName": {
+        yield { src: expr.declName, tag: Tag.identifier };
+        return;
       }
-      case "slice": {
-        let payloadHtml = "";
-        const lhsExpr = zigAnalysis.exprs[expr.slice.lhs];
-        const startExpr = zigAnalysis.exprs[expr.slice.start];
-        let decl = exprName(lhsExpr, opts);
-        let start = exprName(startExpr, opts);
-        let end = "";
-        let sentinel = "";
-        if (expr.slice["end"]) {
-          const endExpr = zigAnalysis.exprs[expr.slice.end];
-          let end_ = exprName(endExpr, opts);
-          end += end_;
+      case "declRef": {
+        const name = getDecl(expr.declRef).name;
+        const canonPath = getCanonDeclPath(expr.declRef);
+        if (canonPath) {
+          const link = navLink(canonPath.modNames, canonPath.declNames);
+          yield { src: name, tag: Tag.identifier, link };
+        } else {
+          yield { src: name, tag: Tag.identifier };
         }
-        if (expr.slice["sentinel"]) {
-          const sentinelExpr = zigAnalysis.exprs[expr.slice.sentinel];
-          let sentinel_ = exprName(sentinelExpr, opts);
-          sentinel += " :" + sentinel_;
+        return;
+      }
+      case "refPath": {
+        for (let i = 0; i < expr.refPath.length; i += 1) {
+          if (i > 0) yield Tok.period;
+          yield* ex(expr.refPath[i]);
         }
-        payloadHtml += decl + "[" + start + ".." + end + sentinel + "]";
-        return payloadHtml;
+        return;
+      }
+      case "fieldRef": {
+        const field_idx = expr.fieldRef.index;
+        const type = getType(expr.fieldRef.type);
+        const field = getAstNode(type.src).fields[field_idx];
+        const name = getAstNode(field).name;
+        yield { src: name, tag: Tag.identifier };
+        return;
       }
-      case "sliceLength": {
-        let payloadHtml = "";
-        const lhsExpr = zigAnalysis.exprs[expr.sliceLength.lhs];
-        const startExpr = zigAnalysis.exprs[expr.sliceLength.start];
-        const lenExpr = zigAnalysis.exprs[expr.sliceLength.len];
-        let decl = exprName(lhsExpr, opts);
-        let start = exprName(startExpr, opts);
-        let len = exprName(lenExpr, opts);
-        let sentinel = "";
-        if (expr.sliceLength["sentinel"]) {
-            const sentinelExpr = zigAnalysis.exprs[expr.sliceLength.sentinel];
-            let sentinel_ = exprName(sentinelExpr, options);
-            sentinel += " :" + sentinel_;
+      case "bool": {
+        if (expr.bool) {
+          yield { src: "true", tag: Tag.identifier };
+          return;
         }
-        payloadHtml += decl + "[" + start + "..][0.." + len + sentinel + "]";
-        return payloadHtml;
+        yield { src: "false", tag: Tag.identifier };
+        return;
       }
-      case "sliceIndex": {
-        const sliceIndex = zigAnalysis.exprs[expr.sliceIndex];
-        return exprName(sliceIndex, opts, opts);
+      case "&": {
+        yield { src: "&", tag: Tag.ampersand };
+        yield* ex(zigAnalysis.exprs[expr["&"]], opts);
+        return;
       }
-      case "cmpxchg": {
-        const typeIndex = zigAnalysis.exprs[expr.cmpxchg.type];
-        const ptrIndex = zigAnalysis.exprs[expr.cmpxchg.ptr];
-        const expectedValueIndex =
-          zigAnalysis.exprs[expr.cmpxchg.expected_value];
-        const newValueIndex = zigAnalysis.exprs[expr.cmpxchg.new_value];
-        const successOrderIndex = zigAnalysis.exprs[expr.cmpxchg.success_order];
-        const failureOrderIndex = zigAnalysis.exprs[expr.cmpxchg.failure_order];
-
-        const type = exprName(typeIndex, opts);
-        const ptr = exprName(ptrIndex, opts);
-        const expectedValue = exprName(expectedValueIndex, opts);
-        const newValue = exprName(newValueIndex, opts);
-        const successOrder = exprName(successOrderIndex, opts);
-        const failureOrder = exprName(failureOrderIndex, opts);
-
-        let fnName = "@";
-
-        switch (expr.cmpxchg.name) {
-          case "cmpxchg_strong": {
-            fnName += "cmpxchgStrong";
-            break;
-          }
-          case "cmpxchg_weak": {
-            fnName += "cmpxchgWeak";
+      case "call": {
+
+        let call = zigAnalysis.calls[expr.call];
+
+        switch (Object.keys(call.func)[0]) {
+          default:
+            throw "TODO";
+          case "declRef":
+          case "refPath": {
+            yield* ex(call.func, opts);
             break;
           }
-          default: {
-            console.log("There's only cmpxchg_strong and cmpxchg_weak");
+        }
+        yield Tok.l_paren;
+
+        for (let i = 0; i < call.args.length; i++) {
+          if (i != 0) {
+            yield Tok.comma;
+            yield Tok.space;
           }
+          yield* ex(call.args[i], opts);
         }
 
-        return (
-          fnName +
-          "(" +
-          type +
-          ", " +
-          ptr +
-          ", " +
-          expectedValue +
-          ", " +
-          newValue +
-          ", " +
-          "." +
-          successOrder +
-          ", " +
-          "." +
-          failureOrder +
-          ")"
-        );
+        yield Tok.r_paren;
+        return;
       }
-      case "cmpxchgIndex": {
-        const cmpxchgIndex = zigAnalysis.exprs[expr.cmpxchgIndex];
-        return exprName(cmpxchgIndex, opts);
+      case "sizeOf": {
+        const sizeOf = zigAnalysis.exprs[expr.sizeOf];
+        yield { src: "@sizeOf", tag: Tag.builtin };
+        yield { src: "(", tag: Tag.l_paren };
+        yield* ex(sizeOf, opts);
+        yield { src: ")", tag: Tag.r_paren };
+        return;
       }
-      case "switchOp": {
-        let condExpr = zigAnalysis.exprs[expr.switchOp.cond_index];
-        let ast = getAstNode(expr.switchOp.src);
-        let file_name = expr.switchOp.file_name;
-        let outer_decl_index = expr.switchOp.outer_decl;
-        let outer_decl = getType(outer_decl_index);
-        let line = 0;
-        // console.log(expr.switchOp)
-        // console.log(outer_decl)
-        while (outer_decl_index !== 0 && outer_decl.line_number > 0) {
-          line += outer_decl.line_number;
-          outer_decl_index = outer_decl.outer_decl;
-          outer_decl = getType(outer_decl_index);
-          // console.log(outer_decl)
-        }
-        line += ast.line + 1;
-        let payloadHtml = "";
-        let cond = exprName(condExpr, opts);
-
-        payloadHtml +=
-          "</br>" +
-          "node_name: " +
-          ast.name +
-          "</br>" +
-          "file: " +
-          file_name +
-          "</br>" +
-          "line: " +
-          line +
-          "</br>";
-        payloadHtml +=
-          "switch(" +
-          cond +
-          ") {" +
-          '<a href="/src/' +
-          file_name +
-          "#L" +
-          line +
-          '">' +
-          "..." +
-          "</a>}";
-        return payloadHtml;
+
+      case "as": {
+        const exprArg = zigAnalysis.exprs[expr.as.exprArg];
+        yield* ex(exprArg, opts);
+        return;
       }
-      case "switchIndex": {
-        const switchIndex = zigAnalysis.exprs[expr.switchIndex];
-        return exprName(switchIndex, opts);
+
+      case "int": {
+        yield { src: expr.int, tag: Tag.number_literal };
+        return;
       }
-      case "fieldRef": {
-        const field_idx = expr.fieldRef.index;
-        const type = getType(expr.fieldRef.type);
-        const field = getAstNode(type.src).fields[field_idx];
-        const name = getAstNode(field).name;
-        return name;
+
+      case "int_big": {
+        if (expr.int_big.negated) {
+          yield { src: "-", tag: Tag.minus };
+        }
+        yield { src: expr.int_big.value, tag: Tag.number_literal };
+        return;
       }
-      case "intFromEnum": {
-        const intFromEnum = zigAnalysis.exprs[expr.intFromEnum];
-        return "@intFromEnum(" + exprName(intFromEnum, opts) + ")";
+
+      case "float": {
+        yield { src: expr.float, tag: Tag.number_literal };
+        return;
       }
-      case "bitSizeOf": {
-        const bitSizeOf = zigAnalysis.exprs[expr.bitSizeOf];
-        return "@bitSizeOf(" + exprName(bitSizeOf, opts) + ")";
+
+      case "float128": {
+        yield { src: expr.float128, tag: Tag.number_literal };
+        return;
       }
-      case "sizeOf": {
-        const sizeOf = zigAnalysis.exprs[expr.sizeOf];
-        return "@sizeOf(" + exprName(sizeOf, opts) + ")";
+
+      case "array": {
+        yield { src: ".", tag: Tag.period };
+        yield Tok.l_brace;
+        for (let i = 0; i < expr.array.length; i++) {
+          if (i != 0) {
+            yield { src: ",", tag: Tag.comma };
+            yield Tok.space;
+          }
+          let elem = zigAnalysis.exprs[expr.array[i]];
+          yield* ex(elem, opts);
+        }
+        yield Tok.r_brace;
+        return;
       }
-      case "builtinIndex": {
-        const builtinIndex = zigAnalysis.exprs[expr.builtinIndex];
-        return exprName(builtinIndex, opts);
+
+      case "compileError": {
+        yield { src: "@compileError", tag: Tag.builtin };
+        yield Tok.l_paren;
+        yield* ex(zigAnalysis.exprs[expr.compileError], opts);
+        yield Tok.r_paren;
+        return;
       }
-      case "builtin": {
-        const param_expr = zigAnalysis.exprs[expr.builtin.param];
-        let param = exprName(param_expr, opts);
-
-        let payloadHtml = "@";
-        switch (expr.builtin.name) {
-          case "align_of": {
-            payloadHtml += "alignOf";
-            break;
-          }
-          case "int_from_bool": {
-            payloadHtml += "intFromBool";
-            break;
-          }
-          case "embed_file": {
-            payloadHtml += "embedFile";
-            break;
-          }
-          case "error_name": {
-            payloadHtml += "errorName";
-            break;
-          }
-          case "panic": {
-            payloadHtml += "panic";
-            break;
-          }
-          case "set_runtime_safety": {
-            payloadHtml += "setRuntimeSafety";
-            break;
-          }
-          case "sqrt": {
-            payloadHtml += "sqrt";
-            break;
-          }
-          case "sin": {
-            payloadHtml += "sin";
-            break;
+
+      case "string": {
+        yield { src: '"' + expr.string + '"', tag: Tag.string_literal };
+        return;
+      }
+
+      case "struct": {
+        yield Tok.period;
+        yield Tok.l_brace;
+        yield Tok.space;
+
+        for (let i = 0; i < expr.struct.length; i++) {
+          const fv = expr.struct[i];
+          const field_name = fv.name;
+          const field_value = ex(fv.val.expr, opts);
+          yield Tok.period;
+          yield { src: field_name, tag: Tag.identifier };
+          yield Tok.space;
+          yield Tok.eql;
+          yield Tok.space;
+          yield* field_value;
+          if (i !== expr.struct.length - 1) {
+            yield Tok.comma;
+            yield Tok.space;
+          } else {
+            yield Tok.space;
           }
-          case "cos": {
-            payloadHtml += "cos";
+        }
+        yield Tok.r_brace;
+        return;
+      }
+
+      case "binOpIndex": {
+        const binOp = zigAnalysis.exprs[expr.binOpIndex];
+        yield* ex(binOp, opts);
+        return;
+      }
+
+      case "binOp": {
+        const lhsOp = zigAnalysis.exprs[expr.binOp.lhs];
+        const rhsOp = zigAnalysis.exprs[expr.binOp.rhs];
+
+        if (lhsOp["binOpIndex"] !== undefined) {
+          yield Tok.l_paren;
+          yield* ex(lhsOp, opts);
+          yield Tok.r_paren;
+        } else {
+          yield* ex(lhsOp, opts);
+        }
+
+        yield Tok.space;
+
+        switch (expr.binOp.name) {
+          case "add": {
+            yield { src: "+", tag: Tag.plus };
             break;
           }
-          case "tan": {
-            payloadHtml += "tan";
+          case "addwrap": {
+            yield { src: "+%", tag: Tag.plus_percent };
             break;
           }
-          case "exp": {
-            payloadHtml += "exp";
+          case "add_sat": {
+            yield { src: "+|", tag: Tag.plus_pipe };
             break;
           }
-          case "exp2": {
-            payloadHtml += "exp2";
+          case "sub": {
+            yield { src: "-", tag: Tag.minus };
             break;
           }
-          case "log": {
-            payloadHtml += "log";
+          case "subwrap": {
+            yield { src: "-%", tag: Tag.minus_percent };
             break;
           }
-          case "log2": {
-            payloadHtml += "log2";
+          case "sub_sat": {
+            yield { src: "-|", tag: Tag.minus_pipe };
             break;
           }
-          case "log10": {
-            payloadHtml += "log10";
+          case "mul": {
+            yield { src: "*", tag: Tag.asterisk };
             break;
           }
-          case "fabs": {
-            payloadHtml += "fabs";
+          case "mulwrap": {
+            yield { src: "*%", tag: Tag.asterisk_percent };
             break;
           }
-          case "floor": {
-            payloadHtml += "floor";
+          case "mul_sat": {
+            yield { src: "*|", tag: Tag.asterisk_pipe };
             break;
           }
-          case "ceil": {
-            payloadHtml += "ceil";
+          case "div": {
+            yield { src: "/", tag: Tag.slash };
             break;
           }
-          case "trunc": {
-            payloadHtml += "trunc";
+          case "shl": {
+            yield { src: "<<", tag: Tag.angle_bracket_angle_bracket_left };
             break;
           }
-          case "round": {
-            payloadHtml += "round";
+          case "shl_sat": {
+            yield { src: "<<|", tag: Tag.angle_bracket_angle_bracket_left_pipe };
             break;
           }
-          case "tag_name": {
-            payloadHtml += "tagName";
+          case "shr": {
+            yield { src: ">>", tag: Tag.angle_bracket_angle_bracket_right };
             break;
           }
-          case "reify": {
-            payloadHtml += "Type";
+          case "bit_or": {
+            yield { src: "|", tag: Tag.pipe };
             break;
           }
-          case "type_name": {
-            payloadHtml += "typeName";
+          case "bit_and": {
+            yield { src: "&", tag: Tag.ampersand };
             break;
           }
-          case "frame_type": {
-            payloadHtml += "Frame";
+          case "array_cat": {
+            yield { src: "++", tag: Tag.plus_plus };
             break;
           }
-          case "frame_size": {
-            payloadHtml += "frameSize";
+          case "array_mul": {
+            yield { src: "**", tag: Tag.asterisk_asterisk };
             break;
           }
-          case "work_item_id": {
-            payloadHtml += "workItemId";
+          case "cmp_eq": {
+            yield { src: "==", tag: Tag.equal_equal };
             break;
           }
-          case "work_group_size": {
-            payloadHtml += "workGroupSize";
+          case "cmp_neq": {
+            yield { src: "!=", tag: Tag.bang_equal };
             break;
           }
-          case "work_group_id": {
-            payloadHtml += "workGroupId";
+          case "cmp_gt": {
+            yield { src: ">", tag: Tag.angle_bracket_right };
             break;
           }
-          case "int_from_ptr": {
-            payloadHtml += "intFromPtr";
+          case "cmp_gte": {
+            yield { src: ">=", tag: Tag.angle_bracket_right_equal };
             break;
           }
-          case "int_from_error": {
-            payloadHtml += "intFromError";
+          case "cmp_lt": {
+            yield { src: "<", tag: Tag.angle_bracket_left };
             break;
           }
-          case "error_to_int": {
-            payloadHtml += "errorFromInt";
+          case "cmp_lte": {
+            yield { src: "<=", tag: Tag.angle_bracket_left_equal };
             break;
           }
-          case "max": {
-            payloadHtml += "max";
+          case "bool_br_and": {
+            yield { src: "and", tag: Tag.keyword_and };
             break;
           }
-          case "min": {
-            payloadHtml += "min";
+          case "bool_br_or": {
+            yield { src: "or", tag: Tag.keyword_or };
             break;
           }
-          case "bit_not": {
-            return "~" + param;
-          }
-          case "bool_not": {
-            return "!" + param;
-          }
-          case "clz": {
-            return "@clz(T" + ", " + param + ")";
-          }
-          case "ctz": {
-            return "@ctz(T" + ", " + param + ")";
-          }
-          case "pop_count": {
-            return "@popCount(T" + ", " + param + ")";
-          }
-          case "byte_swap": {
-            return "@byteSwap(T" + ", " + param + ")";
-          }
-          case "bit_reverse": {
-            return "@bitReverse(T" + ", " + param + ")";
-          }
-          case "truncate": {
-            return "@truncate(" + param + ")";
-          }
           default:
-            console.log("builtin function not handled yet or doesn't exist!");
+            console.log("operator not handled yet or doesn't exist!");
         }
-        return payloadHtml + "(" + param + ")";
+
+        yield Tok.space;
+
+        if (rhsOp["binOpIndex"] !== undefined) {
+          yield Tok.l_paren;
+          yield* ex(rhsOp, opts);
+          yield Tok.r_paren;
+        } else {
+          yield* ex(rhsOp, opts);
+        }
+        return;
       }
+
       case "builtinBinIndex": {
         const builtinBinIndex = zigAnalysis.exprs[expr.builtinBinIndex];
-        return exprName(builtinBinIndex, opts);
+        yield* ex(builtinBinIndex, opts);
+        return;
       }
+
       case "builtinBin": {
         const lhsOp = zigAnalysis.exprs[expr.builtinBin.lhs];
         const rhsOp = zigAnalysis.exprs[expr.builtinBin.rhs];
-        let lhs = exprName(lhsOp, opts);
-        let rhs = exprName(rhsOp, opts);
 
-        let payloadHtml = "@";
+        let builtinName = "@";
         switch (expr.builtinBin.name) {
           case "int_from_float": {
-            payloadHtml += "intFromFloat";
+            builtinName += "intFromFloat";
             break;
           }
           case "float_from_int": {
-            payloadHtml += "floatFromInt";
+            builtinName += "floatFromInt";
             break;
           }
           case "ptr_from_int": {
-            payloadHtml += "ptrFromInt";
+            builtinName += "ptrFromInt";
             break;
           }
           case "enum_from_int": {
-            payloadHtml += "enumFromInt";
+            builtinName += "enumFromInt";
             break;
           }
           case "float_cast": {
-            payloadHtml += "floatCast";
+            builtinName += "floatCast";
             break;
           }
           case "int_cast": {
-            payloadHtml += "intCast";
+            builtinName += "intCast";
             break;
           }
           case "ptr_cast": {
-            payloadHtml += "ptrCast";
+            builtinName += "ptrCast";
             break;
           }
           case "const_cast": {
-            payloadHtml += "constCast";
+            builtinName += "constCast";
             break;
           }
           case "volatile_cast": {
-            payloadHtml += "volatileCast";
+            builtinName += "volatileCast";
             break;
           }
           case "truncate": {
-            payloadHtml += "truncate";
+            builtinName += "truncate";
             break;
           }
           case "has_decl": {
-            payloadHtml += "hasDecl";
+            builtinName += "hasDecl";
             break;
           }
           case "has_field": {
-            payloadHtml += "hasField";
+            builtinName += "hasField";
             break;
           }
           case "bit_reverse": {
-            payloadHtml += "bitReverse";
+            builtinName += "bitReverse";
             break;
           }
           case "div_exact": {
-            payloadHtml += "divExact";
+            builtinName += "divExact";
             break;
           }
           case "div_floor": {
-            payloadHtml += "divFloor";
+            builtinName += "divFloor";
             break;
           }
           case "div_trunc": {
-            payloadHtml += "divTrunc";
+            builtinName += "divTrunc";
             break;
           }
           case "mod": {
-            payloadHtml += "mod";
+            builtinName += "mod";
             break;
           }
           case "rem": {
-            payloadHtml += "rem";
+            builtinName += "rem";
             break;
           }
           case "mod_rem": {
-            payloadHtml += "rem";
+            builtinName += "rem";
             break;
           }
           case "shl_exact": {
-            payloadHtml += "shlExact";
+            builtinName += "shlExact";
             break;
           }
           case "shr_exact": {
-            payloadHtml += "shrExact";
+            builtinName += "shrExact";
             break;
           }
           case "bitcast": {
-            payloadHtml += "bitCast";
+            builtinName += "bitCast";
             break;
           }
           case "align_cast": {
-            payloadHtml += "alignCast";
+            builtinName += "alignCast";
             break;
           }
           case "vector_type": {
-            payloadHtml += "Vector";
+            builtinName += "Vector";
             break;
           }
           case "reduce": {
-            payloadHtml += "reduce";
+            builtinName += "reduce";
             break;
           }
           case "splat": {
-            payloadHtml += "splat";
+            builtinName += "splat";
             break;
           }
           case "offset_of": {
-            payloadHtml += "offsetOf";
+            builtinName += "offsetOf";
             break;
           }
           case "bit_offset_of": {
-            payloadHtml += "bitOffsetOf";
+            builtinName += "bitOffsetOf";
             break;
           }
           default:
             console.log("builtin function not handled yet or doesn't exist!");
         }
-        return payloadHtml + "(" + lhs + ", " + rhs + ")";
-      }
-      case "binOpIndex": {
-        const binOpIndex = zigAnalysis.exprs[expr.binOpIndex];
-        return exprName(binOpIndex, opts);
-      }
-      case "binOp": {
-        const lhsOp = zigAnalysis.exprs[expr.binOp.lhs];
-        const rhsOp = zigAnalysis.exprs[expr.binOp.rhs];
-        let lhs = exprName(lhsOp, opts);
-        let rhs = exprName(rhsOp, opts);
-
-        let print_lhs = "";
-        let print_rhs = "";
 
-        if (lhsOp["binOpIndex"]) {
-          print_lhs = "(" + lhs + ")";
-        } else {
-          print_lhs = lhs;
-        }
-        if (rhsOp["binOpIndex"]) {
-          print_rhs = "(" + rhs + ")";
-        } else {
-          print_rhs = rhs;
-        }
-
-        let operator = "";
-
-        switch (expr.binOp.name) {
-          case "add": {
-            operator += "+";
-            break;
-          }
-          case "addwrap": {
-            operator += "+%";
-            break;
-          }
-          case "add_sat": {
-            operator += "+|";
-            break;
-          }
-          case "sub": {
-            operator += "-";
-            break;
-          }
-          case "subwrap": {
-            operator += "-%";
-            break;
-          }
-          case "sub_sat": {
-            operator += "-|";
-            break;
-          }
-          case "mul": {
-            operator += "*";
-            break;
-          }
-          case "mulwrap": {
-            operator += "*%";
-            break;
-          }
-          case "mul_sat": {
-            operator += "*|";
-            break;
-          }
-          case "div": {
-            operator += "/";
-            break;
-          }
-          case "shl": {
-            operator += "<<";
-            break;
-          }
-          case "shl_sat": {
-            operator += "<<|";
-            break;
-          }
-          case "shr": {
-            operator += ">>";
-            break;
-          }
-          case "bit_or": {
-            operator += "|";
-            break;
-          }
-          case "bit_and": {
-            operator += "&";
-            break;
-          }
-          case "array_cat": {
-            operator += "++";
-            break;
-          }
-          case "array_mul": {
-            operator += "**";
-            break;
-          }
-          case "cmp_eq": {
-            operator += "==";
-            break;
-          }
-          case "cmp_neq": {
-            operator += "!=";
-            break;
-          }
-          case "cmp_gt": {
-            operator += ">";
-            break;
-          }
-          case "cmp_gte": {
-            operator += ">=";
-            break;
-          }
-          case "cmp_lt": {
-            operator += "<";
-            break;
-          }
-          case "cmp_lte": {
-            operator += "<=";
-            break;
-          }
-          case "bool_br_and": {
-            operator += "and";
-            break;
-          }
-          case "bool_br_or": {
-            operator += "or";
-            break;
-          }
-          default:
-            console.log("operator not handled yet or doesn't exist!");
-        }
-
-        return print_lhs + " " + operator + " " + print_rhs;
-      }
-      case "errorSets": {
-        const errUnionObj = getType(expr.errorSets);
-        let lhs = exprName(errUnionObj.lhs, opts);
-        let rhs = exprName(errUnionObj.rhs, opts);
-        return lhs + " || " + rhs;
-      }
-      case "errorUnion": {
-        const errUnionObj = getType(expr.errorUnion);
-        let lhs = exprName(errUnionObj.lhs, opts);
-        let rhs = exprName(errUnionObj.rhs, opts);
-        return lhs + "!" + rhs;
-      }
-      case "struct": {
-        // const struct_name =
-        //   zigAnalysis.decls[expr.struct[0].val.typeRef.refPath[0].declRef].name;
-        const struct_name = ".";
-        let struct_body = "";
-        struct_body += struct_name + "{ ";
-        for (let i = 0; i < expr.struct.length; i++) {
-          const fv = expr.struct[i];
-          const field_name = fv.name;
-          const field_value = exprName(fv.val.expr, opts);
-          // TODO: commented out because it seems not needed. if it deals
-          //       with a corner case, please add a comment when re-enabling it.
-          // let field_value = exprArg[Object.keys(exprArg)[0]];
-          // if (field_value instanceof Object) {
-          //   value_field = exprName(value_field)
-          //     zigAnalysis.decls[value_field[0].val.typeRef.refPath[0].declRef]
-          //       .name;
-          // }
-          struct_body += "." + field_name + " = " + field_value;
-          if (i !== expr.struct.length - 1) {
-            struct_body += ", ";
-          } else {
-            struct_body += " ";
-          }
-        }
-        struct_body += "}";
-        return struct_body;
-      }
-      case "typeOf_peer": {
-        let payloadHtml = "@TypeOf(";
-        for (let i = 0; i < expr.typeOf_peer.length; i++) {
-          let elem = zigAnalysis.exprs[expr.typeOf_peer[i]];
-          payloadHtml += exprName(elem, { wantHtml: true, wantLink: true });
-          if (i !== expr.typeOf_peer.length - 1) {
-            payloadHtml += ", ";
-          }
-        }
-        payloadHtml += ")";
-        return payloadHtml;
-      }
-      case "alignOf": {
-        const alignRefArg = zigAnalysis.exprs[expr.alignOf];
-        let payloadHtml =
-          "@alignOf(" +
-          exprName(alignRefArg, { wantHtml: true, wantLink: true }) +
-          ")";
-        return payloadHtml;
+        yield { src: builtinName, tag: Tag.builtin };
+        yield Tok.l_paren;
+        yield* ex(lhsOp, opts);
+        yield Tok.comma;
+        yield Tok.space;
+        yield* ex(rhsOp, opts);
+        yield Tok.r_paren;
+        return;
       }
-      case "typeOf": {
-        const typeRefArg = zigAnalysis.exprs[expr.typeOf];
-        let payloadHtml =
-          "@TypeOf(" +
-          exprName(typeRefArg, { wantHtml: true, wantLink: true }) +
-          ")";
-        return payloadHtml;
+
+      case "enumLiteral": {
+        let literal = expr.enumLiteral;
+        yield Tok.period;
+        yield { src: literal, tag: Tag.identifier };
+        return;
       }
-      case "typeInfo": {
-        const typeRefArg = zigAnalysis.exprs[expr.typeInfo];
-        let payloadHtml =
-          "@typeInfo(" +
-          exprName(typeRefArg, { wantHtml: true, wantLink: true }) +
-          ")";
-        return payloadHtml;
+
+      case "void": {
+        yield { src: "void", tag: Tag.identifier };
+        return;
       }
+
       case "null": {
-        if (opts.wantHtml) {
-          return '<span class="tok-null">null</span>';
-        } else {
-          return "null";
-        }
-      }
-      case "array": {
-        let payloadHtml = ".{";
-        for (let i = 0; i < expr.array.length; i++) {
-          if (i != 0) payloadHtml += ", ";
-          let elem = zigAnalysis.exprs[expr.array[i]];
-          payloadHtml += exprName(elem, opts);
-        }
-        return payloadHtml + "}";
-      }
-      case "comptimeExpr": {
-        return generate_html_for_src(zigAnalysis.comptimeExprs[expr.comptimeExpr].code);
+        yield { src: "null", tag: Tag.identifier };
+        return;
       }
-      case "call": {
-        let call = zigAnalysis.calls[expr.call];
-        let payloadHtml = "";
-
-        switch (Object.keys(call.func)[0]) {
-          default:
-            throw "TODO";
-          case "declRef":
-          case "refPath": {
-            payloadHtml += exprName(call.func, opts);
-            break;
-          }
-        }
-        payloadHtml += "(";
 
-        for (let i = 0; i < call.args.length; i++) {
-          if (i != 0) payloadHtml += ", ";
-          payloadHtml += exprName(call.args[i], opts);
-        }
-
-        payloadHtml += ")";
-        return payloadHtml;
-      }
-      case "as": {
-        // @Check : this should be done in backend because there are legit @as() calls
-        // const typeRefArg = zigAnalysis.exprs[expr.as.typeRefArg];
-        const exprArg = zigAnalysis.exprs[expr.as.exprArg];
-        // return "@as(" + exprName(typeRefArg, opts) +
-        //   ", " + exprName(exprArg, opts) + ")";
-        return exprName(exprArg, opts);
+      case "undefined": {
+        yield { src: "undefined", tag: Tag.identifier };
+        return;
       }
-      case "declRef": {
-        const name = getDecl(expr.declRef).name;
 
-        if (opts.wantHtml) {
-          let payloadHtml = "";
-          if (opts.wantLink) {
-            payloadHtml += '<a href="' + findDeclNavLink(name) + '">';
-          }
-          payloadHtml +=
-            '<span class="tok-kw" style="color:lightblue;">' +
-            name +
-            "</span>";
-          if (opts.wantLink) payloadHtml += "</a>";
-          return payloadHtml;
-        } else {
-          return name;
-        }
-      }
-      case "refPath": {
-        let firstComponent = expr.refPath[0];
-        let name = exprName(firstComponent, opts);
-        let url = undefined;
-        if (opts.wantLink && "declRef" in firstComponent) {
-          url = findDeclNavLink(getDecl(firstComponent.declRef).name);
-        }
-        for (let i = 1; i < expr.refPath.length; i++) {
-          let component = undefined;
-          if ("string" in expr.refPath[i]) {
-            component = expr.refPath[i].string;
-          } else {
-            component = exprName(expr.refPath[i], { ...opts, wantLink: false });
-            if (opts.wantLink && "declRef" in expr.refPath[i]) {
-              url += "." + getDecl(expr.refPath[i].declRef).name;
-              component = '<a href="' + url + '">' +
-                component +
-                "</a>";
-            }
-          }
-          name += "." + component;
-        }
-        return name;
-      }
-      case "int": {
-        return "" + expr.int;
-      }
-      case "float": {
-        return "" + expr.float.toFixed(2);
-      }
-      case "float128": {
-        return "" + expr.float128.toFixed(2);
+      case "anytype": {
+        yield { src: "anytype", tag: Tag.keyword_anytype };
+        return;
       }
-      case "undefined": {
-        return "undefined";
+
+      case "this": {
+        yield { src: "@This", tag: Tag.builtin };
+        yield Tok.l_paren;
+        yield Tok.r_paren;
+        return;
       }
-      case "string": {
-        return '"' + escapeHtml(expr.string) + '"';
+
+      case "typeInfo": {
+        const arg = zigAnalysis.exprs[expr.typeInfo];
+        yield { src: "@typeInfo", tag: Tag.builtin };
+        yield Tok.l_paren;
+        yield* ex(arg, opts);
+        yield Tok.r_paren;
+        return;
       }
 
-      case "int_big": {
-        return (expr.int_big.negated ? "-" : "") + expr.int_big.value;
+      case "switchIndex": {
+        const switchIndex = zigAnalysis.exprs[expr.switchIndex];
+        yield* ex(switchIndex, opts);
+        return;
       }
 
-      case "anytype": {
-        return "anytype";
+      case "errorSets": {
+        const errSetsObj = getType(expr.errorSets);
+        yield* ex(errSetsObj.lhs, opts);
+        yield Tok.space;
+        yield { src: "||", tag: Tag.pipe_pipe };
+        yield Tok.space;
+        yield* ex(errSetsObj.rhs, opts);
+        return;
       }
 
-      case "this": {
-        return "@This()";
+      case "errorUnion": {
+        const errUnionObj = getType(expr.errorUnion);
+        yield* ex(errUnionObj.lhs, opts);
+        yield { src: "!", tag: Tag.bang };
+        yield* ex(errUnionObj.rhs, opts);
+        return;
       }
 
       case "type": {
@@ -1880,416 +1638,389 @@ Happy writing!
         if (typeof typeObj === "number") typeObj = getType(typeObj);
         switch (typeObj.kind) {
           default:
-            throw "TODO";
+            throw "TODO: " + typeObj.kind;
+          case typeKinds.Type: {
+            yield { src: typeObj.name, tag: Tag.identifier };
+            return;
+          }
+          case typeKinds.Void: {
+            yield { src: "void", tag: Tag.identifier };
+            return;
+          }
+          case typeKinds.NoReturn: {
+            yield { src: "noreturn", tag: Tag.identifier };
+            return;
+          }
+          case typeKinds.ComptimeExpr: {
+            yield { src: "anyopaque", tag: Tag.identifier };
+            return;
+          }
+          case typeKinds.Bool: {
+            yield { src: "bool", tag: Tag.identifier };
+            return;
+          }
+          case typeKinds.ComptimeInt: {
+            yield { src: "comptime_int", tag: Tag.identifier };
+            return;
+          }
+          case typeKinds.ComptimeFloat: {
+            yield { src: "comptime_float", tag: Tag.identifier };
+            return;
+          }
+          case typeKinds.Int: {
+            yield { src: typeObj.name, tag: Tag.identifier };
+            return;
+          }
+          case typeKinds.Float: {
+            yield { src: typeObj.name, tag: Tag.identifier };
+            return;
+          }
+          case typeKinds.Array: {
+            yield Tok.l_bracket;
+            yield* ex(typeObj.len, opts);
+            if (typeObj.sentinel) {
+              yield Tok.colon;
+              yield* ex(typeObj.sentinel, opts);
+            }
+            yield Tok.r_bracket;
+            yield* ex(typeObj.child, opts);
+            return;
+          }
+          case typeKinds.Optional: {
+            yield { src: "?", tag: Tag.question_mark };
+            yield* ex(typeObj.child, opts);
+            return;
+          }
+          case typeKinds.Pointer: {
+            let ptrObj = typeObj;
+            switch (ptrObj.size) {
+              default:
+                console.log("TODO: implement unhandled pointer size case");
+              case pointerSizeEnum.One:
+                yield { src: "*", tag: Tag.asterisk };
+                break;
+              case pointerSizeEnum.Many:
+                yield Tok.l_bracket;
+                yield { src: "*", tag: Tag.asterisk };
+                if (ptrObj.sentinel !== null) {
+                  yield Tok.colon;
+                  yield* ex(ptrObj.sentinel, opts);
+                }
+                yield Tok.r_bracket;
+                break;
+              case pointerSizeEnum.Slice:
+                if (ptrObj.is_ref) {
+                  yield { src: "*", tag: Tag.asterisk };
+                }
+                yield Tok.l_bracket;
+                if (ptrObj.sentinel !== null) {
+                  yield Tok.colon;
+                  yield* ex(ptrObj.sentinel, opts);
+                }
+                yield Tok.r_bracket;
+                break;
+              case pointerSizeEnum.C:
+                yield Tok.l_bracket;
+                yield { src: "*", tag: Tag.asterisk };
+                yield { src: "c", tag: Tag.identifier };
+                if (typeObj.sentinel !== null) {
+                  yield Tok.colon;
+                  yield* ex(ptrObj.sentinel, opts);
+                }
+                yield Tok.r_bracket;
+                break;
+            }
+            if (!ptrObj.is_mutable) {
+              yield Tok.const;
+              yield Tok.space;
+            }
+            if (ptrObj.is_allowzero) {
+              yield { src: "allowzero", tag: Tag.keyword_allowzero };
+              yield Tok.space;
+            }
+            if (ptrObj.is_volatile) {
+              yield { src: "volatile", tag: Tag.keyword_volatile };
+            }
+            if (ptrObj.has_addrspace) {
+              yield { src: "addrspace", tag: Tag.keyword_addrspace };
+              yield Tok.l_paren;
+              yield Tok.period;
+              yield Tok.r_paren;
+            }
+            if (ptrObj.has_align) {
+              yield { src: "align", tag: Tag.keyword_align };
+              yield Tok.l_paren;
+              yield* ex(ptrObj.align, opts);
+              if (ptrObj.hostIntBytes !== undefined && ptrObj.hostIntBytes !== null) {
+                yield Tok.colon;
+                yield* ex(ptrObj.bitOffsetInHost, opts);
+                yield Tok.colon;
+                yield* ex(ptrObj.hostIntBytes, opts);
+              }
+              yield Tok.r_paren;
+              yield Tok.space;
+            }
+            yield* ex(ptrObj.child, opts);
+            return;
+          }
           case typeKinds.Struct: {
             let structObj = typeObj;
-            let name = "";
-            let layout = "";
             if (structObj.layout !== null) {
               switch (structObj.layout.enumLiteral) {
                 case "Packed": {
-                  layout = "packed ";
+                  yield { src: "packed", tag: Tag.keyword_packed };
                   break;
                 }
                 case "Extern": {
-                  layout = "extern ";
+                  yield { src: "extern", tag: Tag.keyword_extern };
                   break;
                 }
               }
+              yield Tok.space;
             }
-            if (opts.wantHtml) {
-              name = "<span class='tok-kw'>" + layout + "struct</span>";
-            } else {
-              name = layout + "struct";
-            }
+            yield { src: "struct", tag: Tag.keyword_struct };
             if (structObj.backing_int !== null) {
-              name += "(" + exprName(structObj.backing_int, opts) + ")";
+              yield Tok.l_paren;
+              yield* ex(structObj.backing_int, opts);
+              yield Tok.r_paren;
             }
-            name += " { ";
-            if (structObj.field_types.length > 1 && opts.wantHtml) { name += "</br>"; }
-            let indent = "";
-            if (structObj.field_types.length > 1 && opts.wantHtml) {
-              indent = "&nbsp;&nbsp;&nbsp;&nbsp;"
+            yield Tok.space;
+            yield Tok.l_brace;
+
+            if (structObj.field_types.length > 1) {
+              yield Tok.enter;
+            } else {
+              yield Tok.space;
             }
-            if (opts.indent && structObj.field_types.length > 1) {
-              indent = opts.indent + indent;
+
+            let indent = 0;
+            if (structObj.field_types.length > 1) {
+              indent = 1;
             }
-            let structNode = getAstNode(structObj.src);
-            let field_end = ",";
-            if (structObj.field_types.length > 1 && opts.wantHtml) {
-              field_end += "</br>";
-            } else {
-              field_end += " ";
+            if (opts.indent && structObj.field_types.length > 1) {
+              indent += opts.ident;
             }
 
+            let structNode = getAstNode(structObj.src);
             for (let i = 0; i < structObj.field_types.length; i += 1) {
               let fieldNode = getAstNode(structNode.fields[i]);
               let fieldName = fieldNode.name;
-              let html = indent;
-              if (!structObj.is_tuple) {
-                html += escapeHtml(fieldName);
+
+              for (let j = 0; j < indent; j += 1) {
+                yield Tok.tab;
               }
 
-              let fieldTypeExpr = structObj.field_types[i];
-              if (!structObj.is_tuple) {
-                html += ": ";
+              if (!typeObj.is_tuple) {
+                yield { src: fieldName, tag: Tag.identifier };
               }
 
-              html += exprName(fieldTypeExpr, { ...opts, indent: indent });
+              let fieldTypeExpr = structObj.field_types[i];
+              if (!typeObj.is_tuple) {
+                yield Tok.colon;
+                yield Tok.space;
+              }
+              yield* ex(fieldTypeExpr, { ...opts, indent: indent });
 
               if (structObj.field_defaults[i] !== null) {
-                html += " = " + exprName(structObj.field_defaults[i], opts);
+                yield Tok.space;
+                yield Tok.eql;
+                yield Tok.space;
+                yield* ex(structObj.field_defaults[i], opts);
               }
 
-              html += field_end;
-
-              name += html;
-            }
-            if (opts.indent && structObj.field_types.length > 1) {
-              name += opts.indent;
+              if (structObj.field_types.length > 1) {
+                yield Tok.comma;
+                yield Tok.enter;
+              } else {
+                yield Tok.space;
+              }
             }
-            name += "}";
-            return name;
+            yield Tok.r_brace;
+            return;
           }
           case typeKinds.Enum: {
             let enumObj = typeObj;
-            let name = "";
-            if (opts.wantHtml) {
-              name = "<span class='tok-kw'>enum</span>";
-            } else {
-              name = "enum";
-            }
+            yield { src: "enum", tag: Tag.keyword_enum };
             if (enumObj.tag) {
-              name += "(" + exprName(enumObj.tag, opts) + ")";
+              yield Tok.l_paren;
+              yield* ex(enumObj.tag, opts);
+              yield Tok.r_paren;
             }
-            name += " { ";
+            yield Tok.space;
+            yield Tok.l_brace;
+
             let enumNode = getAstNode(enumObj.src);
             let fields_len = enumNode.fields.length;
             if (enumObj.nonexhaustive) {
               fields_len += 1;
             }
-            if (fields_len > 1 && opts.wantHtml) { name += "</br>"; }
-            let indent = "";
+
+            if (fields_len > 1) {
+              yield Tok.enter;
+            } else {
+              yield Tok.space;
+            }
+
+            let indent = 0;
             if (fields_len > 1) {
-              if (opts.wantHtml) {
-                indent = "&nbsp;&nbsp;&nbsp;&nbsp;";
-              } else {
-                indent = "    ";
-              }
+              indent = 1;
             }
             if (opts.indent) {
-              indent = opts.indent + indent;
-            }
-            let field_end = ",";
-            if (fields_len > 1 && opts.wantHtml) {
-              field_end += "</br>";
-            } else {
-              field_end += " ";
+              indent += opts.indent;
             }
+
             for (let i = 0; i < enumNode.fields.length; i += 1) {
               let fieldNode = getAstNode(enumNode.fields[i]);
               let fieldName = fieldNode.name;
-              let html = indent + escapeHtml(fieldName);
+
+              for (let j = 0; j < indent; j += 1) yield Tok.tab;
+              yield { src: fieldName, tag: Tag.identifier };
 
               if (enumObj.values[i] !== null) {
-                html += " = " + exprName(enumObj.values[i], opts);
+                yield Tok.space;
+                yield Tok.eql;
+                yield Tok.space;
+                yield* ex(enumObj.values[i], opts);
               }
 
-              html += field_end;
-
-              name += html;
+              if (fields_len > 1) {
+                yield Tok.comma;
+                yield Tok.enter;
+              }
             }
-            if (enumObj.nonexhaustive) {
-              name += indent + "_" + field_end;
+            for (let j = 0; j < indent; j += 1) yield Tok.tab;
+            yield { src: "_", tag: Tag.identifier };
+            if (fields_len > 1) {
+              yield Tok.comma;
+              yield Tok.enter;
             }
             if (opts.indent) {
-              name += opts.indent;
+              for (let j = 0; j < opts.indent; j += 1) yield Tok.tab;
             }
-            name += "}";
-            return name;
+            yield Tok.r_brace;
+            return;
           }
           case typeKinds.Union: {
             let unionObj = typeObj;
-            let name = "";
-            let layout = "";
             if (unionObj.layout !== null) {
               switch (unionObj.layout.enumLiteral) {
                 case "Packed": {
-                  layout = "packed ";
+                  yield { src: "packed", tag: Tag.keyword_packed };
                   break;
                 }
                 case "Extern": {
-                  layout = "extern ";
+                  yield { src: "extern", tag: Tag.keyword_extern };
                   break;
                 }
               }
+              yield Tok.space;
             }
-            if (opts.wantHtml) {
-              name = "<span class='tok-kw'>" + layout + "union</span>";
-            } else {
-              name = layout + "union";
-            }
+            yield { src: "union", tag: Tag.keyword_union };
             if (unionObj.auto_tag) {
-              if (opts.wantHtml) {
-                name += "(<span class='tok-kw'>enum</span>";
-              } else {
-                name += "(enum";
-              }
+              yield Tok.l_paren;
+              yield { src: "enum", tag: Tag.keyword_enum };
               if (unionObj.tag) {
-                name += "(" + exprName(unionObj.tag, opts) + "))";
+                yield Tok.l_paren;
+                yield* ex(unionObj.tag, opts);
+                yield Tok.r_paren;
+                yield Tok.r_paren;
               } else {
-                name += ")";
+                yield Tok.r_paren;
               }
             } else if (unionObj.tag) {
-              name += "(" + exprName(unionObj.tag, opts) + ")";
+              yield Tok.l_paren;
+              yield* ex(unionObj.tag, opts);
+              yield Tok.r_paren;
             }
-            name += " { ";
-            if (unionObj.field_types.length > 1 && opts.wantHtml) {
-              name += "</br>";
+            yield Tok.space;
+            yield Tok.l_brace;
+            if (unionObj.field_types.length > 1) {
+              yield Tok.enter;
+            } else {
+              yield Tok.space;
             }
-            let indent = "";
-            if (unionObj.field_types.length > 1 && opts.wantHtml) {
-              indent = "&nbsp;&nbsp;&nbsp;&nbsp;"
+            let indent = 0;
+            if (unionObj.field_types.length > 1) {
+              indent = 1;
             }
             if (opts.indent) {
-              indent = opts.indent + indent;
+              indent += opts.indent;
             }
             let unionNode = getAstNode(unionObj.src);
-            let field_end = ",";
-            if (unionObj.field_types.length > 1 && opts.wantHtml) {
-              field_end += "</br>";
-            } else {
-              field_end += " ";
-            }
             for (let i = 0; i < unionObj.field_types.length; i += 1) {
               let fieldNode = getAstNode(unionNode.fields[i]);
               let fieldName = fieldNode.name;
-              let html = indent + escapeHtml(fieldName);
+              for (let j = 0; j < indent; j += 1) yield Tok.tab;
+              yield { src: fieldName, tag: Tag.identifier };
 
               let fieldTypeExpr = unionObj.field_types[i];
-              html += ": ";
+              yield Tok.colon;
+              yield Tok.space;
 
-              html += exprName(fieldTypeExpr, { ...opts, indent: indent });
+              yield* ex(fieldTypeExpr, { ...opts, indent: indent });
 
-              html += field_end;
-
-              name += html;
+              if (unionObj.field_types.length > 1) {
+                yield Tok.comma;
+                yield Tok.enter;
+              } else {
+                yield Tok.space;
+              }
             }
             if (opts.indent) {
-              name += opts.indent;
+              for (let j = 0; j < opts.indent; j += 1) yield Tok.tab;
             }
-            name += "}";
-            return name;
+            yield Tok.r_brace;
+            return;
           }
           case typeKinds.Opaque: {
-            let opaqueObj = typeObj;
-            return opaqueObj;
-          }
-          case typeKinds.ComptimeExpr: {
-            return "anyopaque";
-          }
-          case typeKinds.Array: {
-            let arrayObj = typeObj;
-            let name = "[";
-            let lenName = exprName(arrayObj.len, opts);
-            let sentinel = arrayObj.sentinel
-              ? ":" + exprName(arrayObj.sentinel, opts)
-              : "";
-            // let is_mutable = arrayObj.is_multable ? "const " : "";
-
-            if (opts.wantHtml) {
-              name +=
-                '<span class="tok-number">' + lenName + sentinel + "</span>";
-            } else {
-              name += lenName + sentinel;
-            }
-            name += "]";
-            // name += is_mutable;
-            name += exprName(arrayObj.child, opts);
-            return name;
-          }
-          case typeKinds.Optional:
-            return "?" + exprName(typeObj.child, opts);
-          case typeKinds.Pointer: {
-            let ptrObj = typeObj;
-            let sentinel = ptrObj.sentinel
-              ? ":" + exprName(ptrObj.sentinel, opts)
-              : "";
-            let is_mutable = !ptrObj.is_mutable ? "const " : "";
-            let name = "";
-            switch (ptrObj.size) {
-              default:
-                console.log("TODO: implement unhandled pointer size case");
-              case pointerSizeEnum.One:
-                name += "*";
-                name += is_mutable;
-                break;
-              case pointerSizeEnum.Many:
-                name += "[*";
-                name += sentinel;
-                name += "]";
-                name += is_mutable;
-                break;
-              case pointerSizeEnum.Slice:
-                if (ptrObj.is_ref) {
-                  name += "*";
-                }
-                name += "[";
-                name += sentinel;
-                name += "]";
-                name += is_mutable;
-                break;
-              case pointerSizeEnum.C:
-                name += "[*c";
-                name += sentinel;
-                name += "]";
-                name += is_mutable;
-                break;
-            }
-            // @check: after the major changes in arrays the consts are came from switch above
-            // if (!ptrObj.is_mutable) {
-            //     if (opts.wantHtml) {
-            //         name += '<span class="tok-kw">const</span> ';
-            //     } else {
-            //         name += "const ";
-            //     }
-            // }
-            if (ptrObj.is_allowzero) {
-              name += "allowzero ";
-            }
-            if (ptrObj.is_volatile) {
-              name += "volatile ";
-            }
-            if (ptrObj.has_addrspace) {
-              name += "addrspace(";
-              name += "." + "";
-              name += ") ";
-            }
-            if (ptrObj.has_align) {
-              let align = exprName(ptrObj.align, opts);
-              if (opts.wantHtml) {
-                name += '<span class="tok-kw">align</span>(';
-              } else {
-                name += "align(";
-              }
-              if (opts.wantHtml) {
-                name += '<span class="tok-number">' + align + "</span>";
-              } else {
-                name += align;
-              }
-              if (ptrObj.hostIntBytes != null) {
-                name += ":";
-                if (opts.wantHtml) {
-                  name +=
-                    '<span class="tok-number">' +
-                    ptrObj.bitOffsetInHost +
-                    "</span>";
-                } else {
-                  name += ptrObj.bitOffsetInHost;
-                }
-                name += ":";
-                if (opts.wantHtml) {
-                  name +=
-                    '<span class="tok-number">' +
-                    ptrObj.hostIntBytes +
-                    "</span>";
-                } else {
-                  name += ptrObj.hostIntBytes;
-                }
-              }
-              name += ") ";
-            }
-            //name += typeValueName(ptrObj.child, wantHtml, wantSubLink, null);
-            name += exprName(ptrObj.child, opts);
-            return name;
-          }
-          case typeKinds.Float: {
-            let floatObj = typeObj;
-
-            if (opts.wantHtml) {
-              return '<span class="tok-type">' + floatObj.name + "</span>";
-            } else {
-              return floatObj.name;
-            }
+            yield { src: "opaque", tag: Tag.keyword_opaque };
+            yield Tok.space;
+            yield Tok.l_brace;
+            yield Tok.r_brace;
+            return;
           }
-          case typeKinds.Int: {
-            let intObj = typeObj;
-            let name = intObj.name;
-            if (opts.wantHtml) {
-              return '<span class="tok-type">' + name + "</span>";
-            } else {
-              return name;
-            }
+          case typeKinds.EnumLiteral: {
+            yield { src: "(enum literal)", tag: Tag.identifier };
+            return;
           }
-          case typeKinds.ComptimeInt:
-            if (opts.wantHtml) {
-              return '<span class="tok-type">comptime_int</span>';
-            } else {
-              return "comptime_int";
-            }
-          case typeKinds.ComptimeFloat:
-            if (opts.wantHtml) {
-              return '<span class="tok-type">comptime_float</span>';
-            } else {
-              return "comptime_float";
-            }
-          case typeKinds.Type:
-            if (opts.wantHtml) {
-              return '<span class="tok-type">type</span>';
-            } else {
-              return "type";
-            }
-          case typeKinds.Bool:
-            if (opts.wantHtml) {
-              return '<span class="tok-type">bool</span>';
-            } else {
-              return "bool";
-            }
-          case typeKinds.Void:
-            if (opts.wantHtml) {
-              return '<span class="tok-type">void</span>';
-            } else {
-              return "void";
-            }
-          case typeKinds.EnumLiteral:
-            if (opts.wantHtml) {
-              return '<span class="tok-type">(enum literal)</span>';
-            } else {
-              return "(enum literal)";
-            }
-          case typeKinds.NoReturn:
-            if (opts.wantHtml) {
-              return '<span class="tok-type">noreturn</span>';
-            } else {
-              return "noreturn";
-            }
           case typeKinds.ErrorSet: {
             let errSetObj = typeObj;
-            if (errSetObj.fields == null) {
-              return '<span class="tok-type">anyerror</span>';
+            if (errSetObj.fields === null) {
+              yield { src: "anyerror", tag: Tag.identifier };
             } else if (errSetObj.fields.length == 0) {
-              return "error{}";
+              yield { src: "error", tag: Tag.keyword_error };
+              yield Tok.l_brace;
+              yield Tok.r_brace;
             } else if (errSetObj.fields.length == 1) {
-              return "error{" + errSetObj.fields[0].name + "}";
+              yield { src: "error", tag: Tag.keyword_error };
+              yield Tok.l_brace;
+              yield { src: errSetObj.fields[0].name, tag: Tag.identifier };
+              yield Tok.r_brace;
             } else {
-              // throw "TODO";
-              let html = "error{ " + errSetObj.fields[0].name;
-              for (let i = 1; i < errSetObj.fields.length; i++) html += ", " + errSetObj.fields[i].name;
-              html += " }";
-              return html;
+              yield { src: "error", tag: Tag.keyword_error };
+              yield Tok.l_brace;
+              yield { src: errSetObj.fields[0].name, tag: Tag.identifier };
+              for (let i = 1; i < errSetObj.fields.length; i++) {
+                yield Tok.comma;
+                yield Tok.space;
+                yield { src: errSetObj.fields[i].name, tag: Tag.identifier };
+              }
+              yield Tok.r_brace;
             }
+            return;
           }
-
           case typeKinds.ErrorUnion: {
             let errUnionObj = typeObj;
-            let lhs = exprName(errUnionObj.lhs, opts);
-            let rhs = exprName(errUnionObj.rhs, opts);
-            return lhs + "!" + rhs;
+            yield* ex(errUnionObj.lhs, opts);
+            yield { src: "!", tag: Tag.bang };
+            yield* ex(errUnionObj.rhs, opts);
+            return;
           }
           case typeKinds.InferredErrorUnion: {
             let errUnionObj = typeObj;
-            let payload = exprName(errUnionObj.payload, opts);
-            return "!" + payload;
+            yield { src: "!", tag: Tag.bang };
+            yield* ex(errUnionObj.payload, opts);
+            return;
           }
           case typeKinds.Fn: {
             let fnObj = typeObj;
@@ -2299,45 +2030,32 @@ Happy writing!
             opts.linkFnNameDecl = null;
             let payloadHtml = "";
             if (opts.addParensIfFnSignature && fnObj.src == 0) {
-              payloadHtml += "(";
+              yield Tok.l_paren;
             }
             if (fnObj.is_extern) {
-              if (opts.wantHtml) {
-                payloadHtml += '<span class="tok-kw">extern </span>';
-              } else {
-                payloadHtml += "extern ";
-              }
+              yield { src: "extern", tag: Tag.keyword_extern };
+              yield Tok.space;
             } else if (fnObj.has_cc) {
               let cc_expr = zigAnalysis.exprs[fnObj.cc];
               if (cc_expr.enumLiteral === "Inline") {
-                if(opts.wantHtml) {
-                payloadHtml += '<span class="tok-kw">inline </span>'
-                } else {
-                  payloadHtml += "inline "
-                }
+                yield { src: "inline", tag: Tag.keyword_inline };
+                yield Tok.space;
               }
             }
             if (fnObj.has_lib_name) {
-              payloadHtml += '"' + fnObj.lib_name + '" ';
+              yield { src: '"' + fnObj.lib_name + '"', tag: Tag.string_literal };
+              yield Tok.space;
             }
-            if (opts.wantHtml) {
-              payloadHtml += '<span class="tok-kw">fn </span>';
-              if (fnDecl) {
-                payloadHtml += '<span class="tok-fn">';
-                if (linkFnNameDecl) {
-                  payloadHtml +=
-                    '<a href="' + linkFnNameDecl + '">' +
-                    escapeHtml(fnDecl.name) +
-                    "</a>";
-                } else {
-                  payloadHtml += escapeHtml(fnDecl.name);
-                }
-                payloadHtml += "</span>";
+            yield { src: "fn", tag: Tag.keyword_fn };
+            yield Tok.space;
+            if (fnDecl) {
+              if (linkFnNameDecl) {
+                yield { src: fnDecl.name, tag: Tag.identifier, link: linkFnNameDecl, fnDecl: false };
+              } else {
+                yield { src: fnDecl.name, tag: Tag.identifier, fnDecl: true };
               }
-            } else {
-              payloadHtml += "fn ";
             }
-            payloadHtml += "(";
+            yield Tok.l_paren;
             if (fnObj.params) {
               let fields = null;
               let isVarArgs = false;
@@ -2349,13 +2067,10 @@ Happy writing!
 
               for (let i = 0; i < fnObj.params.length; i += 1) {
                 if (i != 0) {
-                  payloadHtml += ", ";
+                  yield Tok.comma;
+                  yield Tok.space;
                 }
 
-                if (opts.wantHtml) {
-                  payloadHtml +=
-                    "<span class='argBreaker'><br>&nbsp;&nbsp;&nbsp;&nbsp;</span>";
-                }
                 let value = fnObj.params[i];
                 let paramValue = resolveValue({ expr: value });
 
@@ -2363,24 +2078,20 @@ Happy writing!
                   let paramNode = getAstNode(fields[i]);
 
                   if (paramNode.varArgs) {
-                    payloadHtml += "...";
+                    yield Tok.period;
+                    yield Tok.period;
+                    yield Tok.period;
                     continue;
                   }
 
                   if (paramNode.noalias) {
-                    if (opts.wantHtml) {
-                      payloadHtml += '<span class="tok-kw">noalias</span> ';
-                    } else {
-                      payloadHtml += "noalias ";
-                    }
+                    yield { src: "noalias", tag: Tag.keyword_noalias };
+                    yield Tok.space;
                   }
 
                   if (paramNode.comptime) {
-                    if (opts.wantHtml) {
-                      payloadHtml += '<span class="tok-kw">comptime</span> ';
-                    } else {
-                      payloadHtml += "comptime ";
-                    }
+                    yield { src: "comptime", tag: Tag.keyword_comptime };
+                    yield Tok.space;
                   }
 
                   let paramName = paramNode.name;
@@ -2390,94 +2101,102 @@ Happy writing!
                       if (paramName === "") {
                         paramName = "_";
                       }
-                      payloadHtml += paramName + ": ";
+                      yield { src: paramName, tag: Tag.identifier };
+                      yield Tok.colon;
+                      yield Tok.space;
                     }
                   }
                 }
 
                 if (isVarArgs && i === fnObj.params.length - 1) {
-                  payloadHtml += "...";
+                  yield Tok.period;
+                  yield Tok.period;
+                  yield Tok.period;
                 } else if ("alignOf" in value) {
-                  payloadHtml += exprName(value, opts);
+                  yield* ex(value, opts);
                 } else if ("typeOf" in value) {
-                  payloadHtml += exprName(value, opts);
+                  yield* ex(value, opts);
                 } else if ("typeOf_peer" in value) {
-                  payloadHtml += exprName(value, opts);
+                  yield* ex(value, opts);
                 } else if ("declRef" in value) {
-                  payloadHtml += exprName(value, opts);
+                  yield* ex(value, opts);
                 } else if ("call" in value) {
-                  payloadHtml += exprName(value, opts);
+                  yield* ex(value, opts);
                 } else if ("refPath" in value) {
-                  payloadHtml += exprName(value, opts);
+                  yield* ex(value, opts);
                 } else if ("type" in value) {
-                  payloadHtml += exprName(value, opts);
+                  yield* ex(value, opts);
                   //payloadHtml += '<span class="tok-kw">' + name + "</span>";
                 } else if ("binOpIndex" in value) {
-                  payloadHtml += exprName(value, opts);
+                  yield* ex(value, opts);
                 } else if ("comptimeExpr" in value) {
                   let comptimeExpr =
                     zigAnalysis.comptimeExprs[value.comptimeExpr].code;
-                  if (opts.wantHtml) {
-                    payloadHtml +=
-                      '<span class="tok-kw">' + comptimeExpr + "</span>";
-                  } else {
-                    payloadHtml += comptimeExpr;
-                  }
-                } else if (opts.wantHtml) {
-                  payloadHtml += '<span class="tok-kw">anytype</span>';
+                  yield* Tokenizer(comptimeExpr);
                 } else {
-                  payloadHtml += "anytype";
+                  yield { src: "anytype", tag: Tag.keyword_anytype };
                 }
               }
             }
 
-            if (opts.wantHtml) {
-              payloadHtml += "<span class='argBreaker'>,<br></span>";
-            }
-            payloadHtml += ") ";
+            yield Tok.r_paren;
+            yield Tok.space;
 
             if (fnObj.has_align) {
               let align = zigAnalysis.exprs[fnObj.align];
-              payloadHtml += "align(" + exprName(align, opts) + ") ";
+              yield { src: "align", tag: Tag.keyword_align };
+              yield Tok.l_paren;
+              yield* ex(align, opts);
+              yield Tok.r_paren;
+              yield Tok.space;
             }
             if (fnObj.has_cc) {
               let cc = zigAnalysis.exprs[fnObj.cc];
               if (cc) {
                 if (cc.enumLiteral !== "Inline") {
-                  payloadHtml += "callconv(" + exprName(cc, opts) + ") ";
+                  yield { src: "collconv", tag: Tag.keyword_callconv };
+                  yield Tok.l_paren;
+                  yield* ex(cc, opts);
+                  yield Tok.r_paren;
+                  yield Tok.space;
                 }
               }
             }
 
             if (fnObj.is_inferred_error) {
-              payloadHtml += "!";
+              yield { src: "!", tag: Tag.bang };
             }
             if (fnObj.ret != null) {
-              payloadHtml += exprName(fnObj.ret, {
+              yield* ex(fnObj.ret, {
                 ...opts,
                 addParensIfFnSignature: true,
               });
-            } else if (opts.wantHtml) {
-              payloadHtml += '<span class="tok-kw">anytype</span>';
             } else {
-              payloadHtml += "anytype";
+              yield { src: "anytype", tag: Tag.keyword_anytype };
             }
 
             if (opts.addParensIfFnSignature && fnObj.src == 0) {
-              payloadHtml += ")";
+              yield Tok.r_paren;
             }
-            return payloadHtml;
+            return;
           }
-          // if (wantHtml) {
-          //     return escapeHtml(typeObj.name);
-          // } else {
-          //     return typeObj.name;
-          // }
         }
       }
+      case "typeOf": {
+        const typeRefArg = zigAnalysis.exprs[expr.typeOf];
+        yield { src: "@TypeOf", tag: Tag.builtin };
+        yield Tok.l_paren;
+        yield* ex(typeRefArg, opts);
+        yield Tok.r_paren;
+        return;
+      }
     }
+
+
   }
 
+
+
   function shouldSkipParamName(typeRef, paramName) {
     let resolvedTypeRef = resolveValue({ expr: typeRef });
     if ("type" in resolvedTypeRef) {
@@ -2504,13 +2223,13 @@ Happy writing!
       typeObj ===
       getType(zigAnalysis.modules[zigAnalysis.rootMod].main)
     ) {
-      name = "std";
+      name = renderSingleToken(Tok.identifier("std"));
     } else {
-      name = exprName({ type: typeObj }, { wantHtml: false, wantLink: false });
+      name = renderTokens(ex({ type: typeObj }));
     }
     if (name != null && name != "") {
-      domHdrName.innerText =
-        name + " (" + zigAnalysis.typeKinds[typeObj.kind] + ")";
+      domHdrName.innerHTML = "<pre class='inline'>" + name + "</pre> ("
+        + zigAnalysis.typeKinds[typeObj.kind] + ")";
       domHdrName.classList.remove("hidden");
     }
     if (typeObj.kind == typeKinds.ErrorSet) {
@@ -2528,7 +2247,7 @@ Happy writing!
         //let srcObj = zigAnalysis.astNodes[errObj.src];
         errorList.push(errObj);
       }
-      errorList.sort(function (a, b) {
+      errorList.sort(function(a, b) {
         return operatorCompare(a.name.toLowerCase(), b.name.toLowerCase());
       });
 
@@ -2624,55 +2343,80 @@ Happy writing!
     if (resolvedValue.expr.fieldRef) {
       const declRef = decl.value.expr.refPath[0].declRef;
       const type = getDecl(declRef);
-      domFnProtoCode.innerHTML =
-        '<span class="tok-kw">const</span> ' +
-        escapeHtml(decl.name) +
-        ": " +
-        type.name +
-        " = " +
-        exprName(decl.value.expr, { wantHtml: true, wantLink: true }) +
-        ";";
+
+      domFnProtoCode.innerHTML = renderTokens(
+        (function*() {
+          yield Tok.const;
+          yield Tok.space;
+          yield Tok.identifier(decl.name);
+          yield Tok.colon;
+          yield Tok.space;
+          yield Tok.identifier(type.name);
+          yield Tok.space;
+          yield Tok.eql;
+          yield Tok.space;
+          yield* ex(decl.value.expr, {});
+          yield Tok.semi;
+        })());
     } else if (
       resolvedValue.expr.string !== undefined ||
       resolvedValue.expr.call !== undefined ||
       resolvedValue.expr.comptimeExpr !== undefined
     ) {
-      let typeRef = null;
-      if (resolvedValue.typeRef !== undefined) {
-        typeRef = resolvedValue.typeRef;
-      }
-      domFnProtoCode.innerHTML =
-        '<span class="tok-kw">const</span> ' +
-        escapeHtml(decl.name) +
-        ": " +
-        exprName(typeRef !== null ? typeRef : resolvedValue.expr, { wantHtml: true, wantLink: true }) +
-        " = " +
-        exprName(decl.value.expr, { wantHtml: true, wantLink: true }) +
-        ";";
+      domFnProtoCode.innerHTML = renderTokens(
+        (function*() {
+          yield Tok.const;
+          yield Tok.space;
+          yield Tok.identifier(decl.name);
+          if (decl.value.typeRef) {
+            yield Tok.colon;
+            yield Tok.space;
+            yield* ex(decl.value.typeRef, {});
+          }
+          yield Tok.space;
+          yield Tok.eql;
+          yield Tok.space;
+          yield* ex(decl.value.expr, {});
+          yield Tok.semi;
+        })());
     } else if (resolvedValue.expr.compileError) {
-      domFnProtoCode.innerHTML =
-        '<span class="tok-kw">const</span> ' +
-        escapeHtml(decl.name) +
-        " = " +
-        exprName(decl.value.expr, { wantHtml: true, wantLink: true }) +
-        ";";
+      domFnProtoCode.innerHTML = renderTokens(
+        (function*() {
+          yield Tok.const;
+          yield Tok.space;
+          yield Tok.identifier(decl.name);
+          yield Tok.space;
+          yield Tok.eql;
+          yield Tok.space;
+          yield* ex(decl.value.expr, {});
+          yield Tok.semi;
+        })());
     } else {
-      domFnProtoCode.innerHTML =
-        '<span class="tok-kw">const</span> ' +
-        escapeHtml(decl.name) +
-        ": " +
-        exprName(resolvedValue.typeRef, { wantHtml: true, wantLink: true }) +
-        " = " +
-        exprName(decl.value.expr, { wantHtml: true, wantLink: true }) +
-        ";";
+      const parent = getType(decl.parent_container);
+      domFnProtoCode.innerHTML = renderTokens(
+        (function*() {
+          yield Tok.const;
+          yield Tok.space;
+          yield Tok.identifier(decl.name);
+          if (decl.value.typeRef !== null) {
+            yield Tok.colon;
+            yield Tok.space;
+            yield* ex(decl.value.typeRef, {});
+          }
+          yield Tok.space;
+          yield Tok.eql;
+          yield Tok.space;
+          yield* ex(decl.value.expr, {});
+          yield Tok.semi;
+        })());
     }
 
     let docs = getAstNode(decl.src).docs;
     if (docs != null) {
       // TODO: it shouldn't just be decl.parent_container, but rather 
       //       the type that the decl holds (if the value is a type)
-      domTldDocs.innerHTML = markdown(docs, getType(decl.parent_container));
-      
+      domTldDocs.innerHTML = markdown(docs, decl);
+
       domTldDocs.classList.remove("hidden");
     }
 
@@ -2685,43 +2429,68 @@ Happy writing!
     if (resolvedVar.expr.fieldRef) {
       const declRef = decl.value.expr.refPath[0].declRef;
       const type = getDecl(declRef);
-      domFnProtoCode.innerHTML =
-        '<span class="tok-kw">var</span> ' +
-        escapeHtml(decl.name) +
-        ": " +
-        type.name +
-        " = " +
-        exprName(decl.value.expr, { wantHtml: true, wantLink: true }) +
-        ";";
+      domFnProtoCode.innerHTML = renderTokens(
+        (function*() {
+          yield Tok.var;
+          yield Tok.space;
+          yield Tok.identifier(decl.name);
+          yield Tok.colon;
+          yield Tok.space;
+          yield Tok.identifier(type.name);
+          yield Tok.space;
+          yield Tok.eql;
+          yield Tok.space;
+          yield* ex(decl.value.expr, {});
+          yield Tok.semi;
+        })());
     } else if (
       resolvedVar.expr.string !== undefined ||
       resolvedVar.expr.call !== undefined ||
       resolvedVar.expr.comptimeExpr !== undefined
     ) {
-      domFnProtoCode.innerHTML =
-        '<span class="tok-kw">var</span> ' +
-        escapeHtml(decl.name) +
-        ": " +
-        exprName(resolvedVar.typeRef !== null ? resolvedVar.typeRef : resolvedVar.expr, { wantHtml: true, wantLink: true }) +
-        " = " +
-        exprName(decl.value.expr, { wantHtml: true, wantLink: true }) +
-        ";";
+      domFnProtoCode.innerHTML = renderTokens(
+        (function*() {
+          yield Tok.var;
+          yield Tok.space;
+          yield Tok.identifier(decl.name);
+          if (decl.value.typeRef) {
+            yield Tok.colon;
+            yield Tok.space;
+            yield* ex(decl.value.typeRef, {});
+          }
+          yield Tok.space;
+          yield Tok.eql;
+          yield Tok.space;
+          yield* ex(decl.value.expr, {});
+          yield Tok.semi;
+        })());
     } else if (resolvedVar.expr.compileError) {
-      domFnProtoCode.innerHTML =
-        '<span class="tok-kw">var</span> ' +
-        escapeHtml(decl.name) +
-        " = " +
-        exprName(decl.value.expr, { wantHtml: true, wantLink: true }) +
-        ";";
+      domFnProtoCode.innerHTML = renderTokens(
+        (function*() {
+          yield Tok.var;
+          yield Tok.space;
+          yield Tok.identifier(decl.name);
+          yield Tok.space;
+          yield Tok.eql;
+          yield Tok.space;
+          yield* ex(decl.value.expr, {});
+          yield Tok.semi;
+        })());
     } else {
-      domFnProtoCode.innerHTML =
-        '<span class="tok-kw">var</span> ' +
-        escapeHtml(decl.name) +
-        ": " +
-        exprName(resolvedVar.typeRef, { wantHtml: true, wantLink: true }) +
-        " = " +
-        exprName(decl.value.expr, { wantHtml: true, wantLink: true }) +
-        ";";
+      domFnProtoCode.innerHTML = renderTokens(
+        (function*() {
+          yield Tok.var;
+          yield Tok.space;
+          yield Tok.identifier(decl.name);
+          yield Tok.colon;
+          yield Tok.space;
+          yield* ex(resolvedVar.typeRef, {});
+          yield Tok.space;
+          yield Tok.eql;
+          yield Tok.space;
+          yield* ex(decl.value.expr, {});
+          yield Tok.semi;
+        })());
     }
 
     let docs = getAstNode(decl.src).docs;
@@ -2955,7 +2724,7 @@ Happy writing!
       resizeDomList(
         domListFns,
         fnsList.length,
-        "<div><dt><div class=\"fnSignature\"></div><div></div></dt><dd></dd></div>"
+        '<div><dt><pre class="inline fnSignature"></pre><div></div></dt><dd></dd></div>'
       );
 
       for (let i = 0; i < fnsList.length; i += 1) {
@@ -2968,12 +2737,10 @@ Happy writing!
 
         let declType = resolveValue(decl.value);
         console.assert("type" in declType.expr);
-        tdFnSignature.innerHTML = exprName(declType.expr, {
-          wantHtml: true,
-          wantLink: true,
+        tdFnSignature.innerHTML = renderTokens(ex(declType.expr, {
           fnDecl: decl,
           linkFnNameDecl: navLinkDecl(decl.name),
-        });
+        }));
         tdFnSrc.innerHTML = "<a style=\"float: right;\" target=\"_blank\" href=\"" +
           sourceFileLink(decl) + "\">[src]</a>";
 
@@ -3018,14 +2785,22 @@ Happy writing!
         if (container.kind === typeKinds.Enum) {
           let value = container.values[i];
           if (value !== null) {
-            html += " = " + exprName(value, { wantHtml: true, wantLink: true });
+            html += renderTokens((function*() {
+              yield Tok.space;
+              yield Tok.eql;
+              yield Tok.space;
+              yield* ex(value, {});
+            })());
           }
         } else {
           let fieldTypeExpr = container.field_types[i];
           if (container.kind !== typeKinds.Struct || !container.is_tuple) {
-            html += ": ";
+            html += renderTokens((function*() {
+              yield Tok.colon;
+              yield Tok.space;
+            })());
           }
-          html += exprName(fieldTypeExpr, { wantHtml: true, wantLink: true });
+          html += renderTokens(ex(fieldTypeExpr, {}));
           let tsn = typeShorthandName(fieldTypeExpr);
           if (tsn) {
             html += "<span> (" + tsn + ")</span>";
@@ -3033,7 +2808,12 @@ Happy writing!
           if (container.kind === typeKinds.Struct && !container.is_tuple) {
             let defaultInitExpr = container.field_defaults[i];
             if (defaultInitExpr !== null) {
-              html += " = " + exprName(defaultInitExpr, { wantHtml: true, wantLink: true });
+              html += renderTokens((function*() {
+                yield Tok.space;
+                yield Tok.eql;
+                yield Tok.space;
+                yield* ex(defaultInitExpr, {});
+              })());
             }
           }
         }
@@ -3052,7 +2832,7 @@ Happy writing!
       resizeDomList(
         domListGlobalVars,
         varsList.length,
-        '<tr><td><a href="#"></a></td><td></td><td></td></tr>'
+        '<tr><td><a href="#"></a></td><td><pre class="inline"></pre></td><td></td></tr>'
       );
       for (let i = 0; i < varsList.length; i += 1) {
         let decl = varsList[i];
@@ -3061,15 +2841,13 @@ Happy writing!
         let tdName = trDom.children[0];
         let tdNameA = tdName.children[0];
         let tdType = trDom.children[1];
+        let preType = tdType.children[0];
         let tdDesc = trDom.children[2];
 
         tdNameA.setAttribute("href", navLinkDecl(decl.name));
         tdNameA.textContent = decl.name;
 
-        tdType.innerHTML = exprName(walkResultTypeRef(decl.value), {
-          wantHtml: true,
-          wantLink: true,
-        });
+        preType.innerHTML = renderTokens(ex(walkResultTypeRef(decl.value), {}));
 
         let docs = getAstNode(decl.src).docs;
         if (docs != null) {
@@ -3085,7 +2863,7 @@ Happy writing!
       resizeDomList(
         domListValues,
         valsList.length,
-        '<tr><td><a href="#"></a></td><td></td><td></td></tr>'
+        '<tr><td><a href="#"></a></td><td><pre class="inline"></pre></td><td></td></tr>'
       );
       for (let i = 0; i < valsList.length; i += 1) {
         let decl = valsList[i];
@@ -3094,15 +2872,13 @@ Happy writing!
         let tdName = trDom.children[0];
         let tdNameA = tdName.children[0];
         let tdType = trDom.children[1];
+        let preType = tdType.children[0];
         let tdDesc = trDom.children[2];
 
         tdNameA.setAttribute("href", navLinkDecl(decl.name));
         tdNameA.textContent = decl.name;
 
-        tdType.innerHTML = exprName(walkResultTypeRef(decl.value), {
-          wantHtml: true,
-          wantLink: true,
-        });
+        preType.innerHTML = renderTokens(ex(walkResultTypeRef(decl.value), {}));
 
         let docs = getAstNode(decl.src).docs;
         if (docs != null) {
@@ -3118,24 +2894,21 @@ Happy writing!
       resizeDomList(
         domListTests,
         testsList.length,
-        '<tr><td><a href="#"></a></td><td></td><td></td></tr>'
+        '<tr><td><pre class="inline"></pre></td><td><pre class="inline"></pre></td><td></td></tr>'
       );
       for (let i = 0; i < testsList.length; i += 1) {
         let decl = testsList[i];
         let trDom = domListTests.children[i];
 
         let tdName = trDom.children[0];
-        let tdNameA = tdName.children[0];
+        let tdNamePre = tdName.children[0];
         let tdType = trDom.children[1];
+        let tdTypePre = tdType.children[0];
         let tdDesc = trDom.children[2];
 
-        tdNameA.setAttribute("href", navLinkDecl(decl.name));
-        tdNameA.textContent = decl.name;
+        tdNamePre.innerHTML = renderSingleToken(Tok.identifier(decl.name));
 
-        tdType.innerHTML = exprName(walkResultTypeRef(decl.value), {
-          wantHtml: true,
-          wantLink: true,
-        });
+        tdTypePre.innerHTML = ex(walkResultTypeRef(decl.value), {});
 
         let docs = getAstNode(decl.src).docs;
         if (docs != null) {
@@ -3260,7 +3033,7 @@ Happy writing!
 
         return;
       case NAV_MODES.GUIDES:
-        
+
         const sections = zigAnalysis.guide_sections;
         if (sections.length != 0 && sections[0].guides.length != 0 && nonSearchPart == "") {
           location.hash = NAV_MODES.GUIDES + sections[0].guides[0].name;
@@ -3428,10 +3201,10 @@ Happy writing!
             if (list[declIndex] != null) continue;
 
             let decl = getDecl(declIndex);
-              
+
             if (decl.is_uns) {
               let unsDeclList = [decl];
-              while(unsDeclList.length != 0) {
+              while (unsDeclList.length != 0) {
                 let unsDecl = unsDeclList.pop();
                 let unsDeclVal = resolveValue(unsDecl.value);
                 if (!("type" in unsDeclVal.expr)) continue;
@@ -3441,7 +3214,7 @@ Happy writing!
                 for (let unsDeclI = 0; unsDeclI < unsPubDeclLen; unsDeclI += 1) {
                   let childDeclIndex = unsType.pubDecls[unsDeclI];
                   let childDecl = getDecl(childDeclIndex);
-                  
+
                   if (childDecl.is_uns) {
                     unsDeclList.push(childDecl);
                   } else {
@@ -3460,54 +3233,54 @@ Happy writing!
     return list;
   }
 
-function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
-  let declVal = resolveValue(decl.value);
-  let declNames = item.declNames.concat([decl.name]);
-  let declIndexes = item.declIndexes.concat([declIndex]);
+  function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
+    let declVal = resolveValue(decl.value);
+    let declNames = item.declNames.concat([decl.name]);
+    let declIndexes = item.declIndexes.concat([declIndex]);
 
-  if (list[declIndex] != null) return;
-  list[declIndex] = {
-    modNames: modNames,
-    declNames: declNames,
-    declIndexes: declIndexes,
-  };
+    if (list[declIndex] != null) return;
+    list[declIndex] = {
+      modNames: modNames,
+      declNames: declNames,
+      declIndexes: declIndexes,
+    };
 
-  // add to search index
-  {
-    declSearchIndex.add(decl.name, {declIndex});
-  }
-  
-  
-  if ("type" in declVal.expr) {
-    let value = getType(declVal.expr.type);
-    if (declCanRepresentTypeKind(value.kind)) {
-      canonTypeDecls[declVal.type] = declIndex;
+    // add to search index
+    {
+      declSearchIndex.add(decl.name, { declIndex });
     }
 
-    if (isContainerType(value)) {
-      stack.push({
-        declNames: declNames,
-        declIndexes: declIndexes,
-        type: value,
-      });
-    }
 
-    // Generic function
-    if (typeIsGenericFn(declVal.expr.type)) {
-      let ret = resolveGenericRet(value);
-      if (ret != null && "type" in ret.expr) {
-        let generic_type = getType(ret.expr.type);
-        if (isContainerType(generic_type)) {
-          stack.push({
-            declNames: declNames,
-            declIndexes: declIndexes,
-            type: generic_type,
-          });
+    if ("type" in declVal.expr) {
+      let value = getType(declVal.expr.type);
+      if (declCanRepresentTypeKind(value.kind)) {
+        canonTypeDecls[declVal.type] = declIndex;
+      }
+
+      if (isContainerType(value)) {
+        stack.push({
+          declNames: declNames,
+          declIndexes: declIndexes,
+          type: value,
+        });
+      }
+
+      // Generic function
+      if (typeIsGenericFn(declVal.expr.type)) {
+        let ret = resolveGenericRet(value);
+        if (ret != null && "type" in ret.expr) {
+          let generic_type = getType(ret.expr.type);
+          if (isContainerType(generic_type)) {
+            stack.push({
+              declNames: declNames,
+              declIndexes: declIndexes,
+              type: generic_type,
+            });
+          }
         }
       }
     }
   }
-}
 
   function getCanonDeclPath(index) {
     if (canonDeclPaths == null) {
@@ -3524,7 +3297,7 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
   }
 
   function escapeHtml(text) {
-    return text.replace(/[&"<>]/g, function (m) {
+    return text.replace(/[&"<>]/g, function(m) {
       return escapeHtmlReplacements[m];
     });
   }
@@ -3553,13 +3326,13 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
   }
 
   function parseGuides() {
-    for (let j = 0; j < zigAnalysis.guide_sections.length; j+=1){
+    for (let j = 0; j < zigAnalysis.guide_sections.length; j += 1) {
       const section = zigAnalysis.guide_sections[j];
-      for (let i = 0; i < section.guides.length; i+=1){
-        let reader = new commonmark.Parser({smart: true});
+      for (let i = 0; i < section.guides.length; i += 1) {
+        let reader = new commonmark.Parser({ smart: true });
         const guide = section.guides[i];
-        const ast = reader.parse(guide.body);        
-        
+        const ast = reader.parse(guide.body);
+
         // Find the first text thing to use as a sidebar title
         guide.title = "[empty guide]";
         {
@@ -3571,7 +3344,7 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
               guide.title = node.literal;
               break;
             }
-          }        
+          }
         }
         // Index this guide
         {
@@ -3580,24 +3353,24 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
           while ((event = walker.next())) {
             node = event.node;
             if (event.entering == true && node.type === 'text') {
-                indexTextForGuide(j, i, node);          
+              indexTextForGuide(j, i, node);
             }
-          }        
+          }
         }
-      }  
+      }
     }
   }
 
-  function indexTextForGuide(section_idx, guide_idx, node){
+  function indexTextForGuide(section_idx, guide_idx, node) {
     const terms = node.literal.split(" ");
-    for (let i = 0; i < terms.length; i += 1){
+    for (let i = 0; i < terms.length; i += 1) {
       const t = terms[i];
       if (!guidesSearchIndex[t]) guidesSearchIndex[t] = new Set();
-      node.guide = {section_idx, guide_idx};
+      node.guide = { section_idx, guide_idx };
       guidesSearchIndex[t].add(node);
-    }  
+    }
   }
-  
+
 
   function markdown(input, contextType) {
     const parsed = new commonmark.Parser({ smart: true }).parse(input);
@@ -3620,70 +3393,71 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
 
     return new commonmark.HtmlRenderer({ safe: true }).render(parsed);
 
-    function detectDeclPath(text, context) {
-      let result = "";
-      let separator = ":";
-      const components = text.split(".");
-      let curDeclOrType = undefined;
-      
-      let curContext = context;
-      let limit = 10000;
-      while (curContext) {
-        limit -= 1;
-        
-        if (limit == 0) {
-          throw "too many iterations";
-        }
-        
-        curDeclOrType = findSubDecl(curContext, components[0]);
-        
-        if (!curDeclOrType) {
-          if (curContext.parent_container == null) break;
-          curContext = getType(curContext.parent_container);
-          continue;
-        }
+  }
 
-        if (curContext == context) {
-          separator = '.';
-          result = location.hash + separator + components[0];
-        } else {
-          // We had to go up, which means we need a new path!
-          const canonPath = getCanonDeclPath(curDeclOrType.find_subdecl_idx);
-          if (!canonPath) return;
-          
-          let lastModName = canonPath.modNames[canonPath.modNames.length - 1];
-          let fullPath = lastModName + ":" + canonPath.declNames.join(".");
-        
-          separator = '.';
-          result = "#A;" + fullPath;
-        }
+  function detectDeclPath(text, context) {
+    let result = "";
+    let separator = ":";
+    const components = text.split(".");
+    let curDeclOrType = undefined;
 
-        break;
-      } 
+    let curContext = context;
+    let limit = 10000;
+    while (curContext) {
+      limit -= 1;
+
+      if (limit == 0) {
+        throw "too many iterations";
+      }
+
+      curDeclOrType = findSubDecl(curContext, components[0]);
 
       if (!curDeclOrType) {
-        for (let i = 0; i < zigAnalysis.modules.length; i += 1){
-          const p = zigAnalysis.modules[i];
-          if (p.name == components[0]) {
-            curDeclOrType = getType(p.main);
-            result += "#A;" + components[0];
-            break;
-          }
-        }
+        if (curContext.parent_container == null) break;
+        curContext = getType(curContext.parent_container);
+        continue;
       }
 
-      if (!curDeclOrType) return null;
-      
-      for (let i = 1; i < components.length; i += 1) {
-        curDeclOrType = findSubDecl(curDeclOrType, components[i]);
-        if (!curDeclOrType) return null;
-        result += separator + components[i];
+      if (curContext == context) {
+        separator = '.';
+        result = location.hash + separator + components[0];
+      } else {
+        // We had to go up, which means we need a new path!
+        const canonPath = getCanonDeclPath(curDeclOrType.find_subdecl_idx);
+        if (!canonPath) return;
+
+        let lastModName = canonPath.modNames[canonPath.modNames.length - 1];
+        let fullPath = lastModName + ":" + canonPath.declNames.join(".");
+
         separator = '.';
+        result = "#A;" + fullPath;
       }
 
-      return result;
-      
+      break;
+    }
+
+    if (!curDeclOrType) {
+      for (let i = 0; i < zigAnalysis.modules.length; i += 1) {
+        const p = zigAnalysis.modules[i];
+        if (p.name == components[0]) {
+          curDeclOrType = getType(p.main);
+          result += "#A;" + components[0];
+          break;
+        }
+      }
+    }
+
+    if (!curDeclOrType) return null;
+
+    for (let i = 1; i < components.length; i += 1) {
+      curDeclOrType = findSubDecl(curDeclOrType, components[i]);
+      if (!curDeclOrType) return null;
+      result += separator + components[i];
+      separator = '.';
     }
+
+    return result;
+
   }
 
   function activateSelectedResult() {
@@ -3723,7 +3497,7 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
       startSearch();
     }
   }
-  
+
 
   function onSearchKeyDown(ev) {
     switch (getKeyString(ev)) {
@@ -3824,7 +3598,7 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
         break;
       case "/":
         if (!getPrefSlashSearch()) break;
-        // fallthrough
+      // fallthrough
       case "s":
         if (!isModalVisible(domHelpModal) && !isModalVisible(domPrefsModal)) {
           if (ev.target == domSearch) break;
@@ -3846,9 +3620,9 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
 
         // toggle the help modal
         if (isModalVisible(domHelpModal)) {
-            hideModal(domHelpModal);
+          hideModal(domHelpModal);
         } else {
-            showModal(domHelpModal);
+          showModal(domHelpModal);
         }
         ev.preventDefault();
         ev.stopPropagation();
@@ -3927,21 +3701,21 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
   function renderSearchGuides() {
     const searchTrimmed = false;
     let ignoreCase = curNavSearch.toLowerCase() === curNavSearch;
-    
+
     let terms = getSearchTerms();
     let matchedItems = new Set();
 
     for (let i = 0; i < terms.length; i += 1) {
       const nodes = guidesSearchIndex[terms[i]];
       if (nodes) {
-        for (const n of nodes) {      
+        for (const n of nodes) {
           matchedItems.add(n);
         }
       }
     }
 
-    
-    
+
+
     if (matchedItems.size !== 0) {
       // Build up the list of search results
       let matchedItemsHTML = "";
@@ -3966,14 +3740,14 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
     }
   }
 
-  function renderSearchAPI(){
+  function renderSearchAPI() {
     if (canonDeclPaths == null) {
       canonDeclPaths = computeCanonDeclPaths();
     }
     let declSet = new Set();
     let otherDeclSet = new Set(); // for low quality results
     let declScores = {};
-    
+
     let ignoreCase = curNavSearch.toLowerCase() === curNavSearch;
     let term_list = getSearchTerms();
     for (let i = 0; i < term_list.length; i += 1) {
@@ -4004,7 +3778,7 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
               if (declSet.has(p)) {
                 found = true;
                 break;
-              }   
+              }
             }
             if (!found) {
               otherDeclSet.add(d);
@@ -4012,9 +3786,9 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
               termSet.add(d);
             }
           }
-          
+
           if (declScores[d] == undefined) declScores[d] = 0;
-        
+
           // scores (lower is better)
           let decl_name = decl.name;
           if (ignoreCase) decl_name = decl_name.toLowerCase();
@@ -4022,21 +3796,21 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
           // shallow path are preferable
           const path_depth = canonPath.declNames.length * 50;
           // matching the start of a decl name is good
-          const match_from_start = decl_name.startsWith(term) ? -term.length * (2 -ignoreCase) : (decl_name.length - term.length) + 1; 
+          const match_from_start = decl_name.startsWith(term) ? -term.length * (2 - ignoreCase) : (decl_name.length - term.length) + 1;
           // being a perfect match is good
           const is_full_match = (decl_name === term) ? -decl_name.length * (1 - ignoreCase) : Math.abs(decl_name.length - term.length);
           // matching the end of a decl name is good
           const matches_the_end = decl_name.endsWith(term) ? -term.length * (1 - ignoreCase) : (decl_name.length - term.length) + 1;
           // explicitly penalizing scream case decls
-          const decl_is_scream_case = decl.name.toUpperCase() != decl.name ? 0 : decl.name.length;  
-        
-          const score =  path_depth 
-            + match_from_start 
-            + is_full_match 
-            + matches_the_end 
+          const decl_is_scream_case = decl.name.toUpperCase() != decl.name ? 0 : decl.name.length;
+
+          const score = path_depth
+            + match_from_start
+            + is_full_match
+            + matches_the_end
             + decl_is_scream_case;
 
-          declScores[d] += score;        
+          declScores[d] += score;
         }
       }
       if (i != 0) {
@@ -4047,19 +3821,19 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
             if (termSet.has(p) || otherDeclSet.has(p)) {
               found = true;
               break;
-            }   
+            }
           }
           if (found) {
             declScores[d] = declScores[d] / term_list.length;
           }
-          
+
           termOtherSet.add(d);
         }
         declSet = termSet;
         for (let d of termOtherSet) {
           otherDeclSet.add(d);
         }
-        
+
       }
     }
 
@@ -4068,21 +3842,21 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
       low_quality: [],
     };
     for (let idx of declSet) {
-      matchedItems.high_quality.push({points: declScores[idx], declIndex: idx})
+      matchedItems.high_quality.push({ points: declScores[idx], declIndex: idx })
     }
     for (let idx of otherDeclSet) {
-      matchedItems.low_quality.push({points: declScores[idx], declIndex: idx})
+      matchedItems.low_quality.push({ points: declScores[idx], declIndex: idx })
     }
-    
-    matchedItems.high_quality.sort(function (a, b) {
+
+    matchedItems.high_quality.sort(function(a, b) {
       let cmp = operatorCompare(a.points, b.points);
       return cmp;
     });
-    matchedItems.low_quality.sort(function (a, b) {
+    matchedItems.low_quality.sort(function(a, b) {
       let cmp = operatorCompare(a.points, b.points);
       return cmp;
     });
-    
+
     // Build up the list of search results
     let matchedItemsHTML = "";
 
@@ -4099,7 +3873,7 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
 
         let lastModName = canonPath.modNames[canonPath.modNames.length - 1];
         let text = lastModName + "." + canonPath.declNames.join(".");
-      
+
 
         const href = navLink(canonPath.modNames, canonPath.declNames);
 
@@ -4113,7 +3887,7 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
 
     domSectSearchResults.classList.remove("hidden");
   }
-   
+
   function renderSearchAPIOld() {
     let matchedItems = [];
     let ignoreCase = curNavSearch.toLowerCase() === curNavSearch;
@@ -4179,7 +3953,7 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
     }
 
     if (matchedItems.length !== 0) {
-      matchedItems.sort(function (a, b) {
+      matchedItems.sort(function(a, b) {
         let cmp = operatorCompare(b.points, a.points);
         if (cmp != 0) return cmp;
         return operatorCompare(a.decl.name, b.decl.name);
@@ -4485,12 +4259,12 @@ function toggleExpand(event) {
 function RadixTree() {
   this.root = null;
 
-  RadixTree.prototype.search = function (query) {
+  RadixTree.prototype.search = function(query) {
     return this.root.search(query);
-    
+
   }
 
-  RadixTree.prototype.add = function (declName, value) {
+  RadixTree.prototype.add = function(declName, value) {
     if (this.root == null) {
       this.root = new Node(declName.toLowerCase(), null, [value]);
     } else {
@@ -4499,7 +4273,7 @@ function RadixTree() {
 
     const not_scream_case = declName.toUpperCase() != declName;
     let found_separator = false;
-    for (let i = 1; i < declName.length; i +=1) {
+    for (let i = 1; i < declName.length; i += 1) {
       if (declName[i] == '_' || declName[i] == '.') {
         found_separator = true;
         continue;
@@ -4507,42 +4281,42 @@ function RadixTree() {
 
 
       if (found_separator || (declName[i].toLowerCase() !== declName[i])) {
-        if (declName.length > i+1 
-          && declName[i+1].toLowerCase() != declName[i+1]) continue;
+        if (declName.length > i + 1
+          && declName[i + 1].toLowerCase() != declName[i + 1]) continue;
         let suffix = declName.slice(i);
         this.root.add(suffix.toLowerCase(), value);
         found_separator = false;
       }
     }
   }
-  
+
   function Node(labels, next, values) {
-    this.labels = labels; 
+    this.labels = labels;
     this.next = next;
     this.values = values;
   }
 
-  Node.prototype.isCompressed = function () {
+  Node.prototype.isCompressed = function() {
     return !Array.isArray(this.next);
   }
 
-  Node.prototype.search = function (word) {
+  Node.prototype.search = function(word) {
     let full_matches = [];
     let partial_matches = [];
     let subtree_root = null;
-    
+
     let cn = this;
     char_loop: for (let i = 0; i < word.length;) {
       if (cn.isCompressed()) {
         for (let j = 0; j < cn.labels.length; j += 1) {
-          let current_idx = i+j;
+          let current_idx = i + j;
 
           if (current_idx == word.length) {
             partial_matches = cn.values;
             subtree_root = cn.next;
-            break char_loop; 
+            break char_loop;
           }
-          
+
           if (word[current_idx] != cn.labels[j]) return null;
         }
 
@@ -4553,33 +4327,33 @@ function RadixTree() {
           subtree_root = cn.next;
           break char_loop;
         }
-        
-        
+
+
         i = new_idx;
         cn = cn.next;
         continue;
       } else {
         for (let j = 0; j < cn.labels.length; j += 1) {
           if (word[i] == cn.labels[j]) {
-            if (i == word.length - 1) { 
+            if (i == word.length - 1) {
               full_matches = cn.values[j];
               subtree_root = cn.next[j];
               break char_loop;
             }
-              
+
             let next = cn.next[j];
             if (next == null) return null;
             cn = next;
             i += 1;
-            continue char_loop; 
-          } 
+            continue char_loop;
+          }
         }
-        
+
         // didn't find a match
         return null;
       }
     }
-    
+
     // Match was found, let's collect all other 
     // partial matches from the subtree
     let stack = [subtree_root];
@@ -4594,22 +4368,22 @@ function RadixTree() {
         for (let v of node.values) {
           partial_matches = partial_matches.concat(v);
         }
-        
+
         for (let n of node.next) {
           if (n != null) stack.push(n);
         }
       }
     }
 
-    return {full: full_matches, partial: partial_matches};
+    return { full: full_matches, partial: partial_matches };
   }
 
-  Node.prototype.add = function (word, value) {
+  Node.prototype.add = function(word, value) {
     let cn = this;
     char_loop: for (let i = 0; i < word.length;) {
       if (cn.isCompressed()) {
-        for(let j = 0; j < cn.labels.length; j += 1) {
-          let current_idx = i+j;
+        for (let j = 0; j < cn.labels.length; j += 1) {
+          let current_idx = i + j;
 
           if (current_idx == word.length) {
             if (j < cn.labels.length - 1) {
@@ -4621,13 +4395,13 @@ function RadixTree() {
             cn.values.push(value);
             return;
           }
-          
+
           if (word[current_idx] == cn.labels[j]) continue;
-          
+
           // if we're here, a mismatch was found
           if (j != cn.labels.length - 1) {
             // create a suffix node
-            const label_suffix = cn.labels.slice(j+1);
+            const label_suffix = cn.labels.slice(j + 1);
             let node = new Node(label_suffix, cn.next, [...cn.values]);
             cn.next = node;
             cn.values = [];
@@ -4641,11 +4415,11 @@ function RadixTree() {
             // meaning that the current node should hold its value
             word_values.push(value);
           } else {
-             node = new Node(word.slice(current_idx+1), null, [value]);
+            node = new Node(word.slice(current_idx + 1), null, [value]);
           }
-        
+
           cn.labels = cn.labels[j] + word[current_idx];
-          cn.next = [cn.next, node];          
+          cn.next = [cn.next, node];
           cn.values = [cn.values, word_values];
 
           if (j != 0) {
@@ -4657,7 +4431,7 @@ function RadixTree() {
           }
 
           return;
-        }        
+        }
         // label matched fully with word, are there any more chars?
         const new_idx = i + cn.labels.length;
         if (new_idx == word.length) {
@@ -4673,7 +4447,7 @@ function RadixTree() {
             i = new_idx;
             continue;
           }
-        } 
+        }
       } else { // node is not compressed
         let letter = word[i];
         for (let j = 0; j < cn.labels.length; j += 1) {
@@ -4683,7 +4457,7 @@ function RadixTree() {
               return;
             }
             if (cn.next[j] == null) {
-              let node = new Node(word.slice(i+1), null, [value]);
+              let node = new Node(word.slice(i + 1), null, [value]);
               cn.next[j] = node;
               return;
             } else {
@@ -4700,7 +4474,7 @@ function RadixTree() {
           cn.next.push(null);
           cn.values.push([value]);
         } else {
-          let node = new Node(word.slice(i+1), null, [value]);
+          let node = new Node(word.slice(i + 1), null, [value]);
           cn.next.push(node);
           cn.values.push([]);
         }
lib/docs/ziglexer.js
@@ -1,6 +1,7 @@
 'use strict';
 
 const Tag = {
+    whitespace: "whitespace",
     invalid: "invalid",
     identifier: "identifier",
     string_literal: "string_literal",
@@ -125,6 +126,27 @@ const Tag = {
     keyword_while: "keyword_while"
 }
 
+const Tok = {
+    const: { src: "const", tag: Tag.keyword_const },
+    var: { src: "var", tag: Tag.keyword_var },
+    colon: { src: ":", tag: Tag.colon },
+    eql: { src: "=", tag: Tag.equals },
+    space: { src: " ", tag: Tag.whitespace },
+    tab: { src: "    ", tag: Tag.whitespace },
+    enter: { src: "\n", tag: Tag.whitespace },
+    semi: { src: ";", tag: Tag.semicolon },
+    l_bracket: { src: "[", tag: Tag.l_bracket },
+    r_bracket: { src: "]", tag: Tag.r_bracket },
+    l_brace: { src: "{", tag: Tag.l_brace },
+    r_brace: { src: "}", tag: Tag.r_brace },
+    l_paren: { src: "(", tag: Tag.l_paren },
+    r_paren: { src: ")", tag: Tag.r_paren },
+    period: { src: ".", tag: Tag.period },
+    comma: { src: ",", tag: Tag.comma },
+    identifier: (name) => { return { src: name, tag: Tag.identifier } },
+};
+
+
 const State = {
     start: 0,
     identifier: 1,
@@ -175,6 +197,7 @@ const State = {
     period_2: 46,
     period_asterisk: 47,
     saw_at_sign: 48,
+    whitespace: 49,
 }
 
 const keywords = {
@@ -256,25 +279,36 @@ function dump_tokens(tokens, raw_source) {
     }
 }
 
+function* Tokenizer(raw_source) {
+    let tokenizer = new InnerTokenizer(raw_source);
+    while (true) {
+        let t = tokenizer.next(); 
+        if (t.tag == Tag.eof) 
+            return;
+        
+        t.src = raw_source.slice(t.loc.start, t.loc.end);
+        
+        yield t;
+    }
 
+}
+function InnerTokenizer(raw_source) {
+    this.index = 0;
+    this.flag = false;
 
-function tokenize_zig_source(raw_source) {
-
-    var index = 0;
-    var flag = false;
-
-    let seen_escape_digits = undefined;
-    let remaining_code_units = undefined;
+    this.seen_escape_digits = undefined;
+    this.remaining_code_units = undefined;
 
-    const next = () => {
+    this.next = () => {
         let state = State.start;
 
         var result = {
             tag: -1,
             loc: {
-                start: index,
+                start: this.index,
                 end: undefined,
             },
+            src: undefined,
         };
 
         //having a while (true) loop seems like a bad idea the loop should never
@@ -284,37 +318,39 @@ function tokenize_zig_source(raw_source) {
 
         while (iterations <= MAX_ITERATIONS) {
 
-            if (flag) {
-                return make_token(Tag.eof, index - 2, index - 2);
+            if (this.flag) {
+                return make_token(Tag.eof, this.index - 2, this.index - 2);
             }
             iterations += 1; // avoid death loops
 
-            var c = raw_source[index];
+            var c = raw_source[this.index];
 
             if (c === undefined) {
                 c = ' '; // push the last token
-                flag = true;
+                this.flag = true;
             }
 
             switch (state) {
                 case State.start:
                     switch (c) {
                         case 0: {
-                            if (index != raw_source.length) {
+                            if (this.index != raw_source.length) {
                                 result.tag = Tag.invalid;
-                                result.loc.start = index;
-                                index += 1;
-                                result.loc.end = index;
+                                result.loc.start = this.index;
+                                this.index += 1;
+                                result.loc.end = this.index;
                                 return result;
                             }
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                         case ' ':
                         case '\n':
                         case '\t':
                         case '\r': {
-                            result.loc.start = index + 1;
+                            state = State.whitespace;
+                            result.tag = Tag.whitespace;
+                            result.loc.start = this.index;
                             break;
                         }
                         case '"': {
@@ -401,51 +437,51 @@ function tokenize_zig_source(raw_source) {
                         }
                         case '(': {
                             result.tag = Tag.l_paren;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
 
                             return result;
                             
                         }
                         case ')': {
                             result.tag = Tag.r_paren;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                             
                         }
                         case '[': {
                             result.tag = Tag.l_bracket;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                             
                         }
                         case ']': {
                             result.tag = Tag.r_bracket;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                             
                         }
                         case ';': {
                             result.tag = Tag.semicolon;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                             
                         }
                         case ',': {
                             result.tag = Tag.comma;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                             
                         }
                         case '?': {
                             result.tag = Tag.question_mark;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                             
                         }
                         case ':': {
                             result.tag = Tag.colon;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                             
                         }
@@ -473,19 +509,19 @@ function tokenize_zig_source(raw_source) {
                         }
                         case '{': {
                             result.tag = Tag.l_brace;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                             
                         }
                         case '}': {
                             result.tag = Tag.r_brace;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                             
                         }
                         case '~': {
                             result.tag = Tag.tilde;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                             
                         }
@@ -517,8 +553,8 @@ function tokenize_zig_source(raw_source) {
                             }
                         default: {
                             result.tag = Tag.invalid;
-                            result.loc.end = index;
-                            index += 1;
+                            result.loc.end = this.index;
+                            this.index += 1;
                             return result;
                         }
                     }
@@ -588,7 +624,7 @@ function tokenize_zig_source(raw_source) {
                         }
                         default: {
                             result.tag = Tag.invalid;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -597,11 +633,11 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.ampersand_equal;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                         }
                         default: {
-                            result.tag = Tag.ampersand; result.loc.end = index;
+                            result.tag = Tag.ampersand; result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -609,12 +645,12 @@ function tokenize_zig_source(raw_source) {
                 case State.asterisk: switch (c) {
                     case '=': {
                         result.tag = Tag.asterisk_equal;
-                        index += 1; result.loc.end = index;
+                        this.index += 1; result.loc.end = this.index;
                         return result;
                     }
                     case '*': {
                         result.tag = Tag.asterisk_asterisk;
-                        index += 1; result.loc.end = index;
+                        this.index += 1; result.loc.end = this.index;
                         return result;
                     }
                     case '%': {
@@ -625,7 +661,7 @@ function tokenize_zig_source(raw_source) {
                     }
                     default: {
                         result.tag = Tag.asterisk;
-                        result.loc.end = index;
+                        result.loc.end = this.index;
                         return result;
                     }
                 }
@@ -634,12 +670,12 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.asterisk_percent_equal;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.asterisk_percent;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result; 
                         }
                     }
@@ -648,11 +684,11 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.asterisk_pipe_equal;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                         }
                         default: {
-                            result.tag = Tag.asterisk_pipe; result.loc.end = index;
+                            result.tag = Tag.asterisk_pipe; result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -661,11 +697,11 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.percent_equal;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                         }
                         default: {
-                            result.tag = Tag.percent; result.loc.end = index;
+                            result.tag = Tag.percent; result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -674,12 +710,12 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.plus_equal;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                         }
                         case '+': {
                             result.tag = Tag.plus_plus;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                         }
                         case '%': {
@@ -689,7 +725,7 @@ function tokenize_zig_source(raw_source) {
                             state = State.plus_pipe; break;
                         }
                         default: {
-                            result.tag = Tag.plus; result.loc.end = index;
+                            result.tag = Tag.plus; result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -698,11 +734,11 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.plus_percent_equal;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                         }
                         default: {
-                            result.tag = Tag.plus_percent; result.loc.end = index;
+                            result.tag = Tag.plus_percent; result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -711,11 +747,11 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.plus_pipe_equal;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                         }
                         default: {
-                            result.tag = Tag.plus_pipe; result.loc.end = index;
+                            result.tag = Tag.plus_pipe; result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -724,11 +760,11 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.caret_equal;
-                            index += 1; result.loc.end = index;
+                            this.index += 1; result.loc.end = this.index;
                             return result;
                         }
                         default: {
-                            result.tag = Tag.caret; result.loc.end = index;
+                            result.tag = Tag.caret; result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -799,12 +835,12 @@ function tokenize_zig_source(raw_source) {
                         case '8':
                         case '9': break;
                         default: {
-                            // if (Token.getKeyword(buffer[result.loc.start..index])) | tag | {
-                            const z = raw_source.substring(result.loc.start, index);
+                            // if (Token.getKeyword(buffer[result.loc.start..this.index])) | tag | {
+                            const z = raw_source.substring(result.loc.start, this.index);
                             if (z in keywords) {
                                 result.tag = keywords[z];
                             }
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result; 
                         }
 
@@ -875,7 +911,7 @@ function tokenize_zig_source(raw_source) {
                     case '7':
                     case '8':
                     case '9': break;
-                    default: result.loc.end = index;
+                    default: result.loc.end = this.index;
                         return result;
                 }
                     break;
@@ -887,7 +923,7 @@ function tokenize_zig_source(raw_source) {
                         }
                         default: {
                             result.tag = Tag.invalid;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -898,25 +934,25 @@ function tokenize_zig_source(raw_source) {
                             state = State.string_literal_backslash; break;
                         }
                         case '"': {
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
 
                             return result; 
                         }
                         case 0: {
                             //TODO: PORT
-                            // if (index == buffer.len) {
+                            // if (this.index == buffer.len) {
                             //     result.tag = .invalid;
                             //     break;
                             // } else {
                             //     checkLiteralCharacter();
                             // }
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result; 
                         }
                         case '\n': {
                             result.tag = Tag.invalid;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result; 
                         }
                         //TODO: PORT
@@ -928,7 +964,7 @@ function tokenize_zig_source(raw_source) {
                         case 0:
                         case '\n': {
                             result.tag = Tag.invalid;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result; 
                         }
                         default: {
@@ -939,7 +975,7 @@ function tokenize_zig_source(raw_source) {
                 case State.char_literal: switch (c) {
                     case 0: {
                         result.tag = Tag.invalid;
-                        result.loc.end = index;
+                        result.loc.end = this.index;
                         return result; 
                     }
                     case '\\': {
@@ -952,15 +988,15 @@ function tokenize_zig_source(raw_source) {
                     //     break;
                     // },
                     // 0xc0...0xdf => { // 110xxxxx
-                    //     remaining_code_units = 1;
+                    //     this.remaining_code_units = 1;
                     //     state = .char_literal_unicode;
                     // },
                     // 0xe0...0xef => { // 1110xxxx
-                    //     remaining_code_units = 2;
+                    //     this.remaining_code_units = 2;
                     //     state = .char_literal_unicode;
                     // },
                     // 0xf0...0xf7 => { // 11110xxx
-                    //     remaining_code_units = 3;
+                    //     this.remaining_code_units = 3;
                     //     state = .char_literal_unicode;
                     // },
 
@@ -1070,7 +1106,7 @@ function tokenize_zig_source(raw_source) {
                     // case 0xdd:
                     // case 0xde:
                     // case 0xdf:
-                    //     remaining_code_units = 1;
+                    //     this.remaining_code_units = 1;
                     //     state = .char_literal_unicode;
                     // case 0xe0:
                     // case 0xe1:
@@ -1088,7 +1124,7 @@ function tokenize_zig_source(raw_source) {
                     // case 0xed:
                     // case 0xee:
                     // case 0xef:
-                    //     remaining_code_units = 2;
+                    //     this.remaining_code_units = 2;
                     //     state = .char_literal_unicode;
                     // case 0xf0:
                     // case 0xf1:
@@ -1098,12 +1134,12 @@ function tokenize_zig_source(raw_source) {
                     // case 0xf5:
                     // case 0xf6:
                     // case 0xf7:
-                    //     remaining_code_units = 3;
+                    //     this.remaining_code_units = 3;
                     //     state = .char_literal_unicode;
 
                     case '\n': {
                         result.tag = Tag.invalid;
-                        result.loc.end = index;
+                        result.loc.end = this.index;
                         return result; 
                     }
                     default: {
@@ -1116,12 +1152,12 @@ function tokenize_zig_source(raw_source) {
                         case 0:
                         case '\n': {
                             result.tag = Tag.invalid;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result; 
                         }
                         case 'x': {
                             state = State.char_literal_hex_escape;
-                            seen_escape_digits = 0; break;
+                            this.seen_escape_digits = 0; break;
                         }
                         case 'u': {
                             state = State.char_literal_unicode_escape_saw_u; break;
@@ -1155,14 +1191,14 @@ function tokenize_zig_source(raw_source) {
                         case 'D':
                         case 'E':
                         case 'F': {
-                            seen_escape_digits += 1;
-                            if (seen_escape_digits == 2) {
+                            this.seen_escape_digits += 1;
+                            if (this.seen_escape_digits == 2) {
                                 state = State.char_literal_end;
                             } break;
                         }
                         default: {
                             result.tag = Tag.invalid;
-                            esult.loc.end = index;
+                            esult.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1171,7 +1207,7 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case 0: {
                             result.tag = Tag.invalid;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                         case '{': {
@@ -1187,7 +1223,7 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case 0: {
                             result.tag = Tag.invalid;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                         case '0':
@@ -1297,13 +1333,13 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '\'': {
                             result.tag = Tag.char_literal;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result; 
                         }
                         default: {
                             result.tag = Tag.invalid;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result; 
                         }
                     }
@@ -1311,14 +1347,14 @@ function tokenize_zig_source(raw_source) {
                 case State.char_literal_unicode:
                     switch (c) {
                         // 0x80...0xbf => {
-                        //         remaining_code_units -= 1;
-                        //         if (remaining_code_units == 0) {
+                        //         this.remaining_code_units -= 1;
+                        //         if (this.remaining_code_units == 0) {
                         //             state = .char_literal_end;
                         //         }
                         //     },
                         default: {
                             result.tag = Tag.invalid;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result; 
                         }
                     }
@@ -1326,12 +1362,12 @@ function tokenize_zig_source(raw_source) {
                 case State.multiline_string_literal_line:
                     switch (c) {
                         case 0:
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         case '\n': {
 
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         case '\t': break;
@@ -1344,13 +1380,13 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.bang_equal;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.bang;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1359,19 +1395,19 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.pipe_equal;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         case '|': {
                             result.tag = Tag.pipe_pipe;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.pipe;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1379,19 +1415,19 @@ function tokenize_zig_source(raw_source) {
                 case State.equal: switch (c) {
                     case '=': {
                         result.tag = Tag.equal_equal;
-                        index += 1;
-                        result.loc.end = index;
+                        this.index += 1;
+                        result.loc.end = this.index;
                         return result;
                     }
                     case '>': {
                         result.tag = Tag.equal_angle_bracket_right;
-                        index += 1;
-                        result.loc.end = index;
+                        this.index += 1;
+                        result.loc.end = this.index;
                         return result;
                     }
                     default: {
                         result.tag = Tag.equal;
-                        result.loc.end = index;
+                        result.loc.end = this.index;
                         return result;
                     }
                 }
@@ -1399,14 +1435,14 @@ function tokenize_zig_source(raw_source) {
                 case State.minus: switch (c) {
                     case '>': {
                         result.tag = Tag.arrow;
-                        index += 1;
-                        result.loc.end = index;
+                        this.index += 1;
+                        result.loc.end = this.index;
                         return result;
                     }
                     case '=': {
                         result.tag = Tag.minus_equal;
-                        index += 1;
-                        result.loc.end = index;
+                        this.index += 1;
+                        result.loc.end = this.index;
                         return result;
                     }
                     case '%': {
@@ -1417,7 +1453,7 @@ function tokenize_zig_source(raw_source) {
                     }
                     default: {
                         result.tag = Tag.minus;
-                        result.loc.end = index;
+                        result.loc.end = this.index;
                         return result;
                     }
                 }
@@ -1426,13 +1462,13 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.minus_percent_equal;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.minus_percent;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1441,13 +1477,13 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.minus_pipe_equal;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.minus_pipe;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1459,13 +1495,13 @@ function tokenize_zig_source(raw_source) {
                         }
                         case '=': {
                             result.tag = Tag.angle_bracket_left_equal;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.angle_bracket_left;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1474,8 +1510,8 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.angle_bracket_angle_bracket_left_equal;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         case '|': {
@@ -1483,7 +1519,7 @@ function tokenize_zig_source(raw_source) {
                         }
                         default: {
                             result.tag = Tag.angle_bracket_angle_bracket_left;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1492,13 +1528,13 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.angle_bracket_angle_bracket_left_pipe_equal;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.angle_bracket_angle_bracket_left_pipe;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1510,13 +1546,13 @@ function tokenize_zig_source(raw_source) {
                         }
                         case '=': {
                             result.tag = Tag.angle_bracket_right_equal;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.angle_bracket_right;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1525,13 +1561,13 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '=': {
                             result.tag = Tag.angle_bracket_angle_bracket_right_equal;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.angle_bracket_angle_bracket_right;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1546,7 +1582,7 @@ function tokenize_zig_source(raw_source) {
                         }
                         default: {
                             result.tag = Tag.period;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1555,13 +1591,13 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '.': {
                             result.tag = Tag.ellipsis3;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.ellipsis2;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1570,12 +1606,12 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case '*': {
                             result.tag = Tag.invalid_periodasterisks;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.period_asterisk;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     }
@@ -1588,24 +1624,24 @@ function tokenize_zig_source(raw_source) {
                         }
                         case '=': {
                             result.tag = Tag.slash_equal;
-                            index += 1;
-                            result.loc.end = index;
+                            this.index += 1;
+                            result.loc.end = this.index;
                             return result;
                         }
                         default: {
                             result.tag = Tag.slash;
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                     } break;
                 case State.line_comment_start:
                     switch (c) {
                         case 0: {
-                            if (index != raw_source.length) {
+                            if (this.index != raw_source.length) {
                                 result.tag = Tag.invalid;
-                                index += 1;
+                                this.index += 1;
                             }
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                         case '/': {
@@ -1617,7 +1653,7 @@ function tokenize_zig_source(raw_source) {
                         }
                         case '\n': {
                             state = State.start;
-                            result.loc.start = index + 1; break;
+                            result.loc.start = this.index + 1; break;
                         }
                         case '\t':
                             state = State.line_comment; break;
@@ -1637,7 +1673,7 @@ function tokenize_zig_source(raw_source) {
                         case '\n':
                             {
                                 result.tag = Tag.doc_comment;
-                                result.loc.end = index;
+                                result.loc.end = this.index;
                                 return result;
                             }
                         case '\t': {
@@ -1655,16 +1691,16 @@ function tokenize_zig_source(raw_source) {
                 case State.line_comment:
                     switch (c) {
                         case 0: {
-                            if (index != raw_source.length) {
+                            if (this.index != raw_source.length) {
                                 result.tag = Tag.invalid;
-                                index += 1;
+                                this.index += 1;
                             }
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         }
                         case '\n': {
                             state = State.start;
-                            result.loc.start = index + 1;
+                            result.loc.start = this.index + 1;
                             break;
                         }
                         case '\t': break;
@@ -1675,7 +1711,7 @@ function tokenize_zig_source(raw_source) {
                     switch (c) {
                         case 0://
                         case '\n':
-                            result.loc.end = index;
+                            result.loc.end = this.index;
                             return result;
                         case '\t': break;
                         //TODOL PORT
@@ -1754,7 +1790,7 @@ function tokenize_zig_source(raw_source) {
                         case 'P':
                             state = State.int_exponent;
                             break;
-                        default: result.loc.end = index;
+                        default: result.loc.end = this.index;
                             return result;
                     } break;
                 case State.int_exponent:
@@ -1766,7 +1802,7 @@ function tokenize_zig_source(raw_source) {
                                 state = State.float; break;
                             }
                         default: {
-                            index -= 1;
+                            this.index -= 1;
                             state = State.int; break;
                         }
                     } break;
@@ -1838,8 +1874,8 @@ function tokenize_zig_source(raw_source) {
                     case 'P':
                         state = State.float_exponent; break;
                     default: {
-                        index -= 1;
-                        result.loc.end = index;
+                        this.index -= 1;
+                        result.loc.end = this.index;
                         return result;
                     }
                 } break;
@@ -1911,7 +1947,7 @@ function tokenize_zig_source(raw_source) {
                         case 'p':
                         case 'P':
                             state = State.float_exponent; break;
-                        default: result.loc.end = index;
+                        default: result.loc.end = this.index;
                             return result;
                     } break;
                 case State.float_exponent:
@@ -1920,13 +1956,27 @@ function tokenize_zig_source(raw_source) {
                         case '+':
                             state = State.float; break;
                         default: {
-                            index -= 1;
+                            this.index -= 1;
                             state = State.float; break;
                         }
                     }
                     break;
+
+                case State.whitespace:
+                    switch(c) {
+                        case ' ':
+                        case '\n':
+                        case '\t':
+                        case '\r': {
+                            break;
+                        }
+                        default: {
+                            result.loc.end = this.index;
+                            return result;
+                        }
+                   }
             }
-            index += 1;
+            this.index += 1;
         }
 
         //TODO: PORT
@@ -1938,52 +1988,37 @@ function tokenize_zig_source(raw_source) {
         //     result.loc.start = sindex;
         // }
 
-        result.loc.end = index;
+        result.loc.end = this.index;
         return result;
 
     }
-
-    let toks = []
-
-    for (let i = 0; i < raw_source.length * 2; i++) {
-        const tok = next();
-        toks.push(tok);
-
-        if (tok.tag == Tag.eof) {
-            break;
-        }
-    }
-
-    return toks;
 }
 
 
-function generate_html_for_src(src) {
-    var toks = tokenize_zig_source(src);
-    var html = [];
-
-    let offset = 0;
-    for (let z = 0; z < toks.length; z++) {
-        const t = toks[z];
+const builtin_types = [
+    "f16",          "f32",     "f64",        "f80",          "f128",
+    "c_longdouble", "c_short", "c_ushort",   "c_int",        "c_uint",
+    "c_long",       "c_ulong", "c_longlong", "c_ulonglong",  "c_char",
+    "anyopaque",    "void",    "bool",       "isize",        "usize",
+    "noreturn",     "type",    "anyerror",   "comptime_int", "comptime_float",
+];
 
-        if(t.tag == Tag.eof)
-            break;
-
-        const spanStart = `<span class="zig_${t.tag}">`
-        const spanEnd = `</span>`
-
-        src = `${src.slice(0, t.loc.start + offset)}` + spanStart + `${src.slice(t.loc.start + offset)}`;
-        offset += spanStart.length;
+function isSimpleType(typeName) {
+    return builtin_types.includes(typeName) || isIntType(typeName);
+}
 
-        src = `${src.slice(0, t.loc.end + offset)}` + spanEnd + `${src.slice(t.loc.end + offset)}`;
-        offset += spanEnd.length;
+function isIntType(typeName) {
+    if (typeName[0] != 'u' && typeName[0] != 'i') return false;
+    let i = 1;
+    if (i == typeName.length) return false;
+    for (; i < typeName.length; i += 1) {
+        if (typeName[i] < '0' || typeName[i] > '9') return false;
     }
+    return true;
+}
 
-
-    html.push(src);
-
-    return html.join("");
-
+function isSpecialIndentifier(identifier) {
+    return ["null", "true", "false", ,"undefined"].includes(identifier);
 }
 
 //const fs = require('fs');
src/Autodoc.zig
@@ -814,6 +814,7 @@ const DocData = struct {
         this: usize, // index in `types`
         declRef: *Scope.DeclStatus,
         declIndex: usize, // index into `decls`, alternative repr for `declRef`
+        declName: []const u8, // unresolved decl name
         builtinField: enum { len, ptr },
         fieldRef: FieldRef,
         refPath: []Expr,
@@ -2282,7 +2283,7 @@ fn walkInstruction(
 
             var path: std.ArrayListUnmanaged(DocData.Expr) = .{};
             try path.append(self.arena, .{
-                .string = file.zir.nullTerminatedString(extra.data.field_name_start),
+                .declName = file.zir.nullTerminatedString(extra.data.field_name_start),
             });
 
             // Put inside path the starting index of each decl name that
@@ -2304,7 +2305,7 @@ fn walkInstruction(
                     );
 
                     try path.append(self.arena, .{
-                        .string = file.zir.nullTerminatedString(lhs_extra.data.field_name_start),
+                        .declName = file.zir.nullTerminatedString(lhs_extra.data.field_name_start),
                     });
                 }
             };
@@ -3665,7 +3666,7 @@ fn tryResolveRefPath(
     var i: usize = 0;
     outer: while (i < path.len - 1) : (i += 1) {
         const parent = path[i];
-        const child_string = path[i + 1].string; // we expect to find a string union case
+        const child_string = path[i + 1].declName; // we expect to find an unsolved decl
 
         var resolved_parent = parent;
         var j: usize = 0;
@@ -3738,14 +3739,14 @@ fn tryResolveRefPath(
                         return;
                     }
 
-                    // If the last element is a string or a CTE, then we give up,
+                    // If the last element is a declName or a CTE, then we give up,
                     // otherwise we resovle the parent to it and loop again.
                     // NOTE: we assume that if we find a string, it's because of
                     // a CTE component somewhere in the path. We know that the path
                     // is not pending futher evaluation because we just checked!
                     const last = rp[rp.len - 1];
                     switch (last) {
-                        .comptimeExpr, .string => break :outer,
+                        .comptimeExpr, .declName => break :outer,
                         else => {
                             resolved_parent = last;
                             continue;
@@ -3772,7 +3773,7 @@ fn tryResolveRefPath(
                     "TODO: handle `{s}`in tryResolveRefPath\nInfo: {}",
                     .{ @tagName(resolved_parent), resolved_parent },
                 );
-                path[i + 1] = (try self.cteTodo("<match failure>")).expr;
+                // path[i + 1] = (try self.cteTodo("<match failure>")).expr;
                 continue :outer;
             },
             .comptimeExpr, .call, .typeOf => {