Repository: Khan/KaTeX
Pull request #1552 (2018-08-06 05:51:17)

Flatten a bunch of non-pervasive ParseNode types (part 2)
Towards #1492.

codecov[bot]: # [Codecov](https://codecov.io/gh/Khan/KaTeX/pull/1552?src=pr&el=h1) Report
> Merging [#1552](https://codecov.io/gh/Khan/KaTeX/pull/1552?src=pr&el=desc) into [master](https://codecov.io/gh/Khan/KaTeX/commit/1506dc1d8899c9bf1f4ff859a259793ca9cf7cb3?src=pr&el=desc) will **increase** coverage by `0.01%`.
> The diff coverage is `82.35%`.


[![Impacted file tree graph](https://codecov.io/gh/Khan/KaTeX/pull/1552/graphs/tree.svg?width=650&token=vfDnj2P77o&height=150&src=pr)](https://codecov.io/gh/Khan/KaTeX/pull/1552?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1552      +/-   ##
==========================================
+ Coverage    84.1%   84.12%   +0.01%     
==========================================
  Files          79       79              
  Lines        4555     4554       -1     
  Branches      803      803              
==========================================
  Hits         3831     3831              
+ Misses        629      628       -1     
  Partials       95       95
```


| [Impacted Files](https://codecov.io/gh/Khan/KaTeX/pull/1552?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/parseNode.js](https://codecov.io/gh/Khan/KaTeX/pull/1552/diff?src=pr&el=tree#diff-c3JjL3BhcnNlTm9kZS5qcw==) | `84.21% <ø> (ø)` | :arrow_up: |
| [src/functions/environment.js](https://codecov.io/gh/Khan/KaTeX/pull/1552/diff?src=pr&el=tree#diff-c3JjL2Z1bmN0aW9ucy9lbnZpcm9ubWVudC5qcw==) | `100% <ø> (ø)` | :arrow_up: |
| [src/functions/underline.js](https://codecov.io/gh/Khan/KaTeX/pull/1552/diff?src=pr&el=tree#diff-c3JjL2Z1bmN0aW9ucy91bmRlcmxpbmUuanM=) | `9.09% <0%> (+0.75%)` | :arrow_up: |
| [src/functions/overline.js](https://codecov.io/gh/Khan/KaTeX/pull/1552/diff?src=pr&el=tree#diff-c3JjL2Z1bmN0aW9ucy9vdmVybGluZS5qcw==) | `25% <0%> (ø)` | :arrow_up: |
| [src/utils.js](https://codecov.io/gh/Khan/KaTeX/pull/1552/diff?src=pr&el=tree#diff-c3JjL3V0aWxzLmpz) | `66.66% <0%> (ø)` | :arrow_up: |
| [src/buildHTML.js](https://codecov.io/gh/Khan/KaTeX/pull/1552/diff?src=pr&el=tree#diff-c3JjL2J1aWxkSFRNTC5qcw==) | `97.69% <0%> (ø)` | :arrow_up: |
| [src/functions/htmlmathml.js](https://codecov.io/gh/Khan/KaTeX/pull/1552/diff?src=pr&el=tree#diff-c3JjL2Z1bmN0aW9ucy9odG1sbWF0aG1sLmpz) | `100% <100%> (ø)` | :arrow_up: |
| [src/functions/smash.js](https://codecov.io/gh/Khan/KaTeX/pull/1552/diff?src=pr&el=tree#diff-c3JjL2Z1bmN0aW9ucy9zbWFzaC5qcw==) | `87.5% <100%> (ø)` | :arrow_up: |
| [src/functions/color.js](https://codecov.io/gh/Khan/KaTeX/pull/1552/diff?src=pr&el=tree#diff-c3JjL2Z1bmN0aW9ucy9jb2xvci5qcw==) | `100% <100%> (ø)` | :arrow_up: |
| [src/functions/raisebox.js](https://codecov.io/gh/Khan/KaTeX/pull/1552/diff?src=pr&el=tree#diff-c3JjL2Z1bmN0aW9ucy9yYWlzZWJveC5qcw==) | `100% <100%> (ø)` | :arrow_up: |
| ... and [9 more](https://codecov.io/gh/Khan/KaTeX/pull/1552/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Khan/KaTeX/pull/1552?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Khan/KaTeX/pull/1552?src=pr&el=footer). Last update [1506dc1...903f19e](https://codecov.io/gh/Khan/KaTeX/pull/1552?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/Parser.js b/src/Parser.js
index 6d09b29a..f35df758 100644
--- a/src/Parser.js
+++ b/src/Parser.js
@@ -223,10 +223,10 @@ export default class Parser {
                 if (overIndex !== -1) {
                     throw new ParseError(
                         "only one infix operator per group",
-                        node.value.token);
+                        node.token);
                 }
                 overIndex = i;
-                funcName = node.value.replaceWith;
+                funcName = node.replaceWith;
             }
         }
 
@@ -327,12 +327,9 @@ export default class Parser {
 
         const colorNode = {
             type: "color",
-            value: {
-                type: "color",
-                color: this.settings.errorColor,
-                value: [textNode],
-            },
             mode: this.mode,
+            color: this.settings.errorColor,
+            body: [textNode],
         };
 
         this.consume();
@@ -460,10 +457,10 @@ export default class Parser {
             const begin =
                 assertNodeType(this.parseGivenFunction(start), "environment");
 
-            const envName = begin.value.name;
+            const envName = begin.name;
             if (!environments.hasOwnProperty(envName)) {
                 throw new ParseError(
-                    "No such environment: " + envName, begin.value.nameGroup);
+                    "No such environment: " + envName, begin.nameGroup);
             }
             // Build the environment object. Arguments and other information will
             // be made available to the begin and end methods using properties.
@@ -483,10 +480,9 @@ export default class Parser {
                 throw new ParseError("failed to parse function after \\end");
             }
             end = assertNodeType(end, "environment");
-            if (end.value.name !== envName) {
+            if (end.name !== envName) {
                 throw new ParseError(
-                    "Mismatch: \\begin{" + envName + "} matched " +
-                    "by \\end{" + end.value.name + "}",
+                    `Mismatch: \\begin{${envName}} matched by \\end{${end.name}}`,
                     endNameToken);
             }
             return result;
diff --git a/src/buildHTML.js b/src/buildHTML.js
index c9ac1504..00c4b259 100644
--- a/src/buildHTML.js
+++ b/src/buildHTML.js
@@ -169,7 +169,7 @@ export const buildExpression = function(
                         if (!node) {
                             // No match.
                         } else if (node.type === "sizing") {
-                            glueOptions = options.havingSize(node.value.size);
+                            glueOptions = options.havingSize(node.size);
                         } else if (node.type === "styling") {
                             glueOptions = options.havingStyle(
                                 styleMap[node.value.style]);
diff --git a/src/functions/color.js b/src/functions/color.js
index df40a505..197569fc 100644
--- a/src/functions/color.js
+++ b/src/functions/color.js
@@ -9,8 +9,8 @@ import * as mml from "../buildMathML";
 
 const htmlBuilder = (group, options) => {
     const elements = html.buildExpression(
-        group.value.value,
-        options.withColor(group.value.color),
+        group.body,
+        options.withColor(group.color),
         false
     );
 
@@ -22,11 +22,11 @@ const htmlBuilder = (group, options) => {
 };
 
 const mathmlBuilder = (group, options) => {
-    const inner = mml.buildExpression(group.value.value, options);
+    const inner = mml.buildExpression(group.body, options);
 
     const node = new mathMLTree.MathNode("mstyle", inner);
 
-    node.setAttribute("mathcolor", group.value.color);
+    node.setAttribute("mathcolor", group.color);
 
     return node;
 };
@@ -46,11 +46,8 @@ defineFunction({
         return {
             type: "color",
             mode: parser.mode,
-            value: {
-                type: "color",
-                color: color.value,
-                value: ordargument(body),
-            },
+            color: color.value,
+            body: ordargument(body),
         };
     },
     htmlBuilder,
@@ -85,11 +82,8 @@ defineFunction({
         return {
             type: "color",
             mode: parser.mode,
-            value: {
-                type: "color",
-                color: "katex-" + funcName.slice(1),
-                value: ordargument(body),
-            },
+            color: "katex-" + funcName.slice(1),
+            body: ordargument(body),
         };
     },
     htmlBuilder,
@@ -114,11 +108,8 @@ defineFunction({
         return {
             type: "color",
             mode: parser.mode,
-            value: {
-                type: "color",
-                color: color.value,
-                value: body,
-            },
+            color: color.value,
+            body,
         };
     },
     htmlBuilder,
diff --git a/src/functions/enclose.js b/src/functions/enclose.js
index 7f3284f0..04c2e482 100644
--- a/src/functions/enclose.js
+++ b/src/functions/enclose.js
@@ -12,9 +12,9 @@ import * as mml from "../buildMathML";
 
 const htmlBuilder = (group, options) => {
     // \cancel, \bcancel, \xcancel, \sout, \fbox, \colorbox, \fcolorbox
-    const inner = html.buildGroup(group.value.body, options);
+    const inner = html.buildGroup(group.body, options);
 
-    const label = group.value.label.substr(1);
+    const label = group.label.substr(1);
     const scale = options.sizeMultiplier;
     let img;
     let imgShift = 0;
@@ -24,7 +24,7 @@ const htmlBuilder = (group, options) => {
     // We don't know the width of a group, so as a proxy, we test if
     // the subject is a single character. This captures most of the
     // subjects that should get the "tall" treatment.
-    const isSingleChar = utils.isCharacterBox(group.value.body);
+    const isSingleChar = utils.isCharacterBox(group.body);
 
     if (label === "sout") {
         img = buildCommon.makeSpan(["stretchy", "sout"]);
@@ -54,16 +54,16 @@ const htmlBuilder = (group, options) => {
         img = stretchy.encloseSpan(inner, label, vertPad, options);
         imgShift = inner.depth + vertPad;
 
-        if (group.value.backgroundColor) {
-            img.style.backgroundColor = group.value.backgroundColor.value;
-            if (group.value.borderColor) {
-                img.style.borderColor = group.value.borderColor.value;
+        if (group.backgroundColor) {
+            img.style.backgroundColor = group.backgroundColor.value;
+            if (group.borderColor) {
+                img.style.borderColor = group.borderColor.value;
             }
         }
     }
 
     let vlist;
-    if (group.value.backgroundColor) {
+    if (group.backgroundColor) {
         vlist = buildCommon.makeVList({
             positionType: "individualShift",
             children: [
@@ -109,8 +109,8 @@ const htmlBuilder = (group, options) => {
 
 const mathmlBuilder = (group, options) => {
     const node = new mathMLTree.MathNode(
-        "menclose", [mml.buildGroup(group.value.body, options)]);
-    switch (group.value.label) {
+        "menclose", [mml.buildGroup(group.body, options)]);
+    switch (group.label) {
         case "\\cancel":
             node.setAttribute("notation", "updiagonalstrike");
             break;
@@ -131,8 +131,8 @@ const mathmlBuilder = (group, options) => {
             node.setAttribute("notation", "updiagonalstrike downdiagonalstrike");
             break;
     }
-    if (group.value.backgroundColor) {
-        node.setAttribute("mathbackground", group.value.backgroundColor.value);
+    if (group.backgroundColor) {
+        node.setAttribute("mathbackground", group.backgroundColor.value);
     }
     return node;
 };
@@ -152,12 +152,9 @@ defineFunction({
         return {
             type: "enclose",
             mode: parser.mode,
-            value: {
-                type: "enclose",
-                label: funcName,
-                backgroundColor: color,
-                body: body,
-            },
+            label: funcName,
+            backgroundColor: color,
+            body,
         };
     },
     htmlBuilder,
@@ -180,13 +177,10 @@ defineFunction({
         return {
             type: "enclose",
             mode: parser.mode,
-            value: {
-                type: "enclose",
-                label: funcName,
-                backgroundColor: backgroundColor,
-                borderColor: borderColor,
-                body: body,
-            },
+            label: funcName,
+            backgroundColor: backgroundColor,
+            borderColor: borderColor,
+            body,
         };
     },
     htmlBuilder,
@@ -205,11 +199,8 @@ defineFunction({
         return {
             type: "enclose",
             mode: parser.mode,
-            value: {
-                type: "enclose",
-                label: "\\fbox",
-                body: args[0],
-            },
+            label: "\\fbox",
+            body: args[0],
         };
     },
 });
@@ -225,11 +216,8 @@ defineFunction({
         return {
             type: "enclose",
             mode: parser.mode,
-            value: {
-                type: "enclose",
-                label: funcName,
-                body: body,
-            },
+            label: funcName,
+            body,
         };
     },
     htmlBuilder,
diff --git a/src/functions/environment.js b/src/functions/environment.js
index 2e45dc5b..d8077b26 100644
--- a/src/functions/environment.js
+++ b/src/functions/environment.js
@@ -24,11 +24,8 @@ defineFunction({
         return {
             type: "environment",
             mode: parser.mode,
-            value: {
-                type: "environment",
-                name: name,
-                nameGroup: nameGroup,
-            },
+            name,
+            nameGroup,
         };
     },
 });
diff --git a/src/functions/genfrac.js b/src/functions/genfrac.js
index 5f1cf4b4..38b8efbc 100644
--- a/src/functions/genfrac.js
+++ b/src/functions/genfrac.js
@@ -15,15 +15,15 @@ const htmlBuilder = (group, options) => {
     // Figure out what style this fraction should be in based on the
     // function used
     let style = options.style;
-    if (group.value.size === "display") {
+    if (group.size === "display") {
         style = Style.DISPLAY;
-    } else if (group.value.size === "text" &&
+    } else if (group.size === "text" &&
         style.size === Style.DISPLAY.size) {
         // We're in a \tfrac but incoming style is displaystyle, so:
         style = Style.TEXT;
-    } else if (group.value.size === "script") {
+    } else if (group.size === "script") {
         style = Style.SCRIPT;
-    } else if (group.value.size === "scriptscript") {
+    } else if (group.size === "scriptscript") {
         style = Style.SCRIPTSCRIPT;
     }
 
@@ -32,9 +32,9 @@ const htmlBuilder = (group, options) => {
     let newOptions;
 
     newOptions = options.havingStyle(nstyle);
-    const numerm = html.buildGroup(group.value.numer, newOptions, options);
+    const numerm = html.buildGroup(group.numer, newOptions, options);
 
-    if (group.value.continued) {
+    if (group.continued) {
         // \cfrac inserts a \strut into the numerator.
         // Get \strut dimensions from TeXbook page 353.
         const hStrut = 8.5 / options.fontMetrics().ptPerEm;
@@ -44,14 +44,14 @@ const htmlBuilder = (group, options) => {
     }
 
     newOptions = options.havingStyle(dstyle);
-    const denomm = html.buildGroup(group.value.denom, newOptions, options);
+    const denomm = html.buildGroup(group.denom, newOptions, options);
 
     let rule;
     let ruleWidth;
     let ruleSpacing;
-    if (group.value.hasBarLine) {
-        if (group.value.barSize) {
-            ruleWidth = calculateSize(group.value.barSize, options);
+    if (group.hasBarLine) {
+        if (group.barSize) {
+            ruleWidth = calculateSize(group.barSize, options);
             rule = buildCommon.makeLineSpan("frac-line", options, ruleWidth);
         } else {
             rule = buildCommon.makeLineSpan("frac-line", options);
@@ -150,21 +150,21 @@ const htmlBuilder = (group, options) => {
 
     let leftDelim;
     let rightDelim;
-    if (group.value.leftDelim == null) {
+    if (group.leftDelim == null) {
         leftDelim = html.makeNullDelimiter(options, ["mopen"]);
     } else {
         leftDelim = delimiter.customSizedDelim(
-            group.value.leftDelim, delimSize, true,
+            group.leftDelim, delimSize, true,
             options.havingStyle(style), group.mode, ["mopen"]);
     }
 
-    if (group.value.continued) {
+    if (group.continued) {
         rightDelim = buildCommon.makeSpan([]); // zero width for \cfrac
-    } else if (group.value.rightDelim == null) {
+    } else if (group.rightDelim == null) {
         rightDelim = html.makeNullDelimiter(options, ["mclose"]);
     } else {
         rightDelim = delimiter.customSizedDelim(
-            group.value.rightDelim, delimSize, true,
+            group.rightDelim, delimSize, true,
             options.havingStyle(style), group.mode, ["mclose"]);
     }
 
@@ -178,23 +178,23 @@ const mathmlBuilder = (group, options) => {
     const node = new mathMLTree.MathNode(
         "mfrac",
         [
-            mml.buildGroup(group.value.numer, options),
-            mml.buildGroup(group.value.denom, options),
+            mml.buildGroup(group.numer, options),
+            mml.buildGroup(group.denom, options),
         ]);
 
-    if (!group.value.hasBarLine) {
+    if (!group.hasBarLine) {
         node.setAttribute("linethickness", "0px");
-    } else if (group.value.barSize) {
-        const ruleWidth = calculateSize(group.value.barSize, options);
+    } else if (group.barSize) {
+        const ruleWidth = calculateSize(group.barSize, options);
         node.setAttribute("linethickness", ruleWidth + "em");
     }
 
-    if (group.value.leftDelim != null || group.value.rightDelim != null) {
+    if (group.leftDelim != null || group.rightDelim != null) {
         const withDelims = [];
 
-        if (group.value.leftDelim != null) {
+        if (group.leftDelim != null) {
             const leftOp = new mathMLTree.MathNode(
-                "mo", [new mathMLTree.TextNode(group.value.leftDelim)]);
+                "mo", [new mathMLTree.TextNode(group.leftDelim)]);
 
             leftOp.setAttribute("fence", "true");
 
@@ -203,9 +203,9 @@ const mathmlBuilder = (group, options) => {
 
         withDelims.push(node);
 
-        if (group.value.rightDelim != null) {
+        if (group.rightDelim != null) {
             const rightOp = new mathMLTree.MathNode(
-                "mo", [new mathMLTree.TextNode(group.value.rightDelim)]);
+                "mo", [new mathMLTree.TextNode(group.rightDelim)]);
 
             rightOp.setAttribute("fence", "true");
 
@@ -284,17 +284,14 @@ defineFunction({
         return {
             type: "genfrac",
             mode: parser.mode,
-            value: {
-                type: "genfrac",
-                continued: funcName === "\\cfrac",
-                numer: numer,
-                denom: denom,
-                hasBarLine: hasBarLine,
-                leftDelim: leftDelim,
-                rightDelim: rightDelim,
-                size: size,
-                barSize: null,
-            },
+            continued: funcName === "\\cfrac",
+            numer,
+            denom,
+            hasBarLine,
+            leftDelim,
+            rightDelim,
+            size,
+            barSize: null,
         };
     },
 
@@ -335,11 +332,8 @@ defineFunction({
         return {
             type: "infix",
             mode: parser.mode,
-            value: {
-                type: "infix",
-                replaceWith: replaceWith,
-                token: token,
-            },
+            replaceWith,
+            token,
         };
     },
 });
@@ -413,17 +407,14 @@ defineFunction({
         return {
             type: "genfrac",
             mode: parser.mode,
-            value: {
-                type: "genfrac",
-                numer: numer,
-                denom: denom,
-                continued: false,
-                hasBarLine: hasBarLine,
-                barSize: barSize,
-                leftDelim: leftDelim,
-                rightDelim: rightDelim,
-                size: size,
-            },
+            numer,
+            denom,
+            continued: false,
+            hasBarLine,
+            barSize,
+            leftDelim,
+            rightDelim,
+            size,
         };
     },
 
@@ -445,12 +436,9 @@ defineFunction({
         return {
             type: "infix",
             mode: parser.mode,
-            value: {
-                type: "infix",
-                replaceWith: "\\\\abovefrac",
-                sizeNode: sizeNode,
-                token: token,
-            },
+            replaceWith: "\\\\abovefrac",
+            sizeNode,
+            token,
         };
     },
 });
@@ -465,7 +453,7 @@ defineFunction({
     handler: ({parser, funcName}, args) => {
         const numer = args[0];
         const infixNode = assertNodeType(args[1], "infix");
-        const sizeNode = assertNodeType(infixNode.value.sizeNode, "size");
+        const sizeNode = assertNodeType(infixNode.sizeNode, "size");
         const denom = args[2];
 
         const barSize = sizeNode.value.value;
@@ -473,17 +461,14 @@ defineFunction({
         return {
             type: "genfrac",
             mode: parser.mode,
-            value: {
-                type: "genfrac",
-                numer: numer,
-                denom: denom,
-                continued: false,
-                hasBarLine: hasBarLine,
-                barSize: barSize,
-                leftDelim: null,
-                rightDelim: null,
-                size: "auto",
-            },
+            numer,
+            denom,
+            continued: false,
+            hasBarLine,
+            barSize,
+            leftDelim: null,
+            rightDelim: null,
+            size: "auto",
         };
     },
 
diff --git a/src/functions/htmlmathml.js b/src/functions/htmlmathml.js
index c9540477..50f4c7e3 100644
--- a/src/functions/htmlmathml.js
+++ b/src/functions/htmlmathml.js
@@ -16,22 +16,19 @@ defineFunction({
         return {
             type: "htmlmathml",
             mode: parser.mode,
-            value: {
-                type: "htmlmathml",
-                html:   ordargument(args[0]),
-                mathml: ordargument(args[1]),
-            },
+            html:   ordargument(args[0]),
+            mathml: ordargument(args[1]),
         };
     },
     htmlBuilder: (group, options) => {
         const elements = html.buildExpression(
-            group.value.html,
+            group.html,
             options,
             false
         );
         return new buildCommon.makeFragment(elements);
     },
     mathmlBuilder: (group, options) => {
-        return mml.buildExpressionRow(group.value.mathml, options);
+        return mml.buildExpressionRow(group.mathml, options);
     },
 });
diff --git a/src/functions/kern.js b/src/functions/kern.js
index 0c074336..3741d903 100644
--- a/src/functions/kern.js
+++ b/src/functions/kern.js
@@ -42,17 +42,14 @@ defineFunction({
         return {
             type: "kern",
             mode: parser.mode,
-            value: {
-                type: "kern",
-                dimension: size.value.value,
-            },
+            dimension: size.value.value,
         };
     },
     htmlBuilder: (group, options) => {
-        return buildCommon.makeGlue(group.value.dimension, options);
+        return buildCommon.makeGlue(group.dimension, options);
     },
     mathmlBuilder: (group, options) => {
-        const dimension = calculateSize(group.value.dimension, options);
+        const dimension = calculateSize(group.dimension, options);
         return new mathMLTree.SpaceNode(dimension);
     },
 });
diff --git a/src/functions/lap.js b/src/functions/lap.js
index 3b30eedc..c49a85b8 100644
--- a/src/functions/lap.js
+++ b/src/functions/lap.js
@@ -19,29 +19,26 @@ defineFunction({
         return {
             type: "lap",
             mode: parser.mode,
-            value: {
-                type: "lap",
-                alignment: funcName.slice(5),
-                body: body,
-            },
+            alignment: funcName.slice(5),
+            body,
         };
     },
     htmlBuilder: (group, options) => {
         // mathllap, mathrlap, mathclap
         let inner;
-        if (group.value.alignment === "clap") {
+        if (group.alignment === "clap") {
             // ref: https://www.math.lsu.edu/~aperlis/publications/mathclap/
             inner = buildCommon.makeSpan(
-                [], [html.buildGroup(group.value.body, options)]);
+                [], [html.buildGroup(group.body, options)]);
             // wrap, since CSS will center a .clap > .inner > span
             inner = buildCommon.makeSpan(["inner"], [inner], options);
         } else {
             inner = buildCommon.makeSpan(
-                ["inner"], [html.buildGroup(group.value.body, options)]);
+                ["inner"], [html.buildGroup(group.body, options)]);
         }
         const fix = buildCommon.makeSpan(["fix"], []);
         let node = buildCommon.makeSpan(
-            [group.value.alignment], [inner, fix], options);
+            [group.alignment], [inner, fix], options);
 
         // At this point, we have correctly set horizontal alignment of the
         // two items involved in the lap.
@@ -64,10 +61,10 @@ defineFunction({
     mathmlBuilder: (group, options) => {
         // mathllap, mathrlap, mathclap
         const node = new mathMLTree.MathNode(
-            "mpadded", [mml.buildGroup(group.value.body, options)]);
+            "mpadded", [mml.buildGroup(group.body, options)]);
 
-        if (group.value.alignment !== "rlap")    {
-            const offset = (group.value.alignment === "llap" ? "-1" : "-0.5");
+        if (group.alignment !== "rlap")    {
+            const offset = (group.alignment === "llap" ? "-1" : "-0.5");
             node.setAttribute("lspace", offset + "width");
         }
         node.setAttribute("width", "0px");
diff --git a/src/functions/operatorname.js b/src/functions/operatorname.js
index 2d801787..36e27b73 100644
--- a/src/functions/operatorname.js
+++ b/src/functions/operatorname.js
@@ -20,16 +20,13 @@ defineFunction({
         return {
             type: "operatorname",
             mode: parser.mode,
-            value: {
-                type: "operatorname",
-                value: ordargument(body),
-            },
+            body: ordargument(body),
         };
     },
 
     htmlBuilder: (group, options) => {
-        if (group.value.value.length > 0) {
-            const groupValue = group.value.value.map(child => {
+        if (group.body.length > 0) {
+            const body = group.body.map(child => {
                 // $FlowFixMe: Check if the node has a string `value` property.
                 const childValue = child.value;
                 if (typeof childValue === "string") {
@@ -45,7 +42,7 @@ defineFunction({
 
             // Consolidate function names into symbol characters.
             const expression = html.buildExpression(
-                groupValue, options.withFont("mathrm"), true);
+                body, options.withFont("mathrm"), true);
 
             for (let i = 0; i < expression.length; i++) {
                 const child = expression[i];
@@ -65,7 +62,7 @@ defineFunction({
     mathmlBuilder: (group, options) => {
         // The steps taken here are similar to the html version.
         let expression = mml.buildExpression(
-            group.value.value, options.withFont("mathrm"));
+            group.body, options.withFont("mathrm"));
 
         // Is expression a string or has it something like a fraction?
         let isAllString = true;  // default
diff --git a/src/functions/overline.js b/src/functions/overline.js
index ba04caa8..6a76eac0 100644
--- a/src/functions/overline.js
+++ b/src/functions/overline.js
@@ -17,17 +17,14 @@ defineFunction({
         return {
             type: "overline",
             mode: parser.mode,
-            value: {
-                type: "overline",
-                body: body,
-            },
+            body,
         };
     },
     htmlBuilder(group, options) {
         // Overlines are handled in the TeXbook pg 443, Rule 9.
 
         // Build the inner group in the cramped style.
-        const innerGroup = html.buildGroup(group.value.body,
+        const innerGroup = html.buildGroup(group.body,
             options.havingCrampedStyle());
 
         // Create the line above the body
@@ -53,7 +50,7 @@ defineFunction({
 
         const node = new mathMLTree.MathNode(
             "mover",
-            [mml.buildGroup(group.value.body, options), operator]);
+            [mml.buildGroup(group.body, options), operator]);
         node.setAttribute("accent", "true");
 
         return node;
diff --git a/src/functions/raisebox.js b/src/functions/raisebox.js
index 43d30adf..83d5178f 100644
--- a/src/functions/raisebox.js
+++ b/src/functions/raisebox.js
@@ -23,12 +23,8 @@ defineFunction({
         return {
             type: "raisebox",
             mode: parser.mode,
-            value: {
-                type: "raisebox",
-                dy: amount,
-                body: body,
-                value: ordargument(body),
-            },
+            dy: amount,
+            body,
         };
     },
     htmlBuilder(group, options) {
@@ -37,21 +33,18 @@ defineFunction({
             mode: group.mode,
             value: {
                 type: "text",
-                body: group.value.value,
+                body: ordargument(group.body),
                 font: "mathrm", // simulate \textrm
             },
         };
         const sizedText = {
             type: "sizing",
             mode: group.mode,
-            value: {
-                type: "sizing",
-                value: [text],
-                size: 6,                // simulate \normalsize
-            },
+            body: [text],
+            size: 6,                // simulate \normalsize
         };
         const body = sizing.htmlBuilder(sizedText, options);
-        const dy = calculateSize(group.value.dy.value.value, options);
+        const dy = calculateSize(group.dy.value.value, options);
         return buildCommon.makeVList({
             positionType: "shift",
             positionData: -dy,
@@ -60,9 +53,8 @@ defineFunction({
     },
     mathmlBuilder(group, options) {
         const node = new mathMLTree.MathNode(
-            "mpadded", [mml.buildGroup(group.value.body, options)]);
-        const dy =
-            group.value.dy.value.value.number + group.value.dy.value.value.unit;
+            "mpadded", [mml.buildGroup(group.body, options)]);
+        const dy = group.dy.value.value.number + group.dy.value.value.unit;
         node.setAttribute("voffset", dy);
         return node;
     },
diff --git a/src/functions/rule.js b/src/functions/rule.js
index c6a4c8b5..03bb9ec9 100644
--- a/src/functions/rule.js
+++ b/src/functions/rule.js
@@ -20,12 +20,9 @@ defineFunction({
         return {
             type: "rule",
             mode: parser.mode,
-            value: {
-                type: "rule",
-                shift: shift && assertNodeType(shift, "size").value.value,
-                width: width.value.value,
-                height: height.value.value,
-            },
+            shift: shift && assertNodeType(shift, "size").value.value,
+            width: width.value.value,
+            height: height.value.value,
         };
     },
     htmlBuilder(group, options) {
@@ -34,12 +31,12 @@ defineFunction({
 
         // Calculate the shift, width, and height of the rule, and account for units
         let shift = 0;
-        if (group.value.shift) {
-            shift = calculateSize(group.value.shift, options);
+        if (group.shift) {
+            shift = calculateSize(group.shift, options);
         }
 
-        const width = calculateSize(group.value.width, options);
-        const height = calculateSize(group.value.height, options);
+        const width = calculateSize(group.width, options);
+        const height = calculateSize(group.height, options);
 
         // Style the rule to the right size
         rule.style.borderRightWidth = width + "em";
diff --git a/src/functions/sizing.js b/src/functions/sizing.js
index 45cdceee..8fe3fa51 100644
--- a/src/functions/sizing.js
+++ b/src/functions/sizing.js
@@ -8,9 +8,15 @@ import * as html from "../buildHTML";
 import * as mml from "../buildMathML";
 
 import type Options from "../Options";
+import type {AnyParseNode} from "../parseNode";
 import type {HtmlBuilder} from "../defineFunction";
+import type {documentFragment as HtmlDocumentFragment} from "../domTree";
 
-export function sizingGroup(value: *, options: Options, baseOptions: Options) {
+export function sizingGroup(
+    value: AnyParseNode[],
+    options: Options,
+    baseOptions: Options,
+): HtmlDocumentFragment {
     const inner = html.buildExpression(value, options, false);
     const multiplier = options.sizeMultiplier / baseOptions.sizeMultiplier;
 
@@ -44,8 +50,8 @@ export const htmlBuilder: HtmlBuilder<"sizing"> = (group, options) => {
     // Handle sizing operators like \Huge. Real TeX doesn't actually allow
     // these functions inside of math expressions, so we do some special
     // handling.
-    const newOptions = options.havingSize(group.value.size);
-    return sizingGroup(group.value.value, newOptions, options);
+    const newOptions = options.havingSize(group.size);
+    return sizingGroup(group.body, newOptions, options);
 };
 
 defineFunction({
@@ -62,18 +68,15 @@ defineFunction({
         return {
             type: "sizing",
             mode: parser.mode,
-            value: {
-                type: "sizing",
-                // Figure out what size to use based on the list of functions above
-                size: utils.indexOf(sizeFuncs, funcName) + 1,
-                value: body,
-            },
+            // Figure out what size to use based on the list of functions above
+            size: utils.indexOf(sizeFuncs, funcName) + 1,
+            body,
         };
     },
     htmlBuilder,
     mathmlBuilder: (group, options) => {
-        const newOptions = options.havingSize(group.value.size);
-        const inner = mml.buildExpression(group.value.value, newOptions);
+        const newOptions = options.havingSize(group.size);
+        const inner = mml.buildExpression(group.body, newOptions);
 
         const node = new mathMLTree.MathNode("mstyle", inner);
 
diff --git a/src/functions/smash.js b/src/functions/smash.js
index a062c283..793efdcf 100644
--- a/src/functions/smash.js
+++ b/src/functions/smash.js
@@ -48,23 +48,20 @@ defineFunction({
         return {
             type: "smash",
             mode: parser.mode,
-            value: {
-                type: "smash",
-                body: body,
-                smashHeight: smashHeight,
-                smashDepth: smashDepth,
-            },
+            body,
+            smashHeight,
+            smashDepth,
         };
     },
     htmlBuilder: (group, options) => {
         const node = buildCommon.makeSpan(
-            ["mord"], [html.buildGroup(group.value.body, options)]);
+            ["mord"], [html.buildGroup(group.body, options)]);
 
-        if (!group.value.smashHeight && !group.value.smashDepth) {
+        if (!group.smashHeight && !group.smashDepth) {
             return node;
         }
 
-        if (group.value.smashHeight) {
+        if (group.smashHeight) {
             node.height = 0;
             // In order to influence makeVList, we have to reset the children.
             if (node.children) {
@@ -74,7 +71,7 @@ defineFunction({
             }
         }
 
-        if (group.value.smashDepth) {
+        if (group.smashDepth) {
             node.depth = 0;
             if (node.children) {
                 for (let i = 0; i < node.children.length; i++) {
@@ -95,13 +92,13 @@ defineFunction({
     },
     mathmlBuilder: (group, options) => {
         const node = new mathMLTree.MathNode(
-            "mpadded", [mml.buildGroup(group.value.body, options)]);
+            "mpadded", [mml.buildGroup(group.body, options)]);
 
-        if (group.value.smashHeight) {
+        if (group.smashHeight) {
             node.setAttribute("height", "0px");
         }
 
-        if (group.value.smashDepth) {
+        if (group.smashDepth) {
             node.setAttribute("depth", "0px");
         }
 
diff --git a/src/functions/sqrt.js b/src/functions/sqrt.js
index 4106d43a..800d36f0 100644
--- a/src/functions/sqrt.js
+++ b/src/functions/sqrt.js
@@ -22,11 +22,8 @@ defineFunction({
         return {
             type: "sqrt",
             mode: parser.mode,
-            value: {
-                type: "sqrt",
-                body: body,
-                index: index,
-            },
+            body,
+            index,
         };
     },
     htmlBuilder(group, options) {
@@ -34,7 +31,7 @@ defineFunction({
 
         // First, we do the same steps as in overline to build the inner group
         // and line
-        let inner = html.buildGroup(group.value.body, options.havingCrampedStyle());
+        let inner = html.buildGroup(group.body, options.havingCrampedStyle());
         if (inner.height === 0) {
             // Render a small surd.
             inner.height = options.fontMetrics().xHeight;
@@ -89,14 +86,14 @@ defineFunction({
             ],
         }, options);
 
-        if (!group.value.index) {
+        if (!group.index) {
             return buildCommon.makeSpan(["mord", "sqrt"], [body], options);
         } else {
             // Handle the optional root index
 
             // The index is always in scriptscript style
             const newOptions = options.havingStyle(Style.SCRIPTSCRIPT);
-            const rootm = html.buildGroup(group.value.index, newOptions, options);
+            const rootm = html.buildGroup(group.index, newOptions, options);
 
             // The amount the index is shifted by. This is taken from the TeX
             // source, in the definition of `\r@@t`.
@@ -117,7 +114,7 @@ defineFunction({
         }
     },
     mathmlBuilder(group, options) {
-        const {body, index} = group.value;
+        const {body, index} = group;
         return index ?
             new mathMLTree.MathNode(
                 "mroot", [
diff --git a/src/functions/underline.js b/src/functions/underline.js
index 41c1430d..e0bba1c3 100644
--- a/src/functions/underline.js
+++ b/src/functions/underline.js
@@ -14,20 +14,16 @@ defineFunction({
         allowedInText: true,
     },
     handler({parser}, args) {
-        const body = args[0];
         return {
             type: "underline",
             mode: parser.mode,
-            value: {
-                type: "underline",
-                body: body,
-            },
+            body: args[0],
         };
     },
     htmlBuilder(group, options) {
         // Underlines are handled in the TeXbook pg 443, Rule 10.
         // Build the inner group.
-        const innerGroup = html.buildGroup(group.value.body, options);
+        const innerGroup = html.buildGroup(group.body, options);
 
         // Create the line to go below the body
         const line = buildCommon.makeLineSpan("underline-line", options);
@@ -53,7 +49,7 @@ defineFunction({
 
         const node = new mathMLTree.MathNode(
             "munder",
-            [mml.buildGroup(group.value.body, options), operator]);
+            [mml.buildGroup(group.body, options), operator]);
         node.setAttribute("accentunder", "true");
 
         return node;
diff --git a/src/parseNode.js b/src/parseNode.js
index 4de68c11..bca006ab 100644
--- a/src/parseNode.js
+++ b/src/parseNode.js
@@ -34,11 +34,8 @@ type ParseNodeTypes = {
         type: "color",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "color",
-            color: string,
-            value: AnyParseNode[],
-        |},
+        color: string,
+        body: AnyParseNode[],
     |},
     "color-token": {|
         type: "color-token",
@@ -216,23 +213,17 @@ type ParseNodeTypes = {
         type: "enclose",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "enclose",
-            label: string,
-            backgroundColor?: ParseNode<"color-token">,
-            borderColor?: ParseNode<"color-token">,
-            body: AnyParseNode,
-        |},
+        label: string,
+        backgroundColor?: ParseNode<"color-token">,
+        borderColor?: ParseNode<"color-token">,
+        body: AnyParseNode,
     |},
     "environment": {|
         type: "environment",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "environment",
-            name: string,
-            nameGroup: AnyParseNode,
-        |},
+        name: string,
+        nameGroup: AnyParseNode,
     |},
     "font": {|
         type: "font",
@@ -245,17 +236,14 @@ type ParseNodeTypes = {
         type: "genfrac",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "genfrac",
-            continued: boolean,
-            numer: AnyParseNode,
-            denom: AnyParseNode,
-            hasBarLine: boolean,
-            leftDelim: ?string,
-            rightDelim: ?string,
-            size: StyleStr | "auto",
-            barSize: Measurement | null,
-        |},
+        continued: boolean,
+        numer: AnyParseNode,
+        denom: AnyParseNode,
+        hasBarLine: boolean,
+        leftDelim: ?string,
+        rightDelim: ?string,
+        size: StyleStr | "auto",
+        barSize: Measurement | null,
     |},
     "horizBrace": {|
         type: "horizBrace",
@@ -276,41 +264,29 @@ type ParseNodeTypes = {
         type: "htmlmathml",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "htmlmathml",
-            html: AnyParseNode[],
-            mathml: AnyParseNode[],
-        |},
+        html: AnyParseNode[],
+        mathml: AnyParseNode[],
     |},
     "infix": {|
         type: "infix",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "infix",
-            replaceWith: string,
-            sizeNode?: ParseNode<"size">,
-            token: ?Token,
-        |},
+        replaceWith: string,
+        sizeNode?: ParseNode<"size">,
+        token: ?Token,
     |},
     "kern": {|
         type: "kern",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "kern",
-            dimension: Measurement,
-        |},
+        dimension: Measurement,
     |},
     "lap": {|
         type: "lap",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "lap",
-            alignment: string,
-            body: AnyParseNode,
-        |},
+        alignment: string,
+        body: AnyParseNode,
     |},
     "leftright": {|
         type: "leftright",
@@ -352,19 +328,13 @@ type ParseNodeTypes = {
         type: "operatorname",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "operatorname",
-            value: AnyParseNode[],
-        |},
+        body: AnyParseNode[],
     |},
     "overline": {|
         type: "overline",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "overline",
-            body: AnyParseNode,
-        |},
+        body: AnyParseNode,
     |},
     "phantom": {|
         type: "phantom",
@@ -388,63 +358,44 @@ type ParseNodeTypes = {
         type: "raisebox",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "raisebox",
-            dy: ParseNode<"size">,
-            body: AnyParseNode,
-            value: AnyParseNode[],
-        |},
+        dy: ParseNode<"size">,
+        body: AnyParseNode,
     |},
     "rule": {|
         type: "rule",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "rule",
-            shift: ?Measurement,
-            width: Measurement,
-            height: Measurement,
-        |},
+        shift: ?Measurement,
+        width: Measurement,
+        height: Measurement,
     |},
     "sizing": {|
         type: "sizing",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "sizing",
-            size: number,
-            value: AnyParseNode[],
-        |},
+        size: number,
+        body: AnyParseNode[],
     |},
     "smash": {|
         type: "smash",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "smash",
-            body: AnyParseNode,
-            smashHeight: boolean,
-            smashDepth: boolean,
-        |},
+        body: AnyParseNode,
+        smashHeight: boolean,
+        smashDepth: boolean,
     |},
     "sqrt": {|
         type: "sqrt",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "sqrt",
-            body: AnyParseNode,
-            index: ?AnyParseNode,
-        |},
+        body: AnyParseNode,
+        index: ?AnyParseNode,
     |},
     "underline": {|
         type: "underline",
         mode: Mode,
         loc?: ?SourceLocation,
-        value: {|
-            type: "underline",
-            body: AnyParseNode,
-        |},
+        body: AnyParseNode,
     |},
     "xArrow": {|
         type: "xArrow",
diff --git a/src/utils.js b/src/utils.js
index c287a141..cc4edfae 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -104,8 +104,8 @@ const getBaseElem = function(group: AnyParseNode): AnyParseNode {
             return group;
         }
     } else if (group.type === "color") {
-        if (group.value.value.length === 1) {
-            return getBaseElem(group.value.value[0]);
+        if (group.body.length === 1) {
+            return getBaseElem(group.body[0]);
         } else {
             return group;
         }


Test patch:
diff --git a/test/__snapshots__/katex-spec.js.snap b/test/__snapshots__/katex-spec.js.snap
index 605a69e2..4fdcfba7 100755
--- a/test/__snapshots__/katex-spec.js.snap
+++ b/test/__snapshots__/katex-spec.js.snap
@@ -643,42 +643,39 @@ exports[`An implicit group parser within optional groups should work style comma
 [
   {
     "type": "sqrt",
-    "mode": "math",
-    "value": {
-      "type": "sqrt",
-      "body": {
-        "type": "ordgroup",
-        "mode": "math",
-        "value": [
-          {
-            "type": "mathord",
-            "mode": "math",
-            "value": "x"
-          }
-        ]
-      },
-      "index": {
-        "type": "ordgroup",
-        "mode": "math",
-        "value": [
-          {
+    "body": {
+      "type": "ordgroup",
+      "mode": "math",
+      "value": [
+        {
+          "type": "mathord",
+          "mode": "math",
+          "value": "x"
+        }
+      ]
+    },
+    "index": {
+      "type": "ordgroup",
+      "mode": "math",
+      "value": [
+        {
+          "type": "styling",
+          "mode": "math",
+          "value": {
             "type": "styling",
-            "mode": "math",
-            "value": {
-              "type": "styling",
-              "style": "text",
-              "value": [
-                {
-                  "type": "textord",
-                  "mode": "math",
-                  "value": "3"
-                }
-              ]
-            }
+            "style": "text",
+            "value": [
+              {
+                "type": "textord",
+                "mode": "math",
+                "value": "3"
+              }
+            ]
           }
-        ]
-      }
-    }
+        }
+      ]
+    },
+    "mode": "math"
   }
 ]
 `;
@@ -687,42 +684,36 @@ exports[`An implicit group parser within optional groups should work with \\colo
 [
   {
     "type": "sqrt",
-    "mode": "math",
-    "value": {
-      "type": "sqrt",
-      "body": {
-        "type": "ordgroup",
-        "mode": "math",
-        "value": [
-          {
-            "type": "mathord",
-            "mode": "math",
-            "value": "x"
-          }
-        ]
-      },
-      "index": {
-        "type": "ordgroup",
-        "mode": "math",
-        "value": [
-          {
-            "type": "color",
-            "mode": "math",
-            "value": {
-              "type": "color",
-              "color": "red",
-              "value": [
-                {
-                  "type": "textord",
-                  "mode": "math",
-                  "value": "3"
-                }
-              ]
+    "body": {
+      "type": "ordgroup",
+      "mode": "math",
+      "value": [
+        {
+          "type": "mathord",
+          "mode": "math",
+          "value": "x"
+        }
+      ]
+    },
+    "index": {
+      "type": "ordgroup",
+      "mode": "math",
+      "value": [
+        {
+          "type": "color",
+          "body": [
+            {
+              "type": "textord",
+              "mode": "math",
+              "value": "3"
             }
-          }
-        ]
-      }
-    }
+          ],
+          "color": "red",
+          "mode": "math"
+        }
+      ]
+    },
+    "mode": "math"
   }
 ]
 `;
@@ -731,43 +722,40 @@ exports[`An implicit group parser within optional groups should work with old fo
 [
   {
     "type": "sqrt",
-    "mode": "math",
-    "value": {
-      "type": "sqrt",
-      "body": {
-        "type": "ordgroup",
-        "mode": "math",
-        "value": [
-          {
-            "type": "mathord",
+    "body": {
+      "type": "ordgroup",
+      "mode": "math",
+      "value": [
+        {
+          "type": "mathord",
+          "mode": "math",
+          "value": "x"
+        }
+      ]
+    },
+    "index": {
+      "type": "ordgroup",
+      "mode": "math",
+      "value": [
+        {
+          "type": "font",
+          "body": {
+            "type": "ordgroup",
             "mode": "math",
-            "value": "x"
-          }
-        ]
-      },
-      "index": {
-        "type": "ordgroup",
-        "mode": "math",
-        "value": [
-          {
-            "type": "font",
-            "body": {
-              "type": "ordgroup",
-              "mode": "math",
-              "value": [
-                {
-                  "type": "textord",
-                  "mode": "math",
-                  "value": "3"
-                }
-              ]
-            },
-            "font": "mathtt",
-            "mode": "math"
-          }
-        ]
-      }
-    }
+            "value": [
+              {
+                "type": "textord",
+                "mode": "math",
+                "value": "3"
+              }
+            ]
+          },
+          "font": "mathtt",
+          "mode": "math"
+        }
+      ]
+    },
+    "mode": "math"
   }
 ]
 `;
@@ -776,42 +764,36 @@ exports[`An implicit group parser within optional groups should work with sizing
 [
   {
     "type": "sqrt",
-    "mode": "math",
-    "value": {
-      "type": "sqrt",
-      "body": {
-        "type": "ordgroup",
-        "mode": "math",
-        "value": [
-          {
-            "type": "mathord",
-            "mode": "math",
-            "value": "x"
-          }
-        ]
-      },
-      "index": {
-        "type": "ordgroup",
-        "mode": "math",
-        "value": [
-          {
-            "type": "sizing",
-            "mode": "math",
-            "value": {
-              "type": "sizing",
-              "size": 5,
-              "value": [
-                {
-                  "type": "textord",
-                  "mode": "math",
-                  "value": "3"
-                }
-              ]
+    "body": {
+      "type": "ordgroup",
+      "mode": "math",
+      "value": [
+        {
+          "type": "mathord",
+          "mode": "math",
+          "value": "x"
+        }
+      ]
+    },
+    "index": {
+      "type": "ordgroup",
+      "mode": "math",
+      "value": [
+        {
+          "type": "sizing",
+          "body": [
+            {
+              "type": "textord",
+              "mode": "math",
+              "value": "3"
             }
-          }
-        ]
-      }
-    }
+          ],
+          "mode": "math",
+          "size": 5
+        }
+      ]
+    },
+    "mode": "math"
   }
 ]
 `;
diff --git a/test/katex-spec.js b/test/katex-spec.js
index 99ac5e40..786ead97 100644
--- a/test/katex-spec.js
+++ b/test/katex-spec.js
@@ -92,7 +92,7 @@ describe("A rel parser", function() {
         for (let i = 0; i < parse.length; i++) {
             let group = parse[i];
             if (group.type === "htmlmathml") {
-                group = group.value.html[0];
+                group = group.html[0];
             }
             if (group.type === "mclass") {
                 expect(group.mclass).toEqual("mrel");
@@ -342,7 +342,8 @@ describe("An implicit group parser", function() {
         const sizing = parse[0];
 
         expect(sizing.type).toEqual("sizing");
-        expect(sizing.value).toBeTruthy();
+        expect(sizing.body).toBeTruthy();
+        expect(sizing.size).toBeDefined();
     });
 
     it("should apply only after the function", function() {
@@ -353,7 +354,7 @@ describe("An implicit group parser", function() {
         const sizing = parse[1];
 
         expect(sizing.type).toEqual("sizing");
-        expect(sizing.value.value).toHaveLength(3);
+        expect(sizing.body).toHaveLength(3);
     });
 
     it("should stop at the ends of groups", function() {
@@ -363,7 +364,7 @@ describe("An implicit group parser", function() {
         const sizing = group.value[1];
 
         expect(sizing.type).toEqual("sizing");
-        expect(sizing.value.value).toHaveLength(1);
+        expect(sizing.body).toHaveLength(1);
     });
 
     describe("within optional groups", () => {
@@ -441,8 +442,8 @@ describe("A frac parser", function() {
         const parse = getParsed(expression)[0];
 
         expect(parse.type).toEqual("genfrac");
-        expect(parse.value.numer).toBeDefined();
-        expect(parse.value.denom).toBeDefined();
+        expect(parse.numer).toBeDefined();
+        expect(parse.denom).toBeDefined();
     });
 
     it("should also parse cfrac, dfrac, tfrac, and genfrac", function() {
@@ -457,28 +458,28 @@ describe("A frac parser", function() {
         const dfracParse = getParsed(dfracExpression)[0];
 
         expect(dfracParse.type).toEqual("genfrac");
-        expect(dfracParse.value.numer).toBeDefined();
-        expect(dfracParse.value.denom).toBeDefined();
+        expect(dfracParse.numer).toBeDefined();
+        expect(dfracParse.denom).toBeDefined();
 
         const tfracParse = getParsed(tfracExpression)[0];
 
         expect(tfracParse.type).toEqual("genfrac");
-        expect(tfracParse.value.numer).toBeDefined();
-        expect(tfracParse.value.denom).toBeDefined();
+        expect(tfracParse.numer).toBeDefined();
+        expect(tfracParse.denom).toBeDefined();
 
         const cfracParse = getParsed(cfracExpression)[0];
 
         expect(cfracParse.type).toEqual("genfrac");
-        expect(cfracParse.value.numer).toBeDefined();
-        expect(cfracParse.value.denom).toBeDefined();
+        expect(cfracParse.numer).toBeDefined();
+        expect(cfracParse.denom).toBeDefined();
 
         const genfracParse = getParsed(genfrac1)[0];
 
         expect(genfracParse.type).toEqual("genfrac");
-        expect(genfracParse.value.numer).toBeDefined();
-        expect(genfracParse.value.denom).toBeDefined();
-        expect(genfracParse.value.leftDelim).toBeDefined();
-        expect(genfracParse.value.rightDelim).toBeDefined();
+        expect(genfracParse.numer).toBeDefined();
+        expect(genfracParse.denom).toBeDefined();
+        expect(genfracParse.leftDelim).toBeDefined();
+        expect(genfracParse.rightDelim).toBeDefined();
     });
 
     it("should fail, given math as a line thickness to genfrac", function() {
@@ -495,9 +496,9 @@ describe("A frac parser", function() {
         const parse = getParsed`x \atop y`[0];
 
         expect(parse.type).toEqual("genfrac");
-        expect(parse.value.numer).toBeDefined();
-        expect(parse.value.denom).toBeDefined();
-        expect(parse.value.hasBarLine).toEqual(false);
+        expect(parse.numer).toBeDefined();
+        expect(parse.denom).toBeDefined();
+        expect(parse.hasBarLine).toEqual(false);
     });
 });
 
@@ -520,43 +521,43 @@ describe("An over/brace/brack parser", function() {
         parse = getParsed(simpleOver)[0];
 
         expect(parse.type).toEqual("genfrac");
-        expect(parse.value.numer).toBeDefined();
-        expect(parse.value.denom).toBeDefined();
+        expect(parse.numer).toBeDefined();
+        expect(parse.denom).toBeDefined();
 
         parse = getParsed(complexOver)[0];
 
         expect(parse.type).toEqual("genfrac");
-        expect(parse.value.numer).toBeDefined();
-        expect(parse.value.denom).toBeDefined();
+        expect(parse.numer).toBeDefined();
+        expect(parse.denom).toBeDefined();
 
         const parseBraceFrac = getParsed(braceFrac)[0];
 
         expect(parseBraceFrac.type).toEqual("genfrac");
-        expect(parseBraceFrac.value.numer).toBeDefined();
-        expect(parseBraceFrac.value.denom).toBeDefined();
-        expect(parseBraceFrac.value.leftDelim).toBeDefined();
-        expect(parseBraceFrac.value.rightDelim).toBeDefined();
+        expect(parseBraceFrac.numer).toBeDefined();
+        expect(parseBraceFrac.denom).toBeDefined();
+        expect(parseBraceFrac.leftDelim).toBeDefined();
+        expect(parseBraceFrac.rightDelim).toBeDefined();
 
         const parseBrackFrac = getParsed(brackFrac)[0];
 
         expect(parseBrackFrac.type).toEqual("genfrac");
-        expect(parseBrackFrac.value.numer).toBeDefined();
-        expect(parseBrackFrac.value.denom).toBeDefined();
-        expect(parseBrackFrac.value.leftDelim).toBeDefined();
-        expect(parseBrackFrac.value.rightDelim).toBeDefined();
+        expect(parseBrackFrac.numer).toBeDefined();
+        expect(parseBrackFrac.denom).toBeDefined();
+        expect(parseBrackFrac.leftDelim).toBeDefined();
+        expect(parseBrackFrac.rightDelim).toBeDefined();
     });
 
     it("should create a numerator from the atoms before \\over", function() {
         const parse = getParsed(complexOver)[0];
 
-        const numer = parse.value.numer;
+        const numer = parse.numer;
         expect(numer.value).toHaveLength(4);
     });
 
     it("should create a demonimator from the atoms after \\over", function() {
         const parse = getParsed(complexOver)[0];
 
-        const denom = parse.value.numer;
+        const denom = parse.numer;
         expect(denom.value).toHaveLength(4);
     });
 
@@ -564,24 +565,24 @@ describe("An over/brace/brack parser", function() {
         const emptyNumerator = r`\over x`;
         const parse = getParsed(emptyNumerator)[0];
         expect(parse.type).toEqual("genfrac");
-        expect(parse.value.numer).toBeDefined();
-        expect(parse.value.denom).toBeDefined();
+        expect(parse.numer).toBeDefined();
+        expect(parse.denom).toBeDefined();
     });
 
     it("should handle empty denominators", function() {
         const emptyDenominator = r`1 \over`;
         const parse = getParsed(emptyDenominator)[0];
         expect(parse.type).toEqual("genfrac");
-        expect(parse.value.numer).toBeDefined();
-        expect(parse.value.denom).toBeDefined();
+        expect(parse.numer).toBeDefined();
+        expect(parse.denom).toBeDefined();
     });
 
     it("should handle \\displaystyle correctly", function() {
         const displaystyleExpression = r`\displaystyle 1 \over 2`;
         const parse = getParsed(displaystyleExpression)[0];
         expect(parse.type).toEqual("genfrac");
-        expect(parse.value.numer.value[0].type).toEqual("styling");
-        expect(parse.value.denom).toBeDefined();
+        expect(parse.numer.value[0].type).toEqual("styling");
+        expect(parse.denom).toBeDefined();
     });
 
     it("should handle \\textstyle correctly", function() {
@@ -593,11 +594,11 @@ describe("An over/brace/brack parser", function() {
         const nestedOverExpression = r`{1 \over 2} \over 3`;
         const parse = getParsed(nestedOverExpression)[0];
         expect(parse.type).toEqual("genfrac");
-        expect(parse.value.numer.value[0].type).toEqual("genfrac");
-        expect(parse.value.numer.value[0].value.numer.value[0].value).toEqual("1");
-        expect(parse.value.numer.value[0].value.denom.value[0].value).toEqual("2");
-        expect(parse.value.denom).toBeDefined();
-        expect(parse.value.denom.value[0].value).toEqual("3");
+        expect(parse.numer.value[0].type).toEqual("genfrac");
+        expect(parse.numer.value[0].numer.value[0].value).toEqual("1");
+        expect(parse.numer.value[0].denom.value[0].value).toEqual("2");
+        expect(parse.denom).toBeDefined();
+        expect(parse.denom.value[0].value).toEqual("3");
     });
 
     it("should fail with multiple overs in the same group", function() {
@@ -641,7 +642,8 @@ describe("A sizing parser", function() {
         const parse = getParsed(sizeExpression)[0];
 
         expect(parse.type).toEqual("sizing");
-        expect(parse.value).toBeDefined();
+        expect(parse.size).toBeDefined();
+        expect(parse.body).toBeDefined();
     });
 });
 
@@ -778,8 +780,8 @@ describe("A color parser", function() {
         const parse = getParsed(colorExpression)[0];
 
         expect(parse.type).toEqual("color");
-        expect(parse.value.color).toBeDefined();
-        expect(parse.value.value).toBeDefined();
+        expect(parse.color).toBeDefined();
+        expect(parse.body).toBeDefined();
     });
 
     it("should parse a custom color", function() {
@@ -791,8 +793,8 @@ describe("A color parser", function() {
         const parse1 = getParsed(customColorExpression1)[0];
         const parse2 = getParsed(customColorExpression2)[0];
 
-        expect(parse1.value.color).toEqual("#fA6");
-        expect(parse2.value.color).toEqual("#fA6fA6");
+        expect(parse1.color).toEqual("#fA6");
+        expect(parse2.color).toEqual("#fA6fA6");
     });
 
     it("should not parse a bad custom color", function() {
@@ -984,25 +986,25 @@ describe("A rule parser", function() {
         const emParse = getParsed(emRule)[0];
         const exParse = getParsed(exRule)[0];
 
-        expect(emParse.value.width.unit).toEqual("em");
-        expect(emParse.value.height.unit).toEqual("em");
+        expect(emParse.width.unit).toEqual("em");
+        expect(emParse.height.unit).toEqual("em");
 
-        expect(exParse.value.width.unit).toEqual("ex");
-        expect(exParse.value.height.unit).toEqual("em");
+        expect(exParse.width.unit).toEqual("ex");
+        expect(exParse.height.unit).toEqual("em");
     });
 
     it("should parse the number correctly", function() {
         const hardNumberParse = getParsed(hardNumberRule)[0];
 
-        expect(hardNumberParse.value.width.number).toBeCloseTo(1.24);
-        expect(hardNumberParse.value.height.number).toBeCloseTo(2.45);
+        expect(hardNumberParse.width.number).toBeCloseTo(1.24);
+        expect(hardNumberParse.height.number).toBeCloseTo(2.45);
     });
 
     it("should parse negative sizes", function() {
         const parse = getParsed`\rule{-1em}{- 0.2em}`[0];
 
-        expect(parse.value.width.number).toBeCloseTo(-1);
-        expect(parse.value.height.number).toBeCloseTo(-0.2);
+        expect(parse.width.number).toBeCloseTo(-1);
+        expect(parse.height.number).toBeCloseTo(-0.2);
     });
 });
 
@@ -1020,10 +1022,10 @@ describe("A kern parser", function() {
         const muParse = getParsed(muKern)[0];
         const abParse = getParsed(abKern)[1];
 
-        expect(emParse.value.dimension.unit).toEqual("em");
-        expect(exParse.value.dimension.unit).toEqual("ex");
-        expect(muParse.value.dimension.unit).toEqual("mu");
-        expect(abParse.value.dimension.unit).toEqual("em");
+        expect(emParse.dimension.unit).toEqual("em");
+        expect(exParse.dimension.unit).toEqual("ex");
+        expect(muParse.dimension.unit).toEqual("mu");
+        expect(abParse.dimension.unit).toEqual("em");
     });
 
     it("should not parse invalid units", function() {
@@ -1033,12 +1035,12 @@ describe("A kern parser", function() {
 
     it("should parse negative sizes", function() {
         const parse = getParsed`\kern{-1em}`[0];
-        expect(parse.value.dimension.number).toBeCloseTo(-1);
+        expect(parse.dimension.number).toBeCloseTo(-1);
     });
 
     it("should parse positive sizes", function() {
         const parse = getParsed`\kern{+1em}`[0];
-        expect(parse.value.dimension.number).toBeCloseTo(1);
+        expect(parse.dimension.number).toBeCloseTo(1);
     });
 });
 
@@ -1060,12 +1062,12 @@ describe("A non-braced kern parser", function() {
         const abParse2 = getParsed(abKern2)[1];
         const abParse3 = getParsed(abKern3)[1];
 
-        expect(emParse.value.dimension.unit).toEqual("em");
-        expect(exParse.value.dimension.unit).toEqual("ex");
-        expect(muParse.value.dimension.unit).toEqual("mu");
-        expect(abParse1.value.dimension.unit).toEqual("mu");
-        expect(abParse2.value.dimension.unit).toEqual("mu");
-        expect(abParse3.value.dimension.unit).toEqual("mu");
+        expect(emParse.dimension.unit).toEqual("em");
+        expect(exParse.dimension.unit).toEqual("ex");
+        expect(muParse.dimension.unit).toEqual("mu");
+        expect(abParse1.dimension.unit).toEqual("mu");
+        expect(abParse2.dimension.unit).toEqual("mu");
+        expect(abParse3.dimension.unit).toEqual("mu");
     });
 
     it("should parse elements on either side of a kern", function() {
@@ -1091,12 +1093,12 @@ describe("A non-braced kern parser", function() {
 
     it("should parse negative sizes", function() {
         const parse = getParsed`\kern-1em`[0];
-        expect(parse.value.dimension.number).toBeCloseTo(-1);
+        expect(parse.dimension.number).toBeCloseTo(-1);
     });
 
     it("should parse positive sizes", function() {
         const parse = getParsed`\kern+1em`[0];
-        expect(parse.value.dimension.number).toBeCloseTo(1);
+        expect(parse.dimension.number).toBeCloseTo(1);
     });
 
     it("should handle whitespace", function() {
@@ -1105,7 +1107,7 @@ describe("A non-braced kern parser", function() {
 
         expect(abParse).toHaveLength(3);
         expect(abParse[0].value).toEqual("a");
-        expect(abParse[1].value.dimension.unit).toEqual("mu");
+        expect(abParse[1].dimension.unit).toEqual("mu");
         expect(abParse[2].value).toEqual("b");
     });
 });
@@ -1511,9 +1513,9 @@ describe("A font parser", function() {
 
     it("should work with \\textcolor", function() {
         const colorMathbbParse = getParsed`\textcolor{blue}{\mathbb R}`[0];
-        expect(colorMathbbParse.value.type).toEqual("color");
-        expect(colorMathbbParse.value.color).toEqual("blue");
-        const body = colorMathbbParse.value.value;
+        expect(colorMathbbParse.type).toEqual("color");
+        expect(colorMathbbParse.color).toEqual("blue");
+        const body = colorMathbbParse.body;
         expect(body).toHaveLength(1);
         expect(body[0].type).toEqual("font");
         expect(body[0].font).toEqual("mathbb");
@@ -2589,7 +2591,7 @@ describe("A parser that does not throw on unsupported commands", function() {
     it("should produce color nodes with a color value given by errorColor", function() {
         const parsedInput = getParsed(r`\error`, noThrowSettings);
         expect(parsedInput[0].type).toBe("color");
-        expect(parsedInput[0].value.color).toBe(errorColor);
+        expect(parsedInput[0].color).toBe(errorColor);
     });
 
     it("should build katex-error span for other type of KaTeX error", function() {