summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Libraries/LibGUI/JSSyntaxHighlighter.cpp1
-rw-r--r--Libraries/LibJS/AST.cpp12
-rw-r--r--Libraries/LibJS/AST.h21
-rw-r--r--Libraries/LibJS/CMakeLists.txt3
-rw-r--r--Libraries/LibJS/Forward.h1
-rw-r--r--Libraries/LibJS/Lexer.cpp44
-rw-r--r--Libraries/LibJS/Lexer.h3
-rw-r--r--Libraries/LibJS/MarkupGenerator.cpp1
-rw-r--r--Libraries/LibJS/Parser.cpp10
-rw-r--r--Libraries/LibJS/Parser.h1
-rw-r--r--Libraries/LibJS/Runtime/GlobalObject.cpp7
-rw-r--r--Libraries/LibJS/Runtime/Object.h1
-rw-r--r--Libraries/LibJS/Runtime/RegExpConstructor.cpp64
-rw-r--r--Libraries/LibJS/Runtime/RegExpConstructor.h46
-rw-r--r--Libraries/LibJS/Runtime/RegExpObject.cpp59
-rw-r--r--Libraries/LibJS/Runtime/RegExpObject.h54
-rw-r--r--Libraries/LibJS/Runtime/RegExpPrototype.cpp47
-rw-r--r--Libraries/LibJS/Runtime/RegExpPrototype.h42
-rw-r--r--Libraries/LibJS/Token.h2
-rw-r--r--Userland/js.cpp10
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;