From 95d84badcae89aa72a6f1433b851304700320920 Mon Sep 17 00:00:00 2001
From: kpdecker <kpdecker@gmail.com>
Date: Tue, 18 Aug 2015 23:57:27 -0700
Subject: [PATCH] Drop AST constructors in favor of JSON

These were little more than object literal statements that were less clear due to their use of index-based arguments.

Fixes #1077
---
 lib/handlebars/compiler/ast.js      | 126 -----------------------
 lib/handlebars/compiler/base.js     |   3 +-
 lib/handlebars/compiler/compiler.js |   9 +-
 lib/handlebars/compiler/helpers.js  |  72 +++++++++----
 lib/handlebars/compiler/visitor.js  |   3 +-
 spec/ast.js                         | 151 ++++------------------------
 spec/compiler.js                    |  10 +-
 spec/parser.js                      |   5 +-
 spec/visitor.js                     |   4 +-
 src/handlebars.yy                   |  57 ++++++++---
 10 files changed, 141 insertions(+), 299 deletions(-)

diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js
index 599dab8d2..1699569ba 100644
--- a/lib/handlebars/compiler/ast.js
+++ b/lib/handlebars/compiler/ast.js
@@ -1,130 +1,4 @@
 let AST = {
-  Program: function(statements, blockParams, strip, locInfo) {
-    this.loc = locInfo;
-    this.type = 'Program';
-    this.body = statements;
-
-    this.blockParams = blockParams;
-    this.strip = strip;
-  },
-
-  MustacheStatement: function(path, params, hash, escaped, strip, locInfo) {
-    this.loc = locInfo;
-    this.type = 'MustacheStatement';
-
-    this.path = path;
-    this.params = params || [];
-    this.hash = hash;
-    this.escaped = escaped;
-
-    this.strip = strip;
-  },
-
-  BlockStatement: function(path, params, hash, program, inverse, openStrip, inverseStrip, closeStrip, locInfo) {
-    this.loc = locInfo;
-    this.type = 'BlockStatement';
-
-    this.path = path;
-    this.params = params || [];
-    this.hash = hash;
-    this.program = program;
-    this.inverse = inverse;
-
-    this.openStrip = openStrip;
-    this.inverseStrip = inverseStrip;
-    this.closeStrip = closeStrip;
-  },
-
-  PartialStatement: function(name, params, hash, strip, locInfo) {
-    this.loc = locInfo;
-    this.type = 'PartialStatement';
-
-    this.name = name;
-    this.params = params || [];
-    this.hash = hash;
-
-    this.indent = '';
-    this.strip = strip;
-  },
-
-  ContentStatement: function(string, locInfo) {
-    this.loc = locInfo;
-    this.type = 'ContentStatement';
-    this.original = this.value = string;
-  },
-
-  CommentStatement: function(comment, strip, locInfo) {
-    this.loc = locInfo;
-    this.type = 'CommentStatement';
-    this.value = comment;
-
-    this.strip = strip;
-  },
-
-  SubExpression: function(path, params, hash, locInfo) {
-    this.loc = locInfo;
-
-    this.type = 'SubExpression';
-    this.path = path;
-    this.params = params || [];
-    this.hash = hash;
-  },
-
-  PathExpression: function(data, depth, parts, original, locInfo) {
-    this.loc = locInfo;
-    this.type = 'PathExpression';
-
-    this.data = data;
-    this.original = original;
-    this.parts = parts;
-    this.depth = depth;
-  },
-
-  StringLiteral: function(string, locInfo) {
-    this.loc = locInfo;
-    this.type = 'StringLiteral';
-    this.original =
-      this.value = string;
-  },
-
-  NumberLiteral: function(number, locInfo) {
-    this.loc = locInfo;
-    this.type = 'NumberLiteral';
-    this.original =
-      this.value = Number(number);
-  },
-
-  BooleanLiteral: function(bool, locInfo) {
-    this.loc = locInfo;
-    this.type = 'BooleanLiteral';
-    this.original =
-      this.value = bool === 'true';
-  },
-
-  UndefinedLiteral: function(locInfo) {
-    this.loc = locInfo;
-    this.type = 'UndefinedLiteral';
-    this.original = this.value = undefined;
-  },
-
-  NullLiteral: function(locInfo) {
-    this.loc = locInfo;
-    this.type = 'NullLiteral';
-    this.original = this.value = null;
-  },
-
-  Hash: function(pairs, locInfo) {
-    this.loc = locInfo;
-    this.type = 'Hash';
-    this.pairs = pairs;
-  },
-  HashPair: function(key, value, locInfo) {
-    this.loc = locInfo;
-    this.type = 'HashPair';
-    this.key = key;
-    this.value = value;
-  },
-
   // Public API used to evaluate derived attributes regarding AST nodes
   helpers: {
     // a mustache is definitely a helper if:
diff --git a/lib/handlebars/compiler/base.js b/lib/handlebars/compiler/base.js
index 85c2997b4..c6871d399 100644
--- a/lib/handlebars/compiler/base.js
+++ b/lib/handlebars/compiler/base.js
@@ -1,5 +1,4 @@
 import parser from './parser';
-import AST from './ast';
 import WhitespaceControl from './whitespace-control';
 import * as Helpers from './helpers';
 import { extend } from '../utils';
@@ -7,7 +6,7 @@ import { extend } from '../utils';
 export { parser };
 
 let yy = {};
-extend(yy, Helpers, AST);
+extend(yy, Helpers);
 
 export function parse(input, options) {
   // Just return if an already-compiled AST was passed in.
diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js
index 59a425f47..c8db7c955 100644
--- a/lib/handlebars/compiler/compiler.js
+++ b/lib/handlebars/compiler/compiler.js
@@ -514,6 +514,13 @@ function transformLiteralToPath(sexpr) {
     let literal = sexpr.path;
     // Casting to string here to make false and 0 literal values play nicely with the rest
     // of the system.
-    sexpr.path = new AST.PathExpression(false, 0, [literal.original + ''], literal.original + '', literal.loc);
+    sexpr.path = {
+      type: 'PathExpression',
+      data: false,
+      depth: 0,
+      parts: [literal.original + ''],
+      original: literal.original + '',
+      loc: literal.loc
+    };
   }
 }
diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js
index f2edfa110..bf72034a0 100644
--- a/lib/handlebars/compiler/helpers.js
+++ b/lib/handlebars/compiler/helpers.js
@@ -32,8 +32,8 @@ export function stripComment(comment) {
                 .replace(/-?-?~?\}\}$/, '');
 }
 
-export function preparePath(data, parts, locInfo) {
-  locInfo = this.locInfo(locInfo);
+export function preparePath(data, parts, loc) {
+  loc = this.locInfo(loc);
 
   let original = data ? '@' : '',
       dig = [],
@@ -49,7 +49,7 @@ export function preparePath(data, parts, locInfo) {
 
     if (!isLiteral && (part === '..' || part === '.' || part === 'this')) {
       if (dig.length > 0) {
-        throw new Exception('Invalid path: ' + original, {loc: locInfo});
+        throw new Exception('Invalid path: ' + original, {loc});
       } else if (part === '..') {
         depth++;
         depthString += '../';
@@ -59,7 +59,14 @@ export function preparePath(data, parts, locInfo) {
     }
   }
 
-  return new this.PathExpression(data, depth, dig, original, locInfo);
+  return {
+    type: 'PathExpression',
+    data,
+    depth,
+    parts: dig,
+    original,
+    loc
+  };
 }
 
 export function prepareMustache(path, params, hash, open, strip, locInfo) {
@@ -67,7 +74,15 @@ export function prepareMustache(path, params, hash, open, strip, locInfo) {
   let escapeFlag = open.charAt(3) || open.charAt(2),
       escaped = escapeFlag !== '{' && escapeFlag !== '&';
 
-  return new this.MustacheStatement(path, params, hash, escaped, strip, this.locInfo(locInfo));
+  return {
+    type: 'MustacheStatement',
+    path,
+    params,
+    hash,
+    escaped,
+    strip,
+    loc: this.locInfo(locInfo)
+  };
 }
 
 export function prepareRawBlock(openRawBlock, contents, close, locInfo) {
@@ -78,13 +93,24 @@ export function prepareRawBlock(openRawBlock, contents, close, locInfo) {
   }
 
   locInfo = this.locInfo(locInfo);
-  let program = new this.Program(contents, null, {}, locInfo);
+  let program = {
+    type: 'Program',
+    body: contents,
+    strip: {},
+    loc: locInfo
+  };
 
-  return new this.BlockStatement(
-      openRawBlock.path, openRawBlock.params, openRawBlock.hash,
-      program, undefined,
-      {}, {}, {},
-      locInfo);
+  return {
+    type: 'BlockStatement',
+    path: openRawBlock.path,
+    params: openRawBlock.params,
+    hash: openRawBlock.hash,
+    program,
+    openStrip: {},
+    inverseStrip: {},
+    closeStrip: {},
+    loc: locInfo
+  };
 }
 
 export function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) {
@@ -115,11 +141,18 @@ export function prepareBlock(openBlock, program, inverseAndProgram, close, inver
     program = inverted;
   }
 
-  return new this.BlockStatement(
-      openBlock.path, openBlock.params, openBlock.hash,
-      program, inverse,
-      openBlock.strip, inverseStrip, close && close.strip,
-      this.locInfo(locInfo));
+  return {
+    type: 'BlockStatement',
+    path: openBlock.path,
+    params: openBlock.params,
+    hash: openBlock.hash,
+    program,
+    inverse,
+    openStrip: openBlock.strip,
+    inverseStrip,
+    closeStrip: close && close.strip,
+    loc: this.locInfo(locInfo)
+  };
 }
 
 export function prepareProgram(statements, loc) {
@@ -143,7 +176,12 @@ export function prepareProgram(statements, loc) {
     }
   }
 
-  return new this.Program(statements, null, {}, loc);
+  return {
+    type: 'Program',
+    body: statements,
+    strip: {},
+    loc: loc
+  };
 }
 
 
diff --git a/lib/handlebars/compiler/visitor.js b/lib/handlebars/compiler/visitor.js
index ba7b3760c..47f86e0d3 100644
--- a/lib/handlebars/compiler/visitor.js
+++ b/lib/handlebars/compiler/visitor.js
@@ -1,5 +1,4 @@
 import Exception from '../exception';
-import AST from './ast';
 
 function Visitor() {
   this.parents = [];
@@ -14,7 +13,7 @@ Visitor.prototype = {
     let value = this.accept(node[name]);
     if (this.mutating) {
       // Hacky sanity check:
-      if (value && (!value.type || !AST[value.type])) {
+      if (value && typeof value.type !== 'string') {
         throw new Exception('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type);
       }
       node[name] = value;
diff --git a/spec/ast.js b/spec/ast.js
index dc5410fa2..8f346d882 100644
--- a/spec/ast.js
+++ b/spec/ast.js
@@ -5,159 +5,46 @@ describe('ast', function() {
 
   var AST = Handlebars.AST;
 
-  var LOCATION_INFO = {
-    start: {
-      line: 1,
-      column: 1
-    },
-    end: {
-      line: 1,
-      column: 1
-    }
-  };
-
-  function testLocationInfoStorage(node) {
-    equals(node.loc.start.line, 1);
-    equals(node.loc.start.column, 1);
-    equals(node.loc.end.line, 1);
-    equals(node.loc.end.column, 1);
-  }
-
-  describe('MustacheStatement', function() {
-    it('should store args', function() {
-      var mustache = new AST.MustacheStatement({}, null, null, true, {}, LOCATION_INFO);
-      equals(mustache.type, 'MustacheStatement');
-      equals(mustache.escaped, true);
-      testLocationInfoStorage(mustache);
-    });
-  });
   describe('BlockStatement', function() {
     it('should throw on mustache mismatch', function() {
       shouldThrow(function() {
         handlebarsEnv.parse('\n  {{#foo}}{{/bar}}');
       }, Handlebars.Exception, "foo doesn't match bar - 2:5");
     });
-
-    it('stores location info', function() {
-      var mustacheNode = new AST.MustacheStatement([{ original: 'foo'}], null, null, false, {});
-      var block = new AST.BlockStatement(
-            mustacheNode,
-            null, null,
-            {body: []},
-            {body: []},
-            {},
-            {},
-            {},
-            LOCATION_INFO);
-      testLocationInfoStorage(block);
-    });
-  });
-  describe('PathExpression', function() {
-    it('stores location info', function() {
-      var idNode = new AST.PathExpression(false, 0, [], 'foo', LOCATION_INFO);
-      testLocationInfoStorage(idNode);
-    });
-  });
-
-  describe('Hash', function() {
-    it('stores location info', function() {
-      var hash = new AST.Hash([], LOCATION_INFO);
-      testLocationInfoStorage(hash);
-    });
-  });
-
-  describe('ContentStatement', function() {
-    it('stores location info', function() {
-      var content = new AST.ContentStatement('HI', LOCATION_INFO);
-      testLocationInfoStorage(content);
-    });
-  });
-
-  describe('CommentStatement', function() {
-    it('stores location info', function() {
-      var comment = new AST.CommentStatement('HI', {}, LOCATION_INFO);
-      testLocationInfoStorage(comment);
-    });
-  });
-
-  describe('NumberLiteral', function() {
-    it('stores location info', function() {
-      var integer = new AST.NumberLiteral('6', LOCATION_INFO);
-      testLocationInfoStorage(integer);
-    });
-  });
-
-  describe('StringLiteral', function() {
-    it('stores location info', function() {
-      var string = new AST.StringLiteral('6', LOCATION_INFO);
-      testLocationInfoStorage(string);
-    });
-  });
-
-  describe('BooleanLiteral', function() {
-    it('stores location info', function() {
-      var bool = new AST.BooleanLiteral('true', LOCATION_INFO);
-      testLocationInfoStorage(bool);
-    });
-  });
-
-  describe('PartialStatement', function() {
-    it('provides default params', function() {
-      var pn = new AST.PartialStatement('so_partial', undefined, {}, {}, LOCATION_INFO);
-      equals(pn.params.length, 0);
-    });
-    it('stores location info', function() {
-      var pn = new AST.PartialStatement('so_partial', [], {}, {}, LOCATION_INFO);
-      testLocationInfoStorage(pn);
-    });
-  });
-
-  describe('Program', function() {
-    it('storing location info', function() {
-      var pn = new AST.Program([], null, {}, LOCATION_INFO);
-      testLocationInfoStorage(pn);
-    });
-  });
-
-  describe('SubExpression', function() {
-    it('provides default params', function() {
-      var pn = new AST.SubExpression('path', undefined, {}, LOCATION_INFO);
-      equals(pn.params.length, 0);
-    });
   });
 
   describe('helpers', function() {
     describe('#helperExpression', function() {
       it('should handle mustache statements', function() {
-        equals(AST.helpers.helperExpression(new AST.MustacheStatement('foo', [], undefined, false, {}, LOCATION_INFO)), false);
-        equals(AST.helpers.helperExpression(new AST.MustacheStatement('foo', [1], undefined, false, {}, LOCATION_INFO)), true);
-        equals(AST.helpers.helperExpression(new AST.MustacheStatement('foo', [], {}, false, {}, LOCATION_INFO)), true);
+        equals(AST.helpers.helperExpression({type: 'MustacheStatement', params: [], hash: undefined}), false);
+        equals(AST.helpers.helperExpression({type: 'MustacheStatement', params: [1], hash: undefined}), true);
+        equals(AST.helpers.helperExpression({type: 'MustacheStatement', params: [], hash: {}}), true);
       });
       it('should handle block statements', function() {
-        equals(AST.helpers.helperExpression(new AST.BlockStatement('foo', [], undefined, false, {}, LOCATION_INFO)), false);
-        equals(AST.helpers.helperExpression(new AST.BlockStatement('foo', [1], undefined, false, {}, LOCATION_INFO)), true);
-        equals(AST.helpers.helperExpression(new AST.BlockStatement('foo', [], {}, false, {}, LOCATION_INFO)), true);
+        equals(AST.helpers.helperExpression({type: 'BlockStatement', params: [], hash: undefined}), false);
+        equals(AST.helpers.helperExpression({type: 'BlockStatement', params: [1], hash: undefined}), true);
+        equals(AST.helpers.helperExpression({type: 'BlockStatement', params: [], hash: {}}), true);
       });
       it('should handle subexpressions', function() {
-        equals(AST.helpers.helperExpression(new AST.SubExpression()), true);
+        equals(AST.helpers.helperExpression({type: 'SubExpression'}), true);
       });
       it('should work with non-helper nodes', function() {
-        equals(AST.helpers.helperExpression(new AST.Program([], [], {}, LOCATION_INFO)), false);
+        equals(AST.helpers.helperExpression({type: 'Program'}), false);
 
-        equals(AST.helpers.helperExpression(new AST.PartialStatement()), false);
-        equals(AST.helpers.helperExpression(new AST.ContentStatement('a', LOCATION_INFO)), false);
-        equals(AST.helpers.helperExpression(new AST.CommentStatement('a', {}, LOCATION_INFO)), false);
+        equals(AST.helpers.helperExpression({type: 'PartialStatement'}), false);
+        equals(AST.helpers.helperExpression({type: 'ContentStatement'}), false);
+        equals(AST.helpers.helperExpression({type: 'CommentStatement'}), false);
 
-        equals(AST.helpers.helperExpression(new AST.PathExpression(false, 0, ['a'], 'a', LOCATION_INFO)), false);
+        equals(AST.helpers.helperExpression({type: 'PathExpression'}), false);
 
-        equals(AST.helpers.helperExpression(new AST.StringLiteral('a', LOCATION_INFO)), false);
-        equals(AST.helpers.helperExpression(new AST.NumberLiteral(1, LOCATION_INFO)), false);
-        equals(AST.helpers.helperExpression(new AST.BooleanLiteral(true, LOCATION_INFO)), false);
-        equals(AST.helpers.helperExpression(new AST.UndefinedLiteral(LOCATION_INFO)), false);
-        equals(AST.helpers.helperExpression(new AST.NullLiteral(LOCATION_INFO)), false);
+        equals(AST.helpers.helperExpression({type: 'StringLiteral'}), false);
+        equals(AST.helpers.helperExpression({type: 'NumberLiteral'}), false);
+        equals(AST.helpers.helperExpression({type: 'BooleanLiteral'}), false);
+        equals(AST.helpers.helperExpression({type: 'UndefinedLiteral'}), false);
+        equals(AST.helpers.helperExpression({type: 'NullLiteral'}), false);
 
-        equals(AST.helpers.helperExpression(new AST.Hash([], LOCATION_INFO)), false);
-        equals(AST.helpers.helperExpression(new AST.HashPair('foo', 'bar', LOCATION_INFO)), false);
+        equals(AST.helpers.helperExpression({type: 'Hash'}), false);
+        equals(AST.helpers.helperExpression({type: 'HashPair'}), false);
       });
     });
   });
diff --git a/spec/compiler.js b/spec/compiler.js
index fe4b63a30..be1fb007d 100644
--- a/spec/compiler.js
+++ b/spec/compiler.js
@@ -39,7 +39,10 @@ describe('compiler', function() {
     });
 
     it('can utilize AST instance', function() {
-      equal(Handlebars.compile(new Handlebars.AST.Program([ new Handlebars.AST.ContentStatement('Hello')], null, {}))(), 'Hello');
+      equal(Handlebars.compile({
+        type: 'Program',
+        body: [ {type: 'ContentStatement', value: 'Hello'}]
+      })(), 'Hello');
     });
 
     it('can pass through an empty string', function() {
@@ -58,7 +61,10 @@ describe('compiler', function() {
     });
 
     it('can utilize AST instance', function() {
-      equal(/return "Hello"/.test(Handlebars.precompile(new Handlebars.AST.Program([ new Handlebars.AST.ContentStatement('Hello')]), null, {})), true);
+      equal(/return "Hello"/.test(Handlebars.precompile({
+        type: 'Program',
+        body: [ {type: 'ContentStatement', value: 'Hello'}]
+      })), true);
     });
 
     it('can pass through an empty string', function() {
diff --git a/spec/parser.js b/spec/parser.js
index 424e2d178..82c32d05d 100644
--- a/spec/parser.js
+++ b/spec/parser.js
@@ -228,7 +228,10 @@ describe('parser', function() {
 
   describe('externally compiled AST', function() {
     it('can pass through an already-compiled AST', function() {
-      equals(astFor(new Handlebars.AST.Program([new Handlebars.AST.ContentStatement('Hello')], null)), 'CONTENT[ \'Hello\' ]\n');
+      equals(astFor({
+        type: 'Program',
+        body: [ {type: 'ContentStatement', value: 'Hello'}]
+      }), 'CONTENT[ \'Hello\' ]\n');
     });
   });
 
diff --git a/spec/visitor.js b/spec/visitor.js
index 1f50d79c1..23113cf77 100644
--- a/spec/visitor.js
+++ b/spec/visitor.js
@@ -49,7 +49,7 @@ describe('Visitor', function() {
 
         visitor.mutating = true;
         visitor.StringLiteral = function(string) {
-          return new Handlebars.AST.NumberLiteral(42, string.locInfo);
+          return {type: 'NumberLiteral', value: 42, loc: string.loc};
         };
 
         var ast = Handlebars.parse('{{foo foo="foo"}}');
@@ -109,7 +109,7 @@ describe('Visitor', function() {
 
         visitor.mutating = true;
         visitor.StringLiteral = function(string) {
-          return new Handlebars.AST.NumberLiteral(42, string.locInfo);
+          return {type: 'NumberLiteral', value: 42, loc: string.locInfo};
         };
 
         var ast = Handlebars.parse('{{foo "foo"}}');
diff --git a/src/handlebars.yy b/src/handlebars.yy
index ecc79afc6..a05b477a3 100644
--- a/src/handlebars.yy
+++ b/src/handlebars.yy
@@ -18,12 +18,24 @@ statement
   | rawBlock -> $1
   | partial -> $1
   | content -> $1
-  | COMMENT -> new yy.CommentStatement(yy.stripComment($1), yy.stripFlags($1, $1), yy.locInfo(@$))
-  ;
+  | COMMENT {
+    $$ = {
+      type: 'CommentStatement',
+      value: yy.stripComment($1),
+      strip: yy.stripFlags($1, $1),
+      loc: yy.locInfo(@$)
+    };
+  };
 
 content
-  : CONTENT -> new yy.ContentStatement($1, yy.locInfo(@$))
-  ;
+  : CONTENT {
+    $$ = {
+      type: 'ContentStatement',
+      original: $1,
+      value: $1,
+      loc: yy.locInfo(@$)
+    };
+  };
 
 rawBlock
   : openRawBlock content+ END_RAW_BLOCK -> yy.prepareRawBlock($1, $2, $3, @$)
@@ -77,7 +89,17 @@ mustache
   ;
 
 partial
-  : OPEN_PARTIAL partialName param* hash? CLOSE -> new yy.PartialStatement($2, $3, $4, yy.stripFlags($1, $5), yy.locInfo(@$))
+  : OPEN_PARTIAL partialName param* hash? CLOSE {
+    $$ = {
+      type: 'PartialStatement',
+      name: $2,
+      params: $3,
+      hash: $4,
+      indent: '',
+      strip: yy.stripFlags($1, $5),
+      loc: yy.locInfo(@$)
+    };
+  }
   ;
 
 param
@@ -86,15 +108,22 @@ param
   ;
 
 sexpr
-  : OPEN_SEXPR helperName param* hash? CLOSE_SEXPR -> new yy.SubExpression($2, $3, $4, yy.locInfo(@$))
-  ;
+  : OPEN_SEXPR helperName param* hash? CLOSE_SEXPR {
+    $$ = {
+      type: 'SubExpression',
+      path: $2,
+      params: $3,
+      hash: $4,
+      loc: yy.locInfo(@$)
+    };
+  };
 
 hash
-  : hashSegment+ -> new yy.Hash($1, yy.locInfo(@$))
+  : hashSegment+ -> {type: 'Hash', pairs: $1, loc: yy.locInfo(@$)}
   ;
 
 hashSegment
-  : ID EQUALS param -> new yy.HashPair(yy.id($1), $3, yy.locInfo(@$))
+  : ID EQUALS param -> {type: 'HashPair', key: yy.id($1), value: $3, loc: yy.locInfo(@$)}
   ;
 
 blockParams
@@ -104,11 +133,11 @@ blockParams
 helperName
   : path -> $1
   | dataName -> $1
-  | STRING -> new yy.StringLiteral($1, yy.locInfo(@$))
-  | NUMBER -> new yy.NumberLiteral($1, yy.locInfo(@$))
-  | BOOLEAN -> new yy.BooleanLiteral($1, yy.locInfo(@$))
-  | UNDEFINED -> new yy.UndefinedLiteral(yy.locInfo(@$))
-  | NULL -> new yy.NullLiteral(yy.locInfo(@$))
+  | STRING -> {type: 'StringLiteral', value: $1, original: $1, loc: yy.locInfo(@$)}
+  | NUMBER -> {type: 'NumberLiteral', value: Number($1), original: Number($1), loc: yy.locInfo(@$)}
+  | BOOLEAN -> {type: 'BooleanLiteral', value: $1 === 'true', original: $1 === 'true', loc: yy.locInfo(@$)}
+  | UNDEFINED -> {type: 'UndefinedLiteral', original: undefined, value: undefined, loc: yy.locInfo(@$)}
+  | NULL -> {type: 'NullLiteral', original: null, value: null, loc: yy.locInfo(@$)}
   ;
 
 partialName
