mirror of
https://github.com/RGBCube/serenity
synced 2025-05-19 20:45:08 +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;
|
break;
|
||||||
case UpdateOp::Decrement:
|
case UpdateOp::Decrement:
|
||||||
interpreter.set_variable(name, Value(previous_value.as_double() - 1));
|
interpreter.set_variable(name, Value(previous_value.as_double() - 1));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return previous_value;
|
return previous_value;
|
||||||
|
@ -520,19 +521,22 @@ Value VariableDeclaration::execute(Interpreter& interpreter) const
|
||||||
|
|
||||||
void VariableDeclaration::dump(int indent) const
|
void VariableDeclaration::dump(int indent) const
|
||||||
{
|
{
|
||||||
const char* op_string = nullptr;
|
const char* declaration_type_string = nullptr;
|
||||||
switch (m_declaration_type) {
|
switch (m_declaration_type) {
|
||||||
case DeclarationType::Let:
|
case DeclarationType::Let:
|
||||||
op_string = "Let";
|
declaration_type_string = "Let";
|
||||||
break;
|
break;
|
||||||
case DeclarationType::Var:
|
case DeclarationType::Var:
|
||||||
op_string = "Var";
|
declaration_type_string = "Var";
|
||||||
|
break;
|
||||||
|
case DeclarationType::Const:
|
||||||
|
declaration_type_string = "Const";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode::dump(indent);
|
ASTNode::dump(indent);
|
||||||
print_indent(indent + 1);
|
print_indent(indent + 1);
|
||||||
printf("%s\n", op_string);
|
printf("%s\n", declaration_type_string);
|
||||||
m_name->dump(indent + 1);
|
m_name->dump(indent + 1);
|
||||||
if (m_initializer)
|
if (m_initializer)
|
||||||
m_initializer->dump(indent + 1);
|
m_initializer->dump(indent + 1);
|
||||||
|
|
|
@ -470,6 +470,7 @@ private:
|
||||||
enum class DeclarationType {
|
enum class DeclarationType {
|
||||||
Var,
|
Var,
|
||||||
Let,
|
Let,
|
||||||
|
Const,
|
||||||
};
|
};
|
||||||
|
|
||||||
class VariableDeclaration : public Statement {
|
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)
|
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)
|
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) {
|
for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) {
|
||||||
auto& scope = m_scope_stack.at(i);
|
auto& scope = m_scope_stack.at(i);
|
||||||
if (scope.type == ScopeType::Function) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +94,11 @@ void Interpreter::declare_variable(String name, DeclarationType declaration_type
|
||||||
global_object().put(move(name), js_undefined());
|
global_object().put(move(name), js_undefined());
|
||||||
break;
|
break;
|
||||||
case DeclarationType::Let:
|
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;
|
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) {
|
for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) {
|
||||||
auto& scope = m_scope_stack.at(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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +127,7 @@ Value Interpreter::get_variable(const String& name)
|
||||||
auto& scope = m_scope_stack.at(i);
|
auto& scope = m_scope_stack.at(i);
|
||||||
auto value = scope.variables.get(name);
|
auto value = scope.variables.get(name);
|
||||||
if (value.has_value())
|
if (value.has_value())
|
||||||
return value.value();
|
return value.value().value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return global_object().get(name);
|
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& scope : m_scope_stack) {
|
||||||
for (auto& it : scope.variables) {
|
for (auto& it : scope.variables) {
|
||||||
if (it.value.is_cell())
|
if (it.value.value.is_cell())
|
||||||
roots.set(it.value.as_cell());
|
roots.set(it.value.value.as_cell());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <LibJS/Forward.h>
|
#include <LibJS/Forward.h>
|
||||||
#include <LibJS/Heap.h>
|
#include <LibJS/Heap.h>
|
||||||
|
#include <LibJS/Value.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
|
@ -38,10 +39,15 @@ enum class ScopeType {
|
||||||
Block,
|
Block,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Variable {
|
||||||
|
Value value;
|
||||||
|
DeclarationType declaration_type;
|
||||||
|
};
|
||||||
|
|
||||||
struct ScopeFrame {
|
struct ScopeFrame {
|
||||||
ScopeType type;
|
ScopeType type;
|
||||||
const ScopeNode& scope_node;
|
const ScopeNode& scope_node;
|
||||||
HashMap<String, Value> variables;
|
HashMap<String, Variable> variables;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Interpreter {
|
class Interpreter {
|
||||||
|
|
|
@ -66,6 +66,7 @@ NonnullOwnPtr<Statement> Parser::parse_statement()
|
||||||
return parse_return_statement();
|
return parse_return_statement();
|
||||||
case TokenType::Var:
|
case TokenType::Var:
|
||||||
case TokenType::Let:
|
case TokenType::Let:
|
||||||
|
case TokenType::Const:
|
||||||
return parse_variable_declaration();
|
return parse_variable_declaration();
|
||||||
case TokenType::For:
|
case TokenType::For:
|
||||||
return parse_for_statement();
|
return parse_for_statement();
|
||||||
|
@ -261,6 +262,10 @@ NonnullOwnPtr<VariableDeclaration> Parser::parse_variable_declaration()
|
||||||
declaration_type = DeclarationType::Let;
|
declaration_type = DeclarationType::Let;
|
||||||
consume(TokenType::Let);
|
consume(TokenType::Let);
|
||||||
break;
|
break;
|
||||||
|
case TokenType::Const:
|
||||||
|
declaration_type = DeclarationType::Const;
|
||||||
|
consume(TokenType::Const);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue