diff options
-rw-r--r-- | Libraries/LibGUI/JSSyntaxHighlighter.cpp | 1 | ||||
-rw-r--r-- | Libraries/LibJS/AST.cpp | 12 | ||||
-rw-r--r-- | Libraries/LibJS/AST.h | 21 | ||||
-rw-r--r-- | Libraries/LibJS/CMakeLists.txt | 3 | ||||
-rw-r--r-- | Libraries/LibJS/Forward.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Lexer.cpp | 44 | ||||
-rw-r--r-- | Libraries/LibJS/Lexer.h | 3 | ||||
-rw-r--r-- | Libraries/LibJS/MarkupGenerator.cpp | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Parser.cpp | 10 | ||||
-rw-r--r-- | Libraries/LibJS/Parser.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/GlobalObject.cpp | 7 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/Object.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/RegExpConstructor.cpp | 64 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/RegExpConstructor.h | 46 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/RegExpObject.cpp | 59 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/RegExpObject.h | 54 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/RegExpPrototype.cpp | 47 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/RegExpPrototype.h | 42 | ||||
-rw-r--r-- | Libraries/LibJS/Token.h | 2 | ||||
-rw-r--r-- | Userland/js.cpp | 10 |
20 files changed, 424 insertions, 5 deletions
diff --git a/Libraries/LibGUI/JSSyntaxHighlighter.cpp b/Libraries/LibGUI/JSSyntaxHighlighter.cpp index 0747a92142..657afd4599 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 840b29412d..2f12a49e36 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -39,6 +39,7 @@ #include <LibJS/Runtime/NativeFunction.h> #include <LibJS/Runtime/PrimitiveString.h> #include <LibJS/Runtime/Reference.h> +#include <LibJS/Runtime/RegExpObject.h> #include <LibJS/Runtime/ScriptFunction.h> #include <LibJS/Runtime/Shape.h> #include <LibJS/Runtime/StringObject.h> @@ -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 82b6cf979e..72f8972cc3 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 38f84aee58..c170fe908a 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 68fd24cad3..17c7953221 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 f67d8ff9bb..597e48c187 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 71f22b0535..18f9bfb2d3 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 6efaf65855..8836c7b069 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 1739c7c2b8..d21b0171c2 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -464,6 +464,8 @@ NonnullRefPtr<Expression> Parser::parse_primary_expression() return parse_function_node<FunctionExpression>(); 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<Expression> Parser::parse_primary_expression() } } +NonnullRefPtr<RegExpLiteral> Parser::parse_regexp_literal() +{ + auto content = consume().value(); + auto flags = match(TokenType::RegexFlags) ? consume().value() : ""; + return create_ast_node<RegExpLiteral>(content.substring_view(1, content.length() - 2), flags); +} + NonnullRefPtr<Expression> 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 4d36d85f98..0b256d3f76 100644 --- a/Libraries/LibJS/Parser.h +++ b/Libraries/LibJS/Parser.h @@ -70,6 +70,7 @@ public: NonnullRefPtr<Expression> parse_expression(int min_precedence, Associativity associate = Associativity::Right, Vector<TokenType> forbidden = {}); NonnullRefPtr<Expression> parse_primary_expression(); NonnullRefPtr<Expression> parse_unary_prefixed_expression(); + NonnullRefPtr<RegExpLiteral> parse_regexp_literal(); NonnullRefPtr<ObjectExpression> parse_object_expression(); NonnullRefPtr<ArrayExpression> parse_array_expression(); NonnullRefPtr<StringLiteral> parse_string_literal(Token token); diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp index 2032178256..f3aebccb0e 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -26,8 +26,6 @@ */ #include <AK/LogStream.h> -#include <AK/String.h> -#include <LibJS/Heap/Heap.h> #include <LibJS/Interpreter.h> #include <LibJS/Runtime/ArrayConstructor.h> #include <LibJS/Runtime/ArrayPrototype.h> @@ -36,13 +34,11 @@ #include <LibJS/Runtime/ConsoleObject.h> #include <LibJS/Runtime/DateConstructor.h> #include <LibJS/Runtime/DatePrototype.h> -#include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/ErrorConstructor.h> #include <LibJS/Runtime/ErrorPrototype.h> #include <LibJS/Runtime/FunctionConstructor.h> #include <LibJS/Runtime/FunctionPrototype.h> #include <LibJS/Runtime/GlobalObject.h> -#include <LibJS/Runtime/LexicalEnvironment.h> #include <LibJS/Runtime/MathObject.h> #include <LibJS/Runtime/NativeFunction.h> #include <LibJS/Runtime/NumberConstructor.h> @@ -53,6 +49,8 @@ #include <LibJS/Runtime/ProxyConstructor.h> #include <LibJS/Runtime/ProxyPrototype.h> #include <LibJS/Runtime/ReflectObject.h> +#include <LibJS/Runtime/RegExpConstructor.h> +#include <LibJS/Runtime/RegExpPrototype.h> #include <LibJS/Runtime/Shape.h> #include <LibJS/Runtime/StringConstructor.h> #include <LibJS/Runtime/StringPrototype.h> @@ -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 519c108d73..5da31021d0 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 0000000000..9a2198a3f2 --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpConstructor.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibJS/Interpreter.h> +#include <LibJS/Runtime/Error.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/RegExpConstructor.h> +#include <LibJS/Runtime/RegExpObject.h> + +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 0000000000..76f4321052 --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpConstructor.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibJS/Runtime/NativeFunction.h> + +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 0000000000..d6ce164e98 --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpObject.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <AK/StringBuilder.h> +#include <LibJS/Heap/Heap.h> +#include <LibJS/Interpreter.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/PrimitiveString.h> +#include <LibJS/Runtime/RegExpObject.h> +#include <LibJS/Runtime/Value.h> + + +namespace JS { + +RegExpObject* RegExpObject::create(GlobalObject& global_object, String content, String flags) +{ + return global_object.heap().allocate<RegExpObject>(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 0000000000..5acc72b4ce --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpObject.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibJS/AST.h> +#include <LibJS/Runtime/Object.h> + +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 0000000000..629d3eadc3 --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpPrototype.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <AK/Function.h> +#include <AK/StringBuilder.h> +#include <LibJS/Heap/Heap.h> +#include <LibJS/Interpreter.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/PrimitiveString.h> +#include <LibJS/Runtime/RegExpObject.h> +#include <LibJS/Runtime/RegExpPrototype.h> + +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 0000000000..07c5d51b3a --- /dev/null +++ b/Libraries/LibJS/Runtime/RegExpPrototype.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibJS/Runtime/RegExpObject.h> + +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 884a249f27..e12747df0b 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 70caa45adb..b994f1c0e4 100644 --- a/Userland/js.cpp +++ b/Userland/js.cpp @@ -40,6 +40,7 @@ #include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/PrimitiveString.h> +#include <LibJS/Runtime/RegExpObject.h> #include <LibJS/Runtime/Shape.h> #include <LibJS/Runtime/Value.h> #include <LibLine/Editor.h> @@ -224,6 +225,12 @@ static void print_error(const JS::Object& object, HashTable<JS::Object*>&) printf(": %s", error.message().characters()); } +static void print_regexp(const JS::Object& object, HashTable<JS::Object*>&) +{ + auto& regexp = static_cast<const JS::RegExpObject&>(object); + printf("\033[34;1m/%s/%s\033[0m", regexp.content().characters(), regexp.flags().characters()); +} + void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects) { if (value.is_empty()) { @@ -252,6 +259,8 @@ void print_value(JS::Value value, HashTable<JS::Object*>& 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; |