mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 03:32:45 +00:00 
			
		
		
		
	LibJS: Implement const variable declarations
This also tightens the means of redeclaration of a variable by proxy, since we now have a way of knowing how a variable was initially declared, we can check if it was declared using `let` or `const` and not tolerate redeclaration like we did previously.
This commit is contained in:
		
							parent
							
								
									8557bc56f7
								
							
						
					
					
						commit
						ee5a49e2fe
					
				
					 5 changed files with 46 additions and 13 deletions
				
			
		|  | @ -456,6 +456,7 @@ Value UpdateExpression::execute(Interpreter& interpreter) const | |||
|         break; | ||||
|     case UpdateOp::Decrement: | ||||
|         interpreter.set_variable(name, Value(previous_value.as_double() - 1)); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     return previous_value; | ||||
|  | @ -520,19 +521,22 @@ Value VariableDeclaration::execute(Interpreter& interpreter) const | |||
| 
 | ||||
| void VariableDeclaration::dump(int indent) const | ||||
| { | ||||
|     const char* op_string = nullptr; | ||||
|     const char* declaration_type_string = nullptr; | ||||
|     switch (m_declaration_type) { | ||||
|     case DeclarationType::Let: | ||||
|         op_string = "Let"; | ||||
|         declaration_type_string = "Let"; | ||||
|         break; | ||||
|     case DeclarationType::Var: | ||||
|         op_string = "Var"; | ||||
|         declaration_type_string = "Var"; | ||||
|         break; | ||||
|     case DeclarationType::Const: | ||||
|         declaration_type_string = "Const"; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     ASTNode::dump(indent); | ||||
|     print_indent(indent + 1); | ||||
|     printf("%s\n", op_string); | ||||
|     printf("%s\n", declaration_type_string); | ||||
|     m_name->dump(indent + 1); | ||||
|     if (m_initializer) | ||||
|         m_initializer->dump(indent + 1); | ||||
|  |  | |||
|  | @ -470,6 +470,7 @@ private: | |||
| enum class DeclarationType { | ||||
|     Var, | ||||
|     Let, | ||||
|     Const, | ||||
| }; | ||||
| 
 | ||||
| class VariableDeclaration : public Statement { | ||||
|  |  | |||
|  | @ -57,7 +57,12 @@ Value Interpreter::run(const ScopeNode& scope_node, HashMap<String, Value> scope | |||
| 
 | ||||
| void Interpreter::enter_scope(const ScopeNode& scope_node, HashMap<String, Value> scope_variables, ScopeType scope_type) | ||||
| { | ||||
|     m_scope_stack.append({ scope_type, scope_node, move(scope_variables) }); | ||||
|     HashMap<String, Variable> scope_variables_with_declaration_type; | ||||
|     for (String name : scope_variables.keys()) { | ||||
|         scope_variables_with_declaration_type.set(name, { scope_variables.get(name).value(), DeclarationType::Var }); | ||||
|     } | ||||
| 
 | ||||
|     m_scope_stack.append({ scope_type, scope_node, move(scope_variables_with_declaration_type) }); | ||||
| } | ||||
| 
 | ||||
| void Interpreter::exit_scope(const ScopeNode& scope_node) | ||||
|  | @ -78,7 +83,10 @@ void Interpreter::declare_variable(String name, DeclarationType declaration_type | |||
|         for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) { | ||||
|             auto& scope = m_scope_stack.at(i); | ||||
|             if (scope.type == ScopeType::Function) { | ||||
|                 scope.variables.set(move(name), js_undefined()); | ||||
|                 if (scope.variables.get(name).has_value() && scope.variables.get(name).value().declaration_type != DeclarationType::Var) | ||||
|                     ASSERT_NOT_REACHED(); | ||||
| 
 | ||||
|                 scope.variables.set(move(name), { js_undefined(), declaration_type }); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | @ -86,7 +94,11 @@ void Interpreter::declare_variable(String name, DeclarationType declaration_type | |||
|         global_object().put(move(name), js_undefined()); | ||||
|         break; | ||||
|     case DeclarationType::Let: | ||||
|         m_scope_stack.last().variables.set(move(name), js_undefined()); | ||||
|     case DeclarationType::Const: | ||||
|         if (m_scope_stack.last().variables.get(name).has_value() && m_scope_stack.last().variables.get(name).value().declaration_type != DeclarationType::Var) | ||||
|             ASSERT_NOT_REACHED(); | ||||
| 
 | ||||
|         m_scope_stack.last().variables.set(move(name), { js_undefined(), declaration_type }); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | @ -95,8 +107,13 @@ void Interpreter::set_variable(String name, Value value) | |||
| { | ||||
|     for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) { | ||||
|         auto& scope = m_scope_stack.at(i); | ||||
|         if (scope.variables.contains(name)) { | ||||
|             scope.variables.set(move(name), move(value)); | ||||
| 
 | ||||
|         auto possible_match = scope.variables.get(name); | ||||
|         if (possible_match.has_value()) { | ||||
|             if (possible_match.value().declaration_type == DeclarationType::Const) | ||||
|                 ASSERT_NOT_REACHED(); | ||||
| 
 | ||||
|             scope.variables.set(move(name), { move(value), possible_match.value().declaration_type }); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | @ -110,7 +127,7 @@ Value Interpreter::get_variable(const String& name) | |||
|         auto& scope = m_scope_stack.at(i); | ||||
|         auto value = scope.variables.get(name); | ||||
|         if (value.has_value()) | ||||
|             return value.value(); | ||||
|             return value.value().value; | ||||
|     } | ||||
| 
 | ||||
|     return global_object().get(name); | ||||
|  | @ -122,8 +139,8 @@ void Interpreter::collect_roots(Badge<Heap>, HashTable<Cell*>& roots) | |||
| 
 | ||||
|     for (auto& scope : m_scope_stack) { | ||||
|         for (auto& it : scope.variables) { | ||||
|             if (it.value.is_cell()) | ||||
|                 roots.set(it.value.as_cell()); | ||||
|             if (it.value.value.is_cell()) | ||||
|                 roots.set(it.value.value.as_cell()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ | |||
| #include <AK/Vector.h> | ||||
| #include <LibJS/Forward.h> | ||||
| #include <LibJS/Heap.h> | ||||
| #include <LibJS/Value.h> | ||||
| 
 | ||||
| namespace JS { | ||||
| 
 | ||||
|  | @ -38,10 +39,15 @@ enum class ScopeType { | |||
|     Block, | ||||
| }; | ||||
| 
 | ||||
| struct Variable { | ||||
|     Value value; | ||||
|     DeclarationType declaration_type; | ||||
| }; | ||||
| 
 | ||||
| struct ScopeFrame { | ||||
|     ScopeType type; | ||||
|     const ScopeNode& scope_node; | ||||
|     HashMap<String, Value> variables; | ||||
|     HashMap<String, Variable> variables; | ||||
| }; | ||||
| 
 | ||||
| class Interpreter { | ||||
|  |  | |||
|  | @ -66,6 +66,7 @@ NonnullOwnPtr<Statement> Parser::parse_statement() | |||
|         return parse_return_statement(); | ||||
|     case TokenType::Var: | ||||
|     case TokenType::Let: | ||||
|     case TokenType::Const: | ||||
|         return parse_variable_declaration(); | ||||
|     case TokenType::For: | ||||
|         return parse_for_statement(); | ||||
|  | @ -261,6 +262,10 @@ NonnullOwnPtr<VariableDeclaration> Parser::parse_variable_declaration() | |||
|         declaration_type = DeclarationType::Let; | ||||
|         consume(TokenType::Let); | ||||
|         break; | ||||
|     case TokenType::Const: | ||||
|         declaration_type = DeclarationType::Const; | ||||
|         consume(TokenType::Const); | ||||
|         break; | ||||
|     default: | ||||
|         ASSERT_NOT_REACHED(); | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 0xtechnobabble
						0xtechnobabble