diff --git a/Libraries/LibGUI/JSSyntaxHighlighter.cpp b/Libraries/LibGUI/JSSyntaxHighlighter.cpp index 0747a92142f..657afd45990 100644 --- a/Libraries/LibGUI/JSSyntaxHighlighter.cpp +++ b/Libraries/LibGUI/JSSyntaxHighlighter.cpp @@ -46,6 +46,7 @@ static TextStyle style_for_token_type(Gfx::Palette palette, JS::TokenType type) case JS::TokenType::TemplateLiteralEnd: case JS::TokenType::TemplateLiteralString: case JS::TokenType::RegexLiteral: + case JS::TokenType::RegexFlags: case JS::TokenType::UnterminatedStringLiteral: return { palette.syntax_string() }; case JS::TokenType::BracketClose: diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 840b29412de..2f12a49e36f 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -1429,6 +1430,17 @@ Value NullLiteral::execute(Interpreter&) const return js_null(); } +void RegExpLiteral::dump(int indent) const +{ + print_indent(indent); + printf("%s (/%s/%s)\n", class_name(), content().characters(), flags().characters()); +} + +Value RegExpLiteral::execute(Interpreter& interpreter) const +{ + return RegExpObject::create(interpreter.global_object(), content(), flags()); +} + void ArrayExpression::dump(int indent) const { ASTNode::dump(indent); diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index 82b6cf979e8..72f8972cc32 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -585,6 +585,27 @@ private: virtual const char* class_name() const override { return "NullLiteral"; } }; +class RegExpLiteral final : public Literal { +public: + explicit RegExpLiteral(String content, String flags) + : m_content(content) + , m_flags(flags) + { + } + + virtual Value execute(Interpreter&) const override; + virtual void dump(int indent) const override; + + const String& content() const { return m_content; } + const String& flags() const { return m_flags; } + +private: + virtual const char* class_name() const override { return "RegexLiteral"; } + + String m_content; + String m_flags; +}; + class Identifier final : public Expression { public: explicit Identifier(const FlyString& string) diff --git a/Libraries/LibJS/CMakeLists.txt b/Libraries/LibJS/CMakeLists.txt index 38f84aee583..c170fe908ae 100644 --- a/Libraries/LibJS/CMakeLists.txt +++ b/Libraries/LibJS/CMakeLists.txt @@ -47,6 +47,9 @@ set(SOURCES Runtime/ProxyPrototype.cpp Runtime/Reference.cpp Runtime/ReflectObject.cpp + Runtime/RegExpConstructor.cpp + Runtime/RegExpObject.cpp + Runtime/RegExpPrototype.cpp Runtime/ScriptFunction.cpp Runtime/Shape.cpp Runtime/StringConstructor.cpp diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index 68fd24cad37..17c79532211 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -35,6 +35,7 @@ __JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor) \ __JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor) \ __JS_ENUMERATE(ProxyObject, proxy, ProxyPrototype, ProxyConstructor) \ + __JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor) \ __JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor) \ __JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor) diff --git a/Libraries/LibJS/Lexer.cpp b/Libraries/LibJS/Lexer.cpp index f67d8ff9bb4..597e48c187b 100644 --- a/Libraries/LibJS/Lexer.cpp +++ b/Libraries/LibJS/Lexer.cpp @@ -244,6 +244,22 @@ bool Lexer::is_numeric_literal_start() const return isdigit(m_current_char) || (m_current_char == '.' && m_position < m_source.length() && isdigit(m_source[m_position])); } +bool Lexer::slash_means_division() const +{ + auto type = m_current_token.type(); + return type == TokenType::BoolLiteral + || type == TokenType::BracketClose + || type == TokenType::CurlyClose + || type == TokenType::Identifier + || type == TokenType::NullLiteral + || type == TokenType::NumericLiteral + || type == TokenType::ParenClose + || type == TokenType::RegexLiteral + || type == TokenType::StringLiteral + || type == TokenType::TemplateLiteralEnd + || type == TokenType::This; +} + Token Lexer::next() { size_t trivia_start = m_position; @@ -277,7 +293,11 @@ Token Lexer::next() size_t value_start = m_position; auto token_type = TokenType::Invalid; - if (m_current_char == '`') { + if (m_current_token.type() == TokenType::RegexLiteral && !is_eof() && isalpha(m_current_char)) { + token_type = TokenType::RegexFlags; + while (!is_eof() && isalpha(m_current_char)) + consume(); + } else if (m_current_char == '`') { consume(); if (!in_template) { @@ -452,6 +472,28 @@ Token Lexer::next() if (!found_four_char_token && !found_three_char_token && !found_two_char_token && !found_one_char_token) { consume(); token_type = TokenType::Invalid; + } else if (token_type == TokenType::Slash && !slash_means_division()) { + token_type = TokenType::RegexLiteral; + + while (!is_eof()) { + if (m_current_char == '[') { + m_regex_is_in_character_class = true; + } else if (m_current_char == ']') { + m_regex_is_in_character_class = false; + } else if (!m_regex_is_in_character_class && m_current_char == '/') { + break; + } + + if (match('\\', '/') || match('\\', '[') || (m_regex_is_in_character_class && match('\\', ']'))) + consume(); + consume(); + } + + if (is_eof()) { + token_type = TokenType::UnterminatedRegexLiteral; + } else { + consume(); + } } } diff --git a/Libraries/LibJS/Lexer.h b/Libraries/LibJS/Lexer.h index 71f22b05352..18f9bfb2d36 100644 --- a/Libraries/LibJS/Lexer.h +++ b/Libraries/LibJS/Lexer.h @@ -53,6 +53,7 @@ private: bool match(char, char) const; bool match(char, char, char) const; bool match(char, char, char, char) const; + bool slash_means_division() const; StringView m_source; size_t m_position { 0 }; @@ -61,6 +62,8 @@ private: size_t m_line_number { 1 }; size_t m_line_column { 0 }; + bool m_regex_is_in_character_class { false }; + struct TemplateState { bool in_expr; u8 open_bracket_count; diff --git a/Libraries/LibJS/MarkupGenerator.cpp b/Libraries/LibJS/MarkupGenerator.cpp index 6efaf65855b..8836c7b069b 100644 --- a/Libraries/LibJS/MarkupGenerator.cpp +++ b/Libraries/LibJS/MarkupGenerator.cpp @@ -223,6 +223,7 @@ MarkupGenerator::StyleType MarkupGenerator::style_type_for_token(Token token) case TokenType::TemplateLiteralEnd: case TokenType::TemplateLiteralString: case TokenType::RegexLiteral: + case TokenType::RegexFlags: case TokenType::UnterminatedStringLiteral: return StyleType::String; case TokenType::BracketClose: diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 1739c7c2b8d..d21b0171c27 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -464,6 +464,8 @@ NonnullRefPtr Parser::parse_primary_expression() return parse_function_node(); case TokenType::BracketOpen: return parse_array_expression(); + case TokenType::RegexLiteral: + return parse_regexp_literal(); case TokenType::TemplateLiteralStart: return parse_template_literal(false); case TokenType::New: @@ -475,6 +477,13 @@ NonnullRefPtr Parser::parse_primary_expression() } } +NonnullRefPtr Parser::parse_regexp_literal() +{ + auto content = consume().value(); + auto flags = match(TokenType::RegexFlags) ? consume().value() : ""; + return create_ast_node(content.substring_view(1, content.length() - 2), flags); +} + NonnullRefPtr Parser::parse_unary_prefixed_expression() { auto precedence = operator_precedence(m_parser_state.m_current_token.type()); @@ -1449,6 +1458,7 @@ bool Parser::match_expression() const || type == TokenType::ParenOpen || type == TokenType::Function || type == TokenType::This + || type == TokenType::RegexLiteral || match_unary_prefixed_expression(); } diff --git a/Libraries/LibJS/Parser.h b/Libraries/LibJS/Parser.h index 4d36d85f98b..0b256d3f76e 100644 --- a/Libraries/LibJS/Parser.h +++ b/Libraries/LibJS/Parser.h @@ -70,6 +70,7 @@ public: NonnullRefPtr parse_expression(int min_precedence, Associativity associate = Associativity::Right, Vector forbidden = {}); NonnullRefPtr parse_primary_expression(); NonnullRefPtr parse_unary_prefixed_expression(); + NonnullRefPtr parse_regexp_literal(); NonnullRefPtr parse_object_expression(); NonnullRefPtr parse_array_expression(); NonnullRefPtr parse_string_literal(Token token); diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp index 20321782560..f3aebccb0e5 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -26,8 +26,6 @@ */ #include -#include -#include #include #include #include @@ -36,13 +34,11 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include @@ -53,6 +49,8 @@ #include #include #include +#include +#include #include #include #include @@ -106,6 +104,7 @@ void GlobalObject::initialize() add_constructor("Number", m_number_constructor, *m_number_prototype); add_constructor("Object", m_object_constructor, *m_object_prototype); add_constructor("Proxy", m_proxy_constructor, *m_proxy_prototype); + add_constructor("RegExp", m_regexp_constructor, *m_regexp_prototype); add_constructor("String", m_string_constructor, *m_string_prototype); add_constructor("Symbol", m_symbol_constructor, *m_symbol_prototype); diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h index 519c108d735..5da31021d0b 100644 --- a/Libraries/LibJS/Runtime/Object.h +++ b/Libraries/LibJS/Runtime/Object.h @@ -102,6 +102,7 @@ public: virtual bool is_bound_function() const { return false; } virtual bool is_native_property() const { return false; } virtual bool is_proxy_object() const { return false; } + virtual bool is_regexp_object() const { return false; } virtual bool is_string_object() const { return false; } virtual bool is_symbol_object() const { return false; } diff --git a/Libraries/LibJS/Runtime/RegExpConstructor.cpp b/Libraries/LibJS/Runtime/RegExpConstructor.cpp new file mode 100644 index 00000000000..9a2198a3f24 --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpConstructor.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020, Matthew Olsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +namespace JS { + +RegExpConstructor::RegExpConstructor() + : NativeFunction("RegExp", *interpreter().global_object().function_prototype()) +{ + define_property("prototype", interpreter().global_object().regexp_prototype(), 0); + define_property("length", Value(2), Attribute::Configurable); +} + +RegExpConstructor::~RegExpConstructor() +{ +} + +Value RegExpConstructor::call(Interpreter& interpreter) +{ + return construct(interpreter); +} + +Value RegExpConstructor::construct(Interpreter& interpreter) +{ + if (!interpreter.argument_count()) + return RegExpObject::create(interpreter.global_object(), "(?:)", ""); + auto contents = interpreter.argument(0).to_string(interpreter); + if (interpreter.exception()) + return {}; + auto flags = interpreter.argument_count() > 1 ? interpreter.argument(1).to_string(interpreter) : ""; + if (interpreter.exception()) + return {}; + return RegExpObject::create(interpreter.global_object(), contents, flags); +} + +} diff --git a/Libraries/LibJS/Runtime/RegExpConstructor.h b/Libraries/LibJS/Runtime/RegExpConstructor.h new file mode 100644 index 00000000000..76f4321052a --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpConstructor.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, Matthew Olsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace JS { + +class RegExpConstructor final : public NativeFunction { +public: + RegExpConstructor(); + virtual ~RegExpConstructor() override; + + virtual Value call(Interpreter&) override; + virtual Value construct(Interpreter&) override; + +private: + virtual bool has_constructor() const override { return true; } + virtual const char* class_name() const override { return "RegExpConstructor"; } +}; + +} diff --git a/Libraries/LibJS/Runtime/RegExpObject.cpp b/Libraries/LibJS/Runtime/RegExpObject.cpp new file mode 100644 index 00000000000..d6ce164e98f --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpObject.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020, Matthew Olsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + + +namespace JS { + +RegExpObject* RegExpObject::create(GlobalObject& global_object, String content, String flags) +{ + return global_object.heap().allocate(content, flags, *global_object.regexp_prototype()); +} + +RegExpObject::RegExpObject(String content, String flags, Object& prototype) + : Object(&prototype) + , m_content(content) + , m_flags(flags) +{ +} + +RegExpObject::~RegExpObject() +{ +} + +Value RegExpObject::to_string() const +{ + return js_string(interpreter(), String::format("/%s/%s", content().characters(), flags().characters())); +} + +} diff --git a/Libraries/LibJS/Runtime/RegExpObject.h b/Libraries/LibJS/Runtime/RegExpObject.h new file mode 100644 index 00000000000..5acc72b4cea --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpObject.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020, Matthew Olsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +namespace JS { + +class RegExpObject : public Object { +public: + static RegExpObject* create(GlobalObject&, String content, String flags); + + RegExpObject(String content, String flags, Object& prototype); + virtual ~RegExpObject() override; + + const String& content() const { return m_content; } + const String& flags() const { return m_flags; } + + Value to_string() const override; + +private: + virtual const char* class_name() const override { return "RegExpObject"; } + virtual bool is_regexp_object() const override { return true; } + + String m_content; + String m_flags; +}; + +} diff --git a/Libraries/LibJS/Runtime/RegExpPrototype.cpp b/Libraries/LibJS/Runtime/RegExpPrototype.cpp new file mode 100644 index 00000000000..629d3eadc3b --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpPrototype.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020, Matthew Olsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace JS { + +RegExpPrototype::RegExpPrototype() + : RegExpObject({}, {}, *interpreter().global_object().object_prototype()) +{ +} + +RegExpPrototype::~RegExpPrototype() +{ +} + +} diff --git a/Libraries/LibJS/Runtime/RegExpPrototype.h b/Libraries/LibJS/Runtime/RegExpPrototype.h new file mode 100644 index 00000000000..07c5d51b3a9 --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpPrototype.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020, Matthew Olsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace JS { + +class RegExpPrototype final : public RegExpObject { +public: + RegExpPrototype(); + virtual ~RegExpPrototype() override; + +private: + virtual const char* class_name() const override { return "RegExpPrototype"; } +}; + +} diff --git a/Libraries/LibJS/Token.h b/Libraries/LibJS/Token.h index 884a249f274..e12747df0ba 100644 --- a/Libraries/LibJS/Token.h +++ b/Libraries/LibJS/Token.h @@ -113,6 +113,7 @@ namespace JS { __ENUMERATE_JS_TOKEN(QuestionMark) \ __ENUMERATE_JS_TOKEN(QuestionMarkPeriod) \ __ENUMERATE_JS_TOKEN(RegexLiteral) \ + __ENUMERATE_JS_TOKEN(RegexFlags) \ __ENUMERATE_JS_TOKEN(Return) \ __ENUMERATE_JS_TOKEN(Semicolon) \ __ENUMERATE_JS_TOKEN(ShiftLeft) \ @@ -138,6 +139,7 @@ namespace JS { __ENUMERATE_JS_TOKEN(Typeof) \ __ENUMERATE_JS_TOKEN(UnsignedShiftRight) \ __ENUMERATE_JS_TOKEN(UnsignedShiftRightEquals) \ + __ENUMERATE_JS_TOKEN(UnterminatedRegexLiteral) \ __ENUMERATE_JS_TOKEN(UnterminatedStringLiteral) \ __ENUMERATE_JS_TOKEN(UnterminatedTemplateLiteral) \ __ENUMERATE_JS_TOKEN(Var) \ diff --git a/Userland/js.cpp b/Userland/js.cpp index 70caa45adba..b994f1c0e47 100644 --- a/Userland/js.cpp +++ b/Userland/js.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -224,6 +225,12 @@ static void print_error(const JS::Object& object, HashTable&) printf(": %s", error.message().characters()); } +static void print_regexp(const JS::Object& object, HashTable&) +{ + auto& regexp = static_cast(object); + printf("\033[34;1m/%s/%s\033[0m", regexp.content().characters(), regexp.flags().characters()); +} + void print_value(JS::Value value, HashTable& seen_objects) { if (value.is_empty()) { @@ -252,6 +259,8 @@ void print_value(JS::Value value, HashTable& seen_objects) return print_date(object, seen_objects); if (object.is_error()) return print_error(object, seen_objects); + if (object.is_regexp_object()) + return print_regexp(object, seen_objects); return print_object(object, seen_objects); } @@ -618,6 +627,7 @@ int main(int argc, char** argv) case JS::TokenType::TemplateLiteralEnd: case JS::TokenType::TemplateLiteralString: case JS::TokenType::RegexLiteral: + case JS::TokenType::RegexFlags: case JS::TokenType::UnterminatedStringLiteral: stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Green), Line::Style::Bold }); break;