diff options
author | Matthew Olsson <matthewcolsson@gmail.com> | 2020-05-06 16:34:14 -0700 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-05-07 23:05:55 +0200 |
commit | b5f1df57ed24ccd2f28a2357238ddd2a7f879ee4 (patch) | |
tree | e78c9b62dadaabc14c184642b5fc95dcca7ea886 | |
parent | 9d0c6a32bde762f1681824ec9796d8761c7fcc63 (diff) | |
download | serenity-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.cpp | 10 | ||||
-rw-r--r-- | Libraries/LibJS/AST.h | 10 | ||||
-rw-r--r-- | Libraries/LibJS/Parser.cpp | 27 | ||||
-rw-r--r-- | Libraries/LibJS/Parser.h | 2 | ||||
-rw-r--r-- | Libraries/LibJS/Tests/tagged-template-literals.js | 12 |
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); |