From e1cb1a0e3ca2312fd403d10fee5960691287a436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Santos?= Date: Sat, 5 Sep 2015 22:32:57 +0100 Subject: [PATCH 1/2] Parse and output ES6 template strings. Yikes! --- lib/ast.js | 16 ++++++++++++++++ lib/output.js | 14 ++++++++++++++ lib/parse.js | 40 +++++++++++++++++++++++++++++++++++++++- test/compress/harmony.js | 10 ++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/lib/ast.js b/lib/ast.js index 7e7503ee..13260582 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -501,6 +501,22 @@ var AST_Destructuring = DEFNODE("Destructuring", "names is_array", { } }); +var AST_TemplateString = DEFNODE("TemplateString", "segments", { + $documentation: "A template string literal", + $propdoc: { + segments: "[string|AST_Expression]* One or more segments. They can be the parts that are evaluated, or the raw string parts." + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.segments.forEach(function(seg, i){ + if (i % 2 !== 0) { + seg._walk(visitor); + } + }); + }); + } +}); + /* -----[ JUMPS ]----- */ var AST_Jump = DEFNODE("Jump", null, { diff --git a/lib/output.js b/lib/output.js index 5d8bef10..c6cb254e 100644 --- a/lib/output.js +++ b/lib/output.js @@ -776,6 +776,20 @@ function OutputStream(options) { self._do_print(output); }); + DEFPRINT(AST_TemplateString, function(self, output) { + output.print("`"); + for (var i = 0; i < self.segments.length; i++) { + if (typeof self.segments[i] !== "string") { + output.print("${"); + self.segments[i].print(output); + output.print("}"); + } else { + output.print(self.segments[i]); + } + } + output.print("`"); + }); + AST_Arrow.DEFMETHOD("_do_print", function(output){ var self = this; var parent = output.parent(); diff --git a/lib/parse.js b/lib/parse.js index 3d3eb664..00ba3f55 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -114,7 +114,7 @@ var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u18 var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:")); -var PUNC_CHARS = makePredicate(characters("[]{}(),;:")); +var PUNC_CHARS = makePredicate(characters("[]{}(),;:`")); var REGEXP_MODIFIERS = makePredicate(characters("gmsiy")); @@ -595,6 +595,9 @@ function tokenizer($TEXT, filename, html5_comments) { parse_error("Unexpected character '" + ch + "'"); }; + next_token.next = next; + next_token.peek = peek; + next_token.context = function(nc) { if (nc) S = nc; return S; @@ -805,6 +808,7 @@ function parse($TEXT, options) { }); case "[": case "(": + case "`": return simple_statement(); case ";": next(); @@ -1336,6 +1340,8 @@ function parse($TEXT, options) { return subscripts(array_(), allow_calls); case "{": return subscripts(object_or_object_destructuring_(), allow_calls); + case "`": + return subscripts(template_string(), allow_calls); } unexpected(); } @@ -1352,6 +1358,38 @@ function parse($TEXT, options) { unexpected(); }; + function template_string() { + var tokenizer_S = S.input, start = S.token, segments = [], segment = "", ch; + + while ((ch = tokenizer_S.next()) !== "`") { + if (ch === "$" && tokenizer_S.peek() === "{") { + segments.push(segment); segment = ""; + tokenizer_S.next(); + next(); + segments.push(expression()); + expect("}"); + if (is("punc", "`")) { + break; + } + continue; + } + segment += ch; + if (ch === "\\") { + segment += tokenizer_S.next(); + } + } + + segments.push(segment); + + next(); + + return new AST_TemplateString({ + start: start, + segments: segments, + end: S.token + }); + } + function expr_list(closing, allow_trailing_comma, allow_empty) { var first = true, a = []; while (!is("punc", closing)) { diff --git a/test/compress/harmony.js b/test/compress/harmony.js index 2fcfd70f..f55c9868 100644 --- a/test/compress/harmony.js +++ b/test/compress/harmony.js @@ -46,6 +46,16 @@ typeof_arrow_functions: { expect_exact: "\"function\";" } +template_strings: { + input: { + ``; + `xx\`x`; + `${ foo + 2 }`; + ` foo ${ bar + `baz ${ qux }` }`; + } + expect_exact: "``;`xx\\`x`;`${foo+2}`;` foo ${bar+`baz ${qux}`}`;"; +} + destructuring_arguments: { input: { (function ( a ) { }); From 242c61be9485ce3ea1bc0752ff80c38468dc5ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Santos?= Date: Sat, 5 Sep 2015 22:48:17 +0100 Subject: [PATCH 2/2] prefixed template strings, like "String.raw`foo\nbar`". --- lib/ast.js | 12 ++++++++++++ lib/output.js | 4 ++++ lib/parse.js | 7 +++++++ test/compress/harmony.js | 8 ++++++++ 4 files changed, 31 insertions(+) diff --git a/lib/ast.js b/lib/ast.js index 13260582..818dde28 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -501,6 +501,18 @@ var AST_Destructuring = DEFNODE("Destructuring", "names is_array", { } }); +var AST_PrefixedTemplateString = DEFNODE("PrefixedTemplateString", "template_string prefix", { + $documentation: "A templatestring with a prefix, such as String.raw`foobarbaz`", + $propdoc: { + template_string: "[AST_TemplateString] The template string", + prefix: "[AST_SymbolRef|AST_PropAccess] The prefix, which can be a symbol such as `foo` or a dotted expression such as `String.raw`." + }, + _walk: function(visitor) { + this.prefix._walk(visitor); + this.template_string._walk(visitor); + } +}) + var AST_TemplateString = DEFNODE("TemplateString", "segments", { $documentation: "A template string literal", $propdoc: { diff --git a/lib/output.js b/lib/output.js index c6cb254e..2b40fdfc 100644 --- a/lib/output.js +++ b/lib/output.js @@ -776,6 +776,10 @@ function OutputStream(options) { self._do_print(output); }); + DEFPRINT(AST_PrefixedTemplateString, function(self, output) { + self.prefix.print(output); + self.template_string.print(output); + }); DEFPRINT(AST_TemplateString, function(self, output) { output.print("`"); for (var i = 0; i < self.segments.length; i++) { diff --git a/lib/parse.js b/lib/parse.js index 00ba3f55..759ce2a3 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -1717,6 +1717,13 @@ function parse($TEXT, options) { }); return arrow_function(expr); } + if ((expr instanceof AST_SymbolRef || expr instanceof AST_PropAccess) && is("punc", "`")) { + return new AST_PrefixedTemplateString({ + start: start, + prefix: expr, + template_string: template_string() + }) + } if (commas && is("punc", ",")) { next(); return new AST_Seq({ diff --git a/test/compress/harmony.js b/test/compress/harmony.js index f55c9868..930bb03e 100644 --- a/test/compress/harmony.js +++ b/test/compress/harmony.js @@ -56,6 +56,14 @@ template_strings: { expect_exact: "``;`xx\\`x`;`${foo+2}`;` foo ${bar+`baz ${qux}`}`;"; } +template_string_prefixes: { + input: { + String.raw`foo`; + foo `bar`; + } + expect_exact: "String.raw`foo`;foo`bar`;"; +} + destructuring_arguments: { input: { (function ( a ) { });