| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-12 10:51:41 +01:00
										 |  |  | #include <AK/ByteBuffer.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | #include <AK/NonnullOwnPtr.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | #include <AK/StringBuilder.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-12 10:51:41 +01:00
										 |  |  | #include <LibCore/ArgsParser.h>
 | 
					
						
							|  |  |  | #include <LibCore/File.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | #include <LibJS/AST.h>
 | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  | #include <LibJS/Console.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | #include <LibJS/Interpreter.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-11 19:27:43 +01:00
										 |  |  | #include <LibJS/Parser.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  | #include <LibJS/Runtime/Array.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-31 18:43:45 +01:00
										 |  |  | #include <LibJS/Runtime/Date.h>
 | 
					
						
							| 
									
										
										
										
											2020-04-02 09:54:15 +02:00
										 |  |  | #include <LibJS/Runtime/Error.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-26 14:54:43 +01:00
										 |  |  | #include <LibJS/Runtime/Function.h>
 | 
					
						
							| 
									
										
										
										
											2020-04-01 18:53:28 +02:00
										 |  |  | #include <LibJS/Runtime/GlobalObject.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-16 14:20:30 +01:00
										 |  |  | #include <LibJS/Runtime/Object.h>
 | 
					
						
							|  |  |  | #include <LibJS/Runtime/PrimitiveString.h>
 | 
					
						
							| 
									
										
										
										
											2020-04-02 19:32:21 +02:00
										 |  |  | #include <LibJS/Runtime/Shape.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-16 14:20:30 +01:00
										 |  |  | #include <LibJS/Runtime/Value.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-31 13:34:57 +02:00
										 |  |  | #include <LibLine/Editor.h>
 | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:10 +04:30
										 |  |  | #include <signal.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  | Vector<String> repl_statements; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | class ReplObject : public JS::GlobalObject { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     ReplObject(); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:18:06 +02:00
										 |  |  |     virtual void initialize() override; | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |     virtual ~ReplObject() override; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-12 22:49:04 -07:00
										 |  |  |     static JS::Value load_file(JS::Interpreter&); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | private: | 
					
						
							|  |  |  |     virtual const char* class_name() const override { return "ReplObject"; } | 
					
						
							|  |  |  |     static JS::Value exit_interpreter(JS::Interpreter&); | 
					
						
							|  |  |  |     static JS::Value repl_help(JS::Interpreter&); | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  |     static JS::Value save_to_file(JS::Interpreter&); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  | static bool s_dump_ast = false; | 
					
						
							|  |  |  | static bool s_print_last_result = false; | 
					
						
							| 
									
										
										
										
											2020-05-26 15:04:39 +04:30
										 |  |  | static RefPtr<Line::Editor> s_editor; | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  | static int s_repl_line_level = 0; | 
					
						
							| 
									
										
										
										
											2020-05-25 16:57:07 +04:30
										 |  |  | static bool s_fail_repl = false; | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  | static String prompt_for_level(int level) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static StringBuilder prompt_builder; | 
					
						
							|  |  |  |     prompt_builder.clear(); | 
					
						
							|  |  |  |     prompt_builder.append("> "); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (auto i = 0; i < level; ++i) | 
					
						
							|  |  |  |         prompt_builder.append("    "); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return prompt_builder.build(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | String read_next_piece() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     StringBuilder piece; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     do { | 
					
						
							| 
									
										
										
										
											2020-05-25 16:57:07 +04:30
										 |  |  |         auto line_result = s_editor->get_line(prompt_for_level(s_repl_line_level)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (line_result.is_error()) { | 
					
						
							|  |  |  |             s_fail_repl = true; | 
					
						
							|  |  |  |             return ""; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto& line = line_result.value(); | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |         s_editor->add_to_history(line); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         piece.append(line); | 
					
						
							|  |  |  |         auto lexer = JS::Lexer(line); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (JS::Token token = lexer.next(); token.type() != JS::TokenType::Eof; token = lexer.next()) { | 
					
						
							|  |  |  |             switch (token.type()) { | 
					
						
							|  |  |  |             case JS::TokenType::BracketOpen: | 
					
						
							|  |  |  |             case JS::TokenType::CurlyOpen: | 
					
						
							|  |  |  |             case JS::TokenType::ParenOpen: | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |                 s_repl_line_level++; | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |                 break; | 
					
						
							|  |  |  |             case JS::TokenType::BracketClose: | 
					
						
							|  |  |  |             case JS::TokenType::CurlyClose: | 
					
						
							|  |  |  |             case JS::TokenType::ParenClose: | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |                 s_repl_line_level--; | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |                 break; | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |     } while (s_repl_line_level > 0); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return piece.to_string(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  | static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  | static void print_array(const JS::Array& array, HashTable<JS::Object*>& seen_objects) | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     fputs("[ ", stdout); | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  |     for (size_t i = 0; i < array.elements().size(); ++i) { | 
					
						
							|  |  |  |         print_value(array.elements()[i], seen_objects); | 
					
						
							|  |  |  |         if (i != array.elements().size() - 1) | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  |             fputs(", ", stdout); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     fputs(" ]", stdout); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  | static void print_object(const JS::Object& object, HashTable<JS::Object*>& seen_objects) | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     fputs("{ ", stdout); | 
					
						
							| 
									
										
										
										
											2020-04-06 16:53:02 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (size_t i = 0; i < object.elements().size(); ++i) { | 
					
						
							| 
									
										
										
										
											2020-04-06 20:24:45 +02:00
										 |  |  |         if (object.elements()[i].is_empty()) | 
					
						
							|  |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2020-04-06 16:53:02 +02:00
										 |  |  |         printf("\"\033[33;1m%zu\033[0m\": ", i); | 
					
						
							|  |  |  |         print_value(object.elements()[i], seen_objects); | 
					
						
							|  |  |  |         if (i != object.elements().size() - 1) | 
					
						
							|  |  |  |             fputs(", ", stdout); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!object.elements().is_empty() && object.shape().property_count()) | 
					
						
							|  |  |  |         fputs(", ", stdout); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  |     size_t index = 0; | 
					
						
							| 
									
										
										
										
											2020-04-28 19:19:31 -07:00
										 |  |  |     for (auto& it : object.shape().property_table_ordered()) { | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  |         printf("\"\033[33;1m%s\033[0m\": ", it.key.characters()); | 
					
						
							| 
									
										
										
										
											2020-04-02 19:32:21 +02:00
										 |  |  |         print_value(object.get_direct(it.value.offset), seen_objects); | 
					
						
							| 
									
										
										
										
											2020-04-06 16:53:02 +02:00
										 |  |  |         if (index != object.shape().property_count() - 1) | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  |             fputs(", ", stdout); | 
					
						
							|  |  |  |         ++index; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     fputs(" }", stdout); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  | static void print_function(const JS::Object& function, HashTable<JS::Object*>&) | 
					
						
							| 
									
										
										
										
											2020-03-26 14:54:43 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  |     printf("\033[34;1m[%s]\033[0m", function.class_name()); | 
					
						
							| 
									
										
										
										
											2020-03-26 14:54:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  | static void print_date(const JS::Object& date, HashTable<JS::Object*>&) | 
					
						
							| 
									
										
										
										
											2020-03-31 18:43:45 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  |     printf("\033[34;1mDate %s\033[0m", static_cast<const JS::Date&>(date).string().characters()); | 
					
						
							| 
									
										
										
										
											2020-03-31 18:43:45 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 09:54:15 +02:00
										 |  |  | static void print_error(const JS::Object& object, HashTable<JS::Object*>&) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto& error = static_cast<const JS::Error&>(object); | 
					
						
							| 
									
										
										
										
											2020-04-02 14:18:19 +01:00
										 |  |  |     printf("\033[34;1m[%s]\033[0m", error.name().characters()); | 
					
						
							|  |  |  |     if (!error.message().is_empty()) | 
					
						
							|  |  |  |         printf(": %s", error.message().characters()); | 
					
						
							| 
									
										
										
										
											2020-04-02 09:54:15 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  | void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-04-08 17:38:09 +01:00
										 |  |  |     if (value.is_empty()) { | 
					
						
							|  |  |  |         printf("\033[34;1m<empty>\033[0m"); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  |     if (value.is_object()) { | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  |         if (seen_objects.contains(&value.as_object())) { | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  |             // FIXME: Maybe we should only do this for circular references,
 | 
					
						
							|  |  |  |             //        not for all reoccurring objects.
 | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  |             printf("<already printed Object %p>", &value.as_object()); | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  |         seen_objects.set(&value.as_object()); | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (value.is_array()) | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  |         return print_array(static_cast<const JS::Array&>(value.as_object()), seen_objects); | 
					
						
							| 
									
										
										
										
											2020-04-01 18:53:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-31 18:43:45 +01:00
										 |  |  |     if (value.is_object()) { | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  |         auto& object = value.as_object(); | 
					
						
							|  |  |  |         if (object.is_function()) | 
					
						
							| 
									
										
										
										
											2020-03-31 18:43:45 +01:00
										 |  |  |             return print_function(object, seen_objects); | 
					
						
							| 
									
										
										
										
											2020-04-01 22:18:47 +02:00
										 |  |  |         if (object.is_date()) | 
					
						
							| 
									
										
										
										
											2020-03-31 18:43:45 +01:00
										 |  |  |             return print_date(object, seen_objects); | 
					
						
							| 
									
										
										
										
											2020-04-02 09:54:15 +02:00
										 |  |  |         if (object.is_error()) | 
					
						
							|  |  |  |             return print_error(object, seen_objects); | 
					
						
							| 
									
										
										
										
											2020-03-31 18:43:45 +01:00
										 |  |  |         return print_object(object, seen_objects); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (value.is_string()) | 
					
						
							| 
									
										
										
										
											2020-04-15 10:11:48 +02:00
										 |  |  |         printf("\033[32;1m"); | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  |     else if (value.is_number()) | 
					
						
							|  |  |  |         printf("\033[35;1m"); | 
					
						
							|  |  |  |     else if (value.is_boolean()) | 
					
						
							| 
									
										
										
										
											2020-04-15 10:11:48 +02:00
										 |  |  |         printf("\033[33;1m"); | 
					
						
							| 
									
										
										
										
											2020-03-26 14:54:43 +01:00
										 |  |  |     else if (value.is_null()) | 
					
						
							|  |  |  |         printf("\033[33;1m"); | 
					
						
							|  |  |  |     else if (value.is_undefined()) | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  |         printf("\033[34;1m"); | 
					
						
							|  |  |  |     if (value.is_string()) | 
					
						
							|  |  |  |         putchar('"'); | 
					
						
							| 
									
										
										
										
											2020-05-15 13:39:24 +02:00
										 |  |  |     printf("%s", value.to_string_without_side_effects().characters()); | 
					
						
							| 
									
										
										
										
											2020-03-26 12:26:11 +01:00
										 |  |  |     if (value.is_string()) | 
					
						
							|  |  |  |         putchar('"'); | 
					
						
							|  |  |  |     printf("\033[0m"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void print(JS::Value value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     HashTable<JS::Object*> seen_objects; | 
					
						
							|  |  |  |     print_value(value, seen_objects); | 
					
						
							|  |  |  |     putchar('\n'); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | bool file_has_shebang(AK::ByteBuffer file_contents) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (file_contents.size() >= 2 && file_contents[0] == '#' && file_contents[1] == '!') | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | StringView strip_shebang(AK::ByteBuffer file_contents) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     size_t i = 0; | 
					
						
							|  |  |  |     for (i = 2; i < file_contents.size(); ++i) { | 
					
						
							|  |  |  |         if (file_contents[i] == '\n') | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return StringView((const char*)file_contents.data() + i, file_contents.size() - i); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  | bool write_to_file(const StringView& path) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int fd = open_with_path_length(path.characters_without_null_termination(), path.length(), O_WRONLY | O_CREAT | O_TRUNC, 0666); | 
					
						
							|  |  |  |     for (size_t i = 0; i < repl_statements.size(); i++) { | 
					
						
							|  |  |  |         auto line = repl_statements[i]; | 
					
						
							|  |  |  |         if (line.length() && i != repl_statements.size() - 1) { | 
					
						
							|  |  |  |             ssize_t nwritten = write(fd, line.characters(), line.length()); | 
					
						
							|  |  |  |             if (nwritten < 0) { | 
					
						
							|  |  |  |                 close(fd); | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (i != repl_statements.size() - 1) { | 
					
						
							|  |  |  |             char ch = '\n'; | 
					
						
							|  |  |  |             ssize_t nwritten = write(fd, &ch, 1); | 
					
						
							|  |  |  |             if (nwritten != 1) { | 
					
						
							|  |  |  |                 perror("write"); | 
					
						
							|  |  |  |                 close(fd); | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     close(fd); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  | bool parse_and_run(JS::Interpreter& interpreter, const StringView& source) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto parser = JS::Parser(JS::Lexer(source)); | 
					
						
							|  |  |  |     auto program = parser.parse_program(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (s_dump_ast) | 
					
						
							|  |  |  |         program->dump(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (parser.has_errors()) { | 
					
						
							|  |  |  |         auto error = parser.errors()[0]; | 
					
						
							| 
									
										
										
										
											2020-05-26 13:00:36 +01:00
										 |  |  |         auto hint = error.source_location_hint(source); | 
					
						
							|  |  |  |         if (!hint.is_empty()) | 
					
						
							|  |  |  |             printf("%s\n", hint.characters()); | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  |         interpreter.throw_exception<JS::SyntaxError>(error.to_string()); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         interpreter.run(*program); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (interpreter.exception()) { | 
					
						
							|  |  |  |         printf("Uncaught exception: "); | 
					
						
							|  |  |  |         print(interpreter.exception()->value()); | 
					
						
							|  |  |  |         interpreter.clear_exception(); | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (s_print_last_result) | 
					
						
							|  |  |  |         print(interpreter.last_value()); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | ReplObject::ReplObject() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-04-18 13:18:06 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ReplObject::initialize() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     GlobalObject::initialize(); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |     put_native_function("exit", exit_interpreter); | 
					
						
							|  |  |  |     put_native_function("help", repl_help); | 
					
						
							| 
									
										
										
										
											2020-04-04 14:13:53 +01:00
										 |  |  |     put_native_function("load", load_file, 1); | 
					
						
							|  |  |  |     put_native_function("save", save_to_file, 1); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ReplObject::~ReplObject() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-13 14:04:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  | JS::Value ReplObject::save_to_file(JS::Interpreter& interpreter) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!interpreter.argument_count()) | 
					
						
							|  |  |  |         return JS::Value(false); | 
					
						
							| 
									
										
										
										
											2020-05-15 13:39:24 +02:00
										 |  |  |     String save_path = interpreter.argument(0).to_string_without_side_effects(); | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  |     StringView path = StringView(save_path.characters()); | 
					
						
							|  |  |  |     if (write_to_file(path)) { | 
					
						
							|  |  |  |         return JS::Value(true); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return JS::Value(false); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-13 14:04:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | JS::Value ReplObject::exit_interpreter(JS::Interpreter& interpreter) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-04-01 22:38:59 +02:00
										 |  |  |     if (!interpreter.argument_count()) | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |         exit(0); | 
					
						
							| 
									
										
										
										
											2020-05-18 00:28:00 +01:00
										 |  |  |     auto exit_code = interpreter.argument(0).to_number(interpreter); | 
					
						
							|  |  |  |     if (interpreter.exception()) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     exit(exit_code.as_double()); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-13 14:04:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 21:08:54 +01:00
										 |  |  | JS::Value ReplObject::repl_help(JS::Interpreter&) | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-04-13 21:08:54 +01:00
										 |  |  |     printf("REPL commands:\n"); | 
					
						
							|  |  |  |     printf("    exit(code): exit the REPL with specified code. Defaults to 0.\n"); | 
					
						
							|  |  |  |     printf("    help(): display this menu\n"); | 
					
						
							|  |  |  |     printf("    load(files): Accepts file names as params to load into running session. For example load(\"js/1.js\", \"js/2.js\", \"js/3.js\")\n"); | 
					
						
							|  |  |  |     return JS::js_undefined(); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | JS::Value ReplObject::load_file(JS::Interpreter& interpreter) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-04-01 22:38:59 +02:00
										 |  |  |     if (!interpreter.argument_count()) | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |         return JS::Value(false); | 
					
						
							| 
									
										
										
										
											2020-04-01 22:38:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (auto& file : interpreter.call_frame().arguments) { | 
					
						
							| 
									
										
										
										
											2020-04-29 12:35:39 +02:00
										 |  |  |         String file_name = file.as_string().string(); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |         auto js_file = Core::File::construct(file_name); | 
					
						
							|  |  |  |         if (!js_file->open(Core::IODevice::ReadOnly)) { | 
					
						
							|  |  |  |             fprintf(stderr, "Failed to open %s: %s\n", file_name.characters(), js_file->error_string()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         auto file_contents = js_file->read_all(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         StringView source; | 
					
						
							|  |  |  |         if (file_has_shebang(file_contents)) { | 
					
						
							|  |  |  |             source = strip_shebang(file_contents); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             source = file_contents; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  |         parse_and_run(interpreter, source); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |     } | 
					
						
							|  |  |  |     return JS::Value(true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | void repl(JS::Interpreter& interpreter) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-05-25 16:57:07 +04:30
										 |  |  |     while (!s_fail_repl) { | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |         String piece = read_next_piece(); | 
					
						
							|  |  |  |         if (piece.is_empty()) | 
					
						
							| 
									
										
										
										
											2020-03-31 13:29:42 +02:00
										 |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  |         repl_statements.append(piece); | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  |         parse_and_run(interpreter, piece); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-09 21:58:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-12 22:49:04 -07:00
										 |  |  | void enable_test_mode(JS::Interpreter& interpreter) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     interpreter.global_object().put_native_function("load", ReplObject::load_file); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 17:59:16 +01:00
										 |  |  | static Function<void()> interrupt_interpreter; | 
					
						
							|  |  |  | void sigint_handler() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     interrupt_interpreter(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  | class ReplConsoleClient final : public JS::ConsoleClient { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     ReplConsoleClient(JS::Console& console) | 
					
						
							|  |  |  |         : ConsoleClient(console) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual JS::Value log() override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         puts(interpreter().join_arguments().characters()); | 
					
						
							|  |  |  |         return JS::js_undefined(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     virtual JS::Value info() override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         printf("(i) %s\n", interpreter().join_arguments().characters()); | 
					
						
							|  |  |  |         return JS::js_undefined(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     virtual JS::Value debug() override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         printf("\033[36;1m"); | 
					
						
							|  |  |  |         puts(interpreter().join_arguments().characters()); | 
					
						
							|  |  |  |         printf("\033[0m"); | 
					
						
							|  |  |  |         return JS::js_undefined(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     virtual JS::Value warn() override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         printf("\033[33;1m"); | 
					
						
							|  |  |  |         puts(interpreter().join_arguments().characters()); | 
					
						
							|  |  |  |         printf("\033[0m"); | 
					
						
							|  |  |  |         return JS::js_undefined(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     virtual JS::Value error() override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         printf("\033[31;1m"); | 
					
						
							|  |  |  |         puts(interpreter().join_arguments().characters()); | 
					
						
							|  |  |  |         printf("\033[0m"); | 
					
						
							|  |  |  |         return JS::js_undefined(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     virtual JS::Value clear() override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         printf("\033[3J\033[H\033[2J"); | 
					
						
							|  |  |  |         fflush(stdout); | 
					
						
							|  |  |  |         return JS::js_undefined(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     virtual JS::Value trace() override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         puts(interpreter().join_arguments().characters()); | 
					
						
							|  |  |  |         auto trace = interpreter().get_trace(); | 
					
						
							| 
									
										
										
										
											2020-05-05 11:48:57 +02:00
										 |  |  |         for (auto& function_name : trace) { | 
					
						
							|  |  |  |             if (function_name.is_empty()) | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  |                 function_name = "<anonymous>"; | 
					
						
							|  |  |  |             printf(" -> %s\n", function_name.characters()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return JS::js_undefined(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     virtual JS::Value count() override | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-05-15 13:39:24 +02:00
										 |  |  |         auto label = interpreter().argument_count() ? interpreter().argument(0).to_string_without_side_effects() : "default"; | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  |         auto counter_value = m_console.counter_increment(label); | 
					
						
							|  |  |  |         printf("%s: %u\n", label.characters(), counter_value); | 
					
						
							|  |  |  |         return JS::js_undefined(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     virtual JS::Value count_reset() override | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-05-15 13:39:24 +02:00
										 |  |  |         auto label = interpreter().argument_count() ? interpreter().argument(0).to_string_without_side_effects() : "default"; | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  |         if (m_console.counter_reset(label)) { | 
					
						
							|  |  |  |             printf("%s: 0\n", label.characters()); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             printf("\033[33;1m"); | 
					
						
							|  |  |  |             printf("\"%s\" doesn't have a count\n", label.characters()); | 
					
						
							|  |  |  |             printf("\033[0m"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return JS::js_undefined(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-12 10:51:41 +01:00
										 |  |  | int main(int argc, char** argv) | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-03-16 19:18:46 +01:00
										 |  |  |     bool gc_on_every_allocation = false; | 
					
						
							| 
									
										
										
										
											2020-04-30 22:37:50 +02:00
										 |  |  |     bool disable_syntax_highlight = false; | 
					
						
							| 
									
										
										
										
											2020-04-05 05:49:46 -07:00
										 |  |  |     bool test_mode = false; | 
					
						
							| 
									
										
										
										
											2020-03-12 10:51:41 +01:00
										 |  |  |     const char* script_path = nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Core::ArgsParser args_parser; | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |     args_parser.add_option(s_dump_ast, "Dump the AST", "dump-ast", 'A'); | 
					
						
							|  |  |  |     args_parser.add_option(s_print_last_result, "Print last result", "print-last-result", 'l'); | 
					
						
							| 
									
										
										
										
											2020-03-16 19:18:46 +01:00
										 |  |  |     args_parser.add_option(gc_on_every_allocation, "GC on every allocation", "gc-on-every-allocation", 'g'); | 
					
						
							| 
									
										
										
										
											2020-04-30 22:37:50 +02:00
										 |  |  |     args_parser.add_option(disable_syntax_highlight, "Disable live syntax highlighting", "no-syntax-highlight", 's'); | 
					
						
							| 
									
										
										
										
											2020-04-12 22:49:04 -07:00
										 |  |  |     args_parser.add_option(test_mode, "Run the interpreter with added functionality for the test harness", "test-mode", 't'); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |     args_parser.add_positional_argument(script_path, "Path to script file", "script", Core::ArgsParser::Required::No); | 
					
						
							| 
									
										
										
										
											2020-03-12 10:51:41 +01:00
										 |  |  |     args_parser.parse(argc, argv); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-30 22:37:50 +02:00
										 |  |  |     bool syntax_highlight = !disable_syntax_highlight; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 17:59:16 +01:00
										 |  |  |     OwnPtr<JS::Interpreter> interpreter; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     interrupt_interpreter = [&] { | 
					
						
							|  |  |  |         auto error = JS::Error::create(interpreter->global_object(), "Error", "Received SIGINT"); | 
					
						
							|  |  |  |         interpreter->throw_exception(error); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |     if (script_path == nullptr) { | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  |         s_print_last_result = true; | 
					
						
							| 
									
										
										
										
											2020-04-24 17:59:16 +01:00
										 |  |  |         interpreter = JS::Interpreter::create<ReplObject>(); | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  |         ReplConsoleClient console_client(interpreter->console()); | 
					
						
							|  |  |  |         interpreter->console().set_client(console_client); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |         interpreter->heap().set_should_collect_on_every_allocation(gc_on_every_allocation); | 
					
						
							| 
									
										
										
										
											2020-04-12 22:49:04 -07:00
										 |  |  |         if (test_mode) | 
					
						
							|  |  |  |             enable_test_mode(*interpreter); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-26 15:04:39 +04:30
										 |  |  |         s_editor = Line::Editor::construct(); | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:10 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |         signal(SIGINT, [](int) { | 
					
						
							| 
									
										
										
										
											2020-04-29 00:31:22 +04:30
										 |  |  |             if (!s_editor->is_editing()) | 
					
						
							|  |  |  |                 sigint_handler(); | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |             s_editor->interrupted(); | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:10 +04:30
										 |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         signal(SIGWINCH, [](int) { | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |             s_editor->resized(); | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:10 +04:30
										 |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |         s_editor->on_display_refresh = [syntax_highlight](Line::Editor& editor) { | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |             auto stylize = [&](Line::Span span, Line::Style styles) { | 
					
						
							|  |  |  |                 if (syntax_highlight) | 
					
						
							|  |  |  |                     editor.stylize(span, styles); | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             editor.strip_styles(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |             size_t open_indents = s_repl_line_level; | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-26 13:00:30 -04:00
										 |  |  |             auto line = editor.line(); | 
					
						
							|  |  |  |             JS::Lexer lexer(line); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |             bool indenters_starting_line = true; | 
					
						
							|  |  |  |             for (JS::Token token = lexer.next(); token.type() != JS::TokenType::Eof; token = lexer.next()) { | 
					
						
							|  |  |  |                 auto length = token.value().length(); | 
					
						
							| 
									
										
										
										
											2020-05-26 13:00:30 -04:00
										 |  |  |                 auto start = token.line_column() - 1; | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 auto end = start + length; | 
					
						
							|  |  |  |                 if (indenters_starting_line) { | 
					
						
							|  |  |  |                     if (token.type() != JS::TokenType::ParenClose && token.type() != JS::TokenType::BracketClose && token.type() != JS::TokenType::CurlyClose) { | 
					
						
							|  |  |  |                         indenters_starting_line = false; | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         --open_indents; | 
					
						
							| 
									
										
										
										
											2020-04-05 18:33:12 +04:30
										 |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |                 switch (token.type()) { | 
					
						
							|  |  |  |                 case JS::TokenType::Invalid: | 
					
						
							|  |  |  |                 case JS::TokenType::Eof: | 
					
						
							| 
									
										
										
										
											2020-05-10 12:27:36 +04:30
										 |  |  |                     stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Red), Line::Style::Underline }); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							|  |  |  |                 case JS::TokenType::NumericLiteral: | 
					
						
							| 
									
										
										
										
											2020-05-10 12:27:36 +04:30
										 |  |  |                     stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Magenta) }); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							|  |  |  |                 case JS::TokenType::StringLiteral: | 
					
						
							| 
									
										
											  
											
												LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
    > `foo
    > bar`
    'foo
    bar'
											
										 
											2020-05-03 15:41:14 -07:00
										 |  |  |                 case JS::TokenType::TemplateLiteralStart: | 
					
						
							|  |  |  |                 case JS::TokenType::TemplateLiteralEnd: | 
					
						
							|  |  |  |                 case JS::TokenType::TemplateLiteralString: | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 case JS::TokenType::RegexLiteral: | 
					
						
							|  |  |  |                 case JS::TokenType::UnterminatedStringLiteral: | 
					
						
							| 
									
										
										
										
											2020-05-10 12:27:36 +04:30
										 |  |  |                     stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Green), Line::Style::Bold }); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							|  |  |  |                 case JS::TokenType::BracketClose: | 
					
						
							|  |  |  |                 case JS::TokenType::BracketOpen: | 
					
						
							|  |  |  |                 case JS::TokenType::Comma: | 
					
						
							|  |  |  |                 case JS::TokenType::CurlyClose: | 
					
						
							|  |  |  |                 case JS::TokenType::CurlyOpen: | 
					
						
							|  |  |  |                 case JS::TokenType::ParenClose: | 
					
						
							|  |  |  |                 case JS::TokenType::ParenOpen: | 
					
						
							|  |  |  |                 case JS::TokenType::Semicolon: | 
					
						
							|  |  |  |                 case JS::TokenType::Period: | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case JS::TokenType::Ampersand: | 
					
						
							|  |  |  |                 case JS::TokenType::AmpersandEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::Asterisk: | 
					
						
							| 
									
										
										
										
											2020-05-04 23:03:35 +01:00
										 |  |  |                 case JS::TokenType::DoubleAsteriskEquals: | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 case JS::TokenType::AsteriskEquals: | 
					
						
							| 
									
										
										
										
											2020-05-04 22:34:45 +01:00
										 |  |  |                 case JS::TokenType::Caret: | 
					
						
							|  |  |  |                 case JS::TokenType::CaretEquals: | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 case JS::TokenType::DoubleAmpersand: | 
					
						
							|  |  |  |                 case JS::TokenType::DoubleAsterisk: | 
					
						
							|  |  |  |                 case JS::TokenType::DoublePipe: | 
					
						
							|  |  |  |                 case JS::TokenType::DoubleQuestionMark: | 
					
						
							|  |  |  |                 case JS::TokenType::Equals: | 
					
						
							|  |  |  |                 case JS::TokenType::EqualsEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::EqualsEqualsEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::ExclamationMark: | 
					
						
							|  |  |  |                 case JS::TokenType::ExclamationMarkEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::ExclamationMarkEqualsEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::GreaterThan: | 
					
						
							|  |  |  |                 case JS::TokenType::GreaterThanEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::LessThan: | 
					
						
							|  |  |  |                 case JS::TokenType::LessThanEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::Minus: | 
					
						
							|  |  |  |                 case JS::TokenType::MinusEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::MinusMinus: | 
					
						
							|  |  |  |                 case JS::TokenType::Percent: | 
					
						
							|  |  |  |                 case JS::TokenType::PercentEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::Pipe: | 
					
						
							|  |  |  |                 case JS::TokenType::PipeEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::Plus: | 
					
						
							|  |  |  |                 case JS::TokenType::PlusEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::PlusPlus: | 
					
						
							|  |  |  |                 case JS::TokenType::QuestionMark: | 
					
						
							|  |  |  |                 case JS::TokenType::QuestionMarkPeriod: | 
					
						
							|  |  |  |                 case JS::TokenType::ShiftLeft: | 
					
						
							|  |  |  |                 case JS::TokenType::ShiftLeftEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::ShiftRight: | 
					
						
							|  |  |  |                 case JS::TokenType::ShiftRightEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::Slash: | 
					
						
							|  |  |  |                 case JS::TokenType::SlashEquals: | 
					
						
							|  |  |  |                 case JS::TokenType::Tilde: | 
					
						
							|  |  |  |                 case JS::TokenType::UnsignedShiftRight: | 
					
						
							|  |  |  |                 case JS::TokenType::UnsignedShiftRightEquals: | 
					
						
							|  |  |  |                     break; | 
					
						
							| 
									
										
										
										
											2020-04-16 16:37:12 +02:00
										 |  |  |                 case JS::TokenType::BoolLiteral: | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 case JS::TokenType::NullLiteral: | 
					
						
							| 
									
										
										
										
											2020-05-10 12:27:36 +04:30
										 |  |  |                     stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Yellow), Line::Style::Bold }); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							|  |  |  |                 case JS::TokenType::Class: | 
					
						
							|  |  |  |                 case JS::TokenType::Const: | 
					
						
							| 
									
										
										
										
											2020-04-30 17:26:27 +01:00
										 |  |  |                 case JS::TokenType::Debugger: | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 case JS::TokenType::Delete: | 
					
						
							|  |  |  |                 case JS::TokenType::Function: | 
					
						
							|  |  |  |                 case JS::TokenType::In: | 
					
						
							|  |  |  |                 case JS::TokenType::Instanceof: | 
					
						
							|  |  |  |                 case JS::TokenType::Interface: | 
					
						
							|  |  |  |                 case JS::TokenType::Let: | 
					
						
							|  |  |  |                 case JS::TokenType::New: | 
					
						
							| 
									
										
											  
											
												LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
    > `foo
    > bar`
    'foo
    bar'
											
										 
											2020-05-03 15:41:14 -07:00
										 |  |  |                 case JS::TokenType::TemplateLiteralExprStart: | 
					
						
							|  |  |  |                 case JS::TokenType::TemplateLiteralExprEnd: | 
					
						
							| 
									
										
										
										
											2020-04-11 14:18:48 +01:00
										 |  |  |                 case JS::TokenType::Throw: | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 case JS::TokenType::Typeof: | 
					
						
							|  |  |  |                 case JS::TokenType::Var: | 
					
						
							|  |  |  |                 case JS::TokenType::Void: | 
					
						
							| 
									
										
										
										
											2020-05-10 12:27:36 +04:30
										 |  |  |                     stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Blue), Line::Style::Bold }); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							|  |  |  |                 case JS::TokenType::Await: | 
					
						
							| 
									
										
										
										
											2020-04-11 14:18:48 +01:00
										 |  |  |                 case JS::TokenType::Case: | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 case JS::TokenType::Catch: | 
					
						
							|  |  |  |                 case JS::TokenType::Do: | 
					
						
							|  |  |  |                 case JS::TokenType::Else: | 
					
						
							|  |  |  |                 case JS::TokenType::Finally: | 
					
						
							|  |  |  |                 case JS::TokenType::For: | 
					
						
							|  |  |  |                 case JS::TokenType::If: | 
					
						
							|  |  |  |                 case JS::TokenType::Return: | 
					
						
							| 
									
										
										
										
											2020-04-11 14:18:48 +01:00
										 |  |  |                 case JS::TokenType::Switch: | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 case JS::TokenType::Try: | 
					
						
							|  |  |  |                 case JS::TokenType::While: | 
					
						
							|  |  |  |                 case JS::TokenType::Yield: | 
					
						
							| 
									
										
										
										
											2020-05-10 12:27:36 +04:30
										 |  |  |                     stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Cyan), Line::Style::Italic }); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							|  |  |  |                 case JS::TokenType::Identifier: | 
					
						
							| 
									
										
										
										
											2020-05-10 12:27:36 +04:30
										 |  |  |                     stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::White), Line::Style::Bold }); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 default: | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             editor.set_prompt(prompt_for_level(open_indents)); | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |         auto complete = [&interpreter](const Line::Editor& editor) -> Vector<Line::CompletionSuggestion> { | 
					
						
							|  |  |  |             auto line = editor.line(editor.cursor()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             JS::Lexer lexer { line }; | 
					
						
							|  |  |  |             enum { | 
					
						
							|  |  |  |                 Initial, | 
					
						
							|  |  |  |                 CompleteVariable, | 
					
						
							|  |  |  |                 CompleteNullProperty, | 
					
						
							|  |  |  |                 CompleteProperty, | 
					
						
							|  |  |  |             } mode { Initial }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             StringView variable_name; | 
					
						
							|  |  |  |             StringView property_name; | 
					
						
							| 
									
										
										
										
											2020-04-11 19:02:15 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |             // we're only going to complete either
 | 
					
						
							|  |  |  |             //    - <N>
 | 
					
						
							|  |  |  |             //        where N is part of the name of a variable
 | 
					
						
							|  |  |  |             //    - <N>.<P>
 | 
					
						
							|  |  |  |             //        where N is the complete name of a variable and
 | 
					
						
							|  |  |  |             //        P is part of the name of one of its properties
 | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |             auto js_token = lexer.next(); | 
					
						
							|  |  |  |             for (; js_token.type() != JS::TokenType::Eof; js_token = lexer.next()) { | 
					
						
							|  |  |  |                 switch (mode) { | 
					
						
							|  |  |  |                 case CompleteVariable: | 
					
						
							|  |  |  |                     switch (js_token.type()) { | 
					
						
							|  |  |  |                     case JS::TokenType::Period: | 
					
						
							|  |  |  |                         // ...<name> <dot>
 | 
					
						
							|  |  |  |                         mode = CompleteNullProperty; | 
					
						
							|  |  |  |                         break; | 
					
						
							|  |  |  |                     default: | 
					
						
							|  |  |  |                         // not a dot, reset back to initial
 | 
					
						
							|  |  |  |                         mode = Initial; | 
					
						
							|  |  |  |                         break; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case CompleteNullProperty: | 
					
						
							|  |  |  |                     if (js_token.is_identifier_name()) { | 
					
						
							|  |  |  |                         // ...<name> <dot> <name>
 | 
					
						
							|  |  |  |                         mode = CompleteProperty; | 
					
						
							|  |  |  |                         property_name = js_token.value(); | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         mode = Initial; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case CompleteProperty: | 
					
						
							|  |  |  |                     // something came after the property access, reset to initial
 | 
					
						
							|  |  |  |                 case Initial: | 
					
						
							|  |  |  |                     if (js_token.is_identifier_name()) { | 
					
						
							|  |  |  |                         // ...<name>...
 | 
					
						
							|  |  |  |                         mode = CompleteVariable; | 
					
						
							|  |  |  |                         variable_name = js_token.value(); | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         mode = Initial; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             bool last_token_has_trivia = js_token.trivia().length() > 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (mode == CompleteNullProperty) { | 
					
						
							|  |  |  |                 mode = CompleteProperty; | 
					
						
							|  |  |  |                 property_name = ""; | 
					
						
							|  |  |  |                 last_token_has_trivia = false; // <name> <dot> [tab] is sensible to complete.
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (mode == Initial || last_token_has_trivia) | 
					
						
							|  |  |  |                 return {}; // we do not know how to complete this
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-19 18:32:28 +04:30
										 |  |  |             Vector<Line::CompletionSuggestion> results; | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |             Function<void(const JS::Shape&, const StringView&)> list_all_properties = [&results, &list_all_properties](const JS::Shape& shape, auto& property_pattern) { | 
					
						
							|  |  |  |                 for (const auto& descriptor : shape.property_table()) { | 
					
						
							| 
									
										
										
										
											2020-05-02 16:01:09 +01:00
										 |  |  |                     if (descriptor.key.view().starts_with(property_pattern)) { | 
					
						
							| 
									
										
										
										
											2020-05-22 05:43:22 +04:30
										 |  |  |                         Line::CompletionSuggestion completion { descriptor.key, Line::CompletionSuggestion::ForSearch }; | 
					
						
							| 
									
										
										
										
											2020-05-02 16:01:09 +01:00
										 |  |  |                         if (!results.contains_slow(completion)) { // hide duplicates
 | 
					
						
							| 
									
										
										
										
											2020-05-22 05:43:22 +04:30
										 |  |  |                             results.append({ descriptor.key }); | 
					
						
							| 
									
										
										
										
											2020-04-11 19:02:15 +04:30
										 |  |  |                         } | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (const auto* prototype = shape.prototype()) { | 
					
						
							|  |  |  |                     list_all_properties(prototype->shape(), property_pattern); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |             switch (mode) { | 
					
						
							|  |  |  |             case CompleteProperty: { | 
					
						
							|  |  |  |                 auto maybe_variable = interpreter->get_variable(variable_name); | 
					
						
							| 
									
										
										
										
											2020-04-25 18:43:34 +02:00
										 |  |  |                 if (maybe_variable.is_empty()) { | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |                     maybe_variable = interpreter->global_object().get(variable_name); | 
					
						
							| 
									
										
										
										
											2020-04-25 18:43:34 +02:00
										 |  |  |                     if (maybe_variable.is_empty()) | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |                         break; | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-25 18:43:34 +02:00
										 |  |  |                 auto variable = maybe_variable; | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |                 if (!variable.is_object()) | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |                     break; | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-17 21:38:47 +01:00
										 |  |  |                 const auto* object = variable.to_object(*interpreter); | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |                 const auto& shape = object->shape(); | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |                 list_all_properties(shape, property_name); | 
					
						
							| 
									
										
										
										
											2020-04-11 19:02:15 +04:30
										 |  |  |                 if (results.size()) | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |                     editor.suggest(property_name.length()); | 
					
						
							|  |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |             case CompleteVariable: { | 
					
						
							|  |  |  |                 const auto& variable = interpreter->global_object(); | 
					
						
							|  |  |  |                 list_all_properties(variable.shape(), variable_name); | 
					
						
							|  |  |  |                 if (results.size()) | 
					
						
							|  |  |  |                     editor.suggest(variable_name.length()); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 ASSERT_NOT_REACHED(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |             return results; | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |         s_editor->on_tab_complete = move(complete); | 
					
						
							| 
									
										
										
										
											2020-04-01 21:04:51 +02:00
										 |  |  |         repl(*interpreter); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2020-04-24 17:59:16 +01:00
										 |  |  |         interpreter = JS::Interpreter::create<JS::GlobalObject>(); | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  |         ReplConsoleClient console_client(interpreter->console()); | 
					
						
							|  |  |  |         interpreter->console().set_client(console_client); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |         interpreter->heap().set_should_collect_on_every_allocation(gc_on_every_allocation); | 
					
						
							| 
									
										
										
										
											2020-04-12 22:49:04 -07:00
										 |  |  |         if (test_mode) | 
					
						
							|  |  |  |             enable_test_mode(*interpreter); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 17:59:16 +01:00
										 |  |  |         signal(SIGINT, [](int) { | 
					
						
							|  |  |  |             sigint_handler(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |         auto file = Core::File::construct(script_path); | 
					
						
							|  |  |  |         if (!file->open(Core::IODevice::ReadOnly)) { | 
					
						
							|  |  |  |             fprintf(stderr, "Failed to open %s: %s\n", script_path, file->error_string()); | 
					
						
							|  |  |  |             return 1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         auto file_contents = file->read_all(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         StringView source; | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |         if (file_has_shebang(file_contents)) { | 
					
						
							|  |  |  |             source = strip_shebang(file_contents); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             source = file_contents; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  |         if (!parse_and_run(*interpreter, source)) | 
					
						
							| 
									
										
										
										
											2020-04-13 02:05:21 +02:00
										 |  |  |             return 1; | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-08 19:59:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } |