summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Olsson <matthewcolsson@gmail.com>2020-05-06 16:34:14 -0700
committerAndreas Kling <kling@serenityos.org>2020-05-07 23:05:55 +0200
commitb5f1df57ed24ccd2f28a2357238ddd2a7f879ee4 (patch)
treee78c9b62dadaabc14c184642b5fc95dcca7ea886
parent9d0c6a32bde762f1681824ec9796d8761c7fcc63 (diff)
downloadserenity-b5f1df57ed24ccd2f28a2357238ddd2a7f879ee4.zip
LibJS: Add raw strings to tagged template literals
When calling a function with a tagged template, the first array that is passed in now contains a "raw" property with the raw, escaped strings.
-rw-r--r--Libraries/LibJS/AST.cpp10
-rw-r--r--Libraries/LibJS/AST.h10
-rw-r--r--Libraries/LibJS/Parser.cpp27
-rw-r--r--Libraries/LibJS/Parser.h2
-rw-r--r--Libraries/LibJS/Tests/tagged-template-literals.js12
5 files changed, 52 insertions, 9 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp
index a3537577e9..0a758197c0 100644
--- a/Libraries/LibJS/AST.cpp
+++ b/Libraries/LibJS/AST.cpp
@@ -1347,6 +1347,16 @@ Value TaggedTemplateLiteral::execute(Interpreter& interpreter) const
else
arguments.append(value);
}
+
+ auto* raw_strings = Array::create(interpreter.global_object());
+ for (auto& raw_string : m_template_literal->raw_strings()) {
+ auto value = raw_string.execute(interpreter);
+ if (interpreter.exception())
+ return {};
+ raw_strings->elements().append(value);
+ }
+ strings->put("raw", raw_strings, 0);
+
return interpreter.call(tag_function, js_undefined(), move(arguments));
}
diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h
index af753de830..e487c98d21 100644
--- a/Libraries/LibJS/AST.h
+++ b/Libraries/LibJS/AST.h
@@ -779,7 +779,13 @@ private:
class TemplateLiteral final : public Expression {
public:
TemplateLiteral(NonnullRefPtrVector<Expression> expressions)
- : m_expressions(expressions)
+ : m_expressions(move(expressions))
+ {
+ }
+
+ TemplateLiteral(NonnullRefPtrVector<Expression> expressions, NonnullRefPtrVector<Expression> raw_strings)
+ : m_expressions(move(expressions))
+ , m_raw_strings(move(raw_strings))
{
}
@@ -787,11 +793,13 @@ public:
virtual void dump(int indent) const override;
const NonnullRefPtrVector<Expression>& expressions() const { return m_expressions; }
+ const NonnullRefPtrVector<Expression>& raw_strings() const { return m_raw_strings; }
private:
virtual const char* class_name() const override { return "TemplateLiteral"; }
const NonnullRefPtrVector<Expression> m_expressions;
+ const NonnullRefPtrVector<Expression> m_raw_strings;
};
class TaggedTemplateLiteral final : public Expression {
diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp
index 05568e7dd4..9ca17144a5 100644
--- a/Libraries/LibJS/Parser.cpp
+++ b/Libraries/LibJS/Parser.cpp
@@ -417,7 +417,7 @@ NonnullRefPtr<Expression> Parser::parse_primary_expression()
case TokenType::BracketOpen:
return parse_array_expression();
case TokenType::TemplateLiteralStart:
- return parse_template_literal();
+ return parse_template_literal(false);
case TokenType::New:
return parse_new_expression();
default:
@@ -560,18 +560,29 @@ NonnullRefPtr<ArrayExpression> Parser::parse_array_expression()
return create_ast_node<ArrayExpression>(move(elements));
}
-NonnullRefPtr<TemplateLiteral> Parser::parse_template_literal()
+NonnullRefPtr<TemplateLiteral> Parser::parse_template_literal(bool is_tagged)
{
consume(TokenType::TemplateLiteralStart);
NonnullRefPtrVector<Expression> expressions;
+ NonnullRefPtrVector<Expression> raw_strings;
+
+ auto append_empty_string = [&expressions, &raw_strings, is_tagged]() {
+ auto string_literal = create_ast_node<StringLiteral>("");
+ expressions.append(string_literal);
+ if (is_tagged)
+ raw_strings.append(string_literal);
+ };
if (!match(TokenType::TemplateLiteralString))
- expressions.append(create_ast_node<StringLiteral>(""));
+ append_empty_string();
while (!match(TokenType::TemplateLiteralEnd) && !match(TokenType::UnterminatedTemplateLiteral)) {
if (match(TokenType::TemplateLiteralString)) {
- expressions.append(create_ast_node<StringLiteral>(consume().string_value()));
+ auto token = consume();
+ expressions.append(create_ast_node<StringLiteral>(token.string_value()));
+ if (is_tagged)
+ raw_strings.append(create_ast_node<StringLiteral>(token.value()));
} else if (match(TokenType::TemplateLiteralExprStart)) {
consume(TokenType::TemplateLiteralExprStart);
if (match(TokenType::TemplateLiteralExprEnd)) {
@@ -587,7 +598,7 @@ NonnullRefPtr<TemplateLiteral> Parser::parse_template_literal()
consume(TokenType::TemplateLiteralExprEnd);
if (!match(TokenType::TemplateLiteralString))
- expressions.append(create_ast_node<StringLiteral>(""));
+ append_empty_string();
}
}
@@ -597,6 +608,8 @@ NonnullRefPtr<TemplateLiteral> Parser::parse_template_literal()
consume(TokenType::TemplateLiteralEnd);
}
+ if (is_tagged)
+ return create_ast_node<TemplateLiteral>(expressions, raw_strings);
return create_ast_node<TemplateLiteral>(expressions);
}
@@ -604,7 +617,7 @@ NonnullRefPtr<Expression> Parser::parse_expression(int min_precedence, Associati
{
auto expression = parse_primary_expression();
while (match(TokenType::TemplateLiteralStart)) {
- auto template_literal = parse_template_literal();
+ auto template_literal = parse_template_literal(true);
expression = create_ast_node<TaggedTemplateLiteral>(move(expression), move(template_literal));
}
while (match_secondary_expression()) {
@@ -617,7 +630,7 @@ NonnullRefPtr<Expression> Parser::parse_expression(int min_precedence, Associati
Associativity new_associativity = operator_associativity(m_parser_state.m_current_token.type());
expression = parse_secondary_expression(move(expression), new_precedence, new_associativity);
while (match(TokenType::TemplateLiteralStart)) {
- auto template_literal = parse_template_literal();
+ auto template_literal = parse_template_literal(true);
expression = create_ast_node<TaggedTemplateLiteral>(move(expression), move(template_literal));
}
}
diff --git a/Libraries/LibJS/Parser.h b/Libraries/LibJS/Parser.h
index 273dce7c53..90eb95ed09 100644
--- a/Libraries/LibJS/Parser.h
+++ b/Libraries/LibJS/Parser.h
@@ -69,7 +69,7 @@ public:
NonnullRefPtr<Expression> parse_unary_prefixed_expression();
NonnullRefPtr<ObjectExpression> parse_object_expression();
NonnullRefPtr<ArrayExpression> parse_array_expression();
- NonnullRefPtr<TemplateLiteral> parse_template_literal();
+ NonnullRefPtr<TemplateLiteral> parse_template_literal(bool is_tagged);
NonnullRefPtr<Expression> parse_secondary_expression(NonnullRefPtr<Expression>, int min_precedence, Associativity associate = Associativity::Right);
NonnullRefPtr<CallExpression> parse_call_expression(NonnullRefPtr<Expression>);
NonnullRefPtr<NewExpression> parse_new_expression();
diff --git a/Libraries/LibJS/Tests/tagged-template-literals.js b/Libraries/LibJS/Tests/tagged-template-literals.js
index 7318ab5e35..1c7498e1bd 100644
--- a/Libraries/LibJS/Tests/tagged-template-literals.js
+++ b/Libraries/LibJS/Tests/tagged-template-literals.js
@@ -82,6 +82,18 @@ try {
var rating = "great";
assert(review`${name} is a ${rating} project!` === "**SerenityOS** is a _great_ project!");
+ const getTemplateObject = (...rest) => rest;
+ const getRawTemplateStrings = arr => arr.raw;
+
+ let o = getTemplateObject`foo\nbar`;
+ assert(Object.getOwnPropertyNames(o[0]).includes('raw'));
+
+ let raw = getRawTemplateStrings`foo${1 + 3}\nbar`;
+ assert(!Object.getOwnPropertyNames(raw).includes('raw'));
+ assert(raw.length === 2);
+ assert(raw[0] === 'foo');
+ assert(raw[1].length === 5 && raw[1] === '\\nbar');
+
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);