From 956cacc11b6b3b45ba5bc914b8332d8a7c23be05 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Tue, 27 Apr 2021 15:31:12 +0800 Subject: implemented local declaration with const attribute in Yuescript. --- src/yuescript/yue_compiler.cpp | 167 ++++++++++++++++++++++++----------------- 1 file changed, 98 insertions(+), 69 deletions(-) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 5f59340..ff554ac 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -59,7 +59,7 @@ inline std::string s(std::string_view sv) { return std::string(sv); } -const std::string_view version = "0.7.9"sv; +const std::string_view version = "0.7.10"sv; const std::string_view extension = "yue"sv; class YueCompilerImpl { @@ -218,7 +218,7 @@ private: }; struct Scope { GlobalMode mode = GlobalMode::None; - std::unique_ptr> vars; + std::unique_ptr> vars; std::unique_ptr> allows; std::unique_ptr> globals; }; @@ -257,7 +257,7 @@ private: void pushScope() { _scopes.emplace_back(); - _scopes.back().vars = std::make_unique>(); + _scopes.back().vars = std::make_unique>(); } void popScope() { @@ -272,11 +272,11 @@ private: if (current.globals) { if (current.globals->find(name) != current.globals->end()) { isDefined = true; - current.vars->insert(name); + current.vars->insert_or_assign(name, false); } } else { isDefined = true; - current.vars->insert(name); + current.vars->insert_or_assign(name, false); } } decltype(_scopes.back().allows.get()) allows = nullptr; @@ -310,6 +310,30 @@ private: return isDefined; } + bool isConst(const std::string& name) const { + bool isConst = false; + for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { + auto vars = it->vars.get(); + auto vit = vars->find(name); + if (vit != vars->end()) { + isConst = vit->second; + break; + } + } + return isConst; + } + + void checkConst(const std::string& name, ast_node* x) const { + if (isConst(name)) { + throw std::logic_error(_info.errorMessage(s("attempt to assign to const variable '"sv) + name + '\'', x)); + } + } + + void markVarConst(const std::string& name) { + auto& scope = _scopes.back(); + scope.vars->insert_or_assign(name, true); + } + void markVarShadowed() { auto& scope = _scopes.back(); scope.allows = std::make_unique>(); @@ -335,7 +359,7 @@ private: void forceAddToScope(const std::string& name) { auto& scope = _scopes.back(); - scope.vars->insert(name); + scope.vars->insert_or_assign(name, false); } Scope& currentScope() { @@ -346,7 +370,7 @@ private: bool defined = isDefined(name); if (!defined) { auto& scope = currentScope(); - scope.vars->insert(name); + scope.vars->insert_or_assign(name, false); } return !defined; } @@ -723,19 +747,21 @@ private: return nullptr; } - bool isAssignable(const node_container& chainItems) const { + bool isAssignable(const node_container& chainItems) { if (chainItems.size() == 1) { - auto firstItem = chainItems.back(); - if (auto callable = ast_cast(firstItem)) { - switch (callable->item->getId()) { - case id(): - case id(): - return true; - } - } else if (firstItem->getId() == id()) { - return true; - } - } else { + auto firstItem = chainItems.back(); + if (auto callable = ast_cast(firstItem)) { + switch (callable->item->getId()) { + case id(): + checkConst(_parser.toString(callable->item.get()), callable->item.get()); + return true; + case id(): + return true; + } + } else if (firstItem->getId() == id()) { + return true; + } + } else { auto lastItem = chainItems.back(); switch (lastItem->getId()) { case id(): @@ -746,7 +772,7 @@ private: return false; } - bool isAssignable(Exp_t* exp) const { + bool isAssignable(Exp_t* exp) { if (auto value = singleValueFrom(exp)) { auto item = value->item.get(); switch (item->getId()) { @@ -768,14 +794,14 @@ private: return false; } - bool isAssignable(Assignable_t* assignable) const { + bool isAssignable(Assignable_t* assignable) { if (auto assignableChain = ast_cast(assignable->item)) { return isAssignable(assignableChain->items.objects()); } return true; } - void checkAssignable(ExpList_t* expList) const { + void checkAssignable(ExpList_t* expList) { for (auto exp_ : expList->exprs.objects()) { Exp_t* exp = static_cast(exp_); if (!isAssignable(exp)) { @@ -1014,36 +1040,13 @@ private: return vars; } - str_list getAssignDefs(ExpList_t* expList) { - str_list preDefs; - for (auto exp_ : expList->exprs.objects()) { - auto exp = static_cast(exp_); - if (auto value = singleValueFrom(exp)) { - if (auto chain = value->item.as()) { - BLOCK_START - BREAK_IF(chain->items.size() != 1); - auto callable = ast_cast(chain->items.front()); - BREAK_IF(!callable); - std::string name; - if (auto var = callable->item.as()) { - name = _parser.toString(var); - } else if (auto self = callable->item.as()) { - if (self->name.is()) name = "self"sv; - } - BREAK_IF(name.empty()); - if (!isDefined(name)) { - preDefs.push_back(name); - } - BLOCK_END - } - } else { - throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, exp)); - } - } - return preDefs; - } + enum class DefOp { + Get, + Check, + Mark + }; - str_list transformAssignDefs(ExpList_t* expList, bool markDefined = true) { + str_list transformAssignDefs(ExpList_t* expList, DefOp op) { str_list defs; for (auto exp_ : expList->exprs.objects()) { auto exp = static_cast(exp_); @@ -1060,8 +1063,16 @@ private: if (self->name.is()) name = "self"sv; } BREAK_IF(name.empty()); - if (!markDefined || addToScope(name)) { - defs.push_back(name); + switch (op) { + case DefOp::Mark: + if (addToScope(name)) defs.push_back(name); + break; + case DefOp::Check: + if (!isDefined(name)) defs.push_back(name); + break; + case DefOp::Get: + defs.push_back(name); + break; } BLOCK_END } @@ -1096,7 +1107,7 @@ private: std::string getPredefine(ExpListAssign_t* assignment) { auto preDefine = getDestrucureDefine(assignment); if (preDefine.empty()) { - preDefine = getPredefine(transformAssignDefs(assignment->expList)); + preDefine = getPredefine(transformAssignDefs(assignment->expList, DefOp::Mark)); } return preDefine; } @@ -1183,7 +1194,7 @@ private: case id(): { auto expList = assignment->expList.get(); str_list temp; - auto defs = transformAssignDefs(expList); + auto defs = transformAssignDefs(expList, DefOp::Mark); if (!defs.empty()) temp.push_back(getPredefine(defs) + nll(expList)); switch (value->getId()) { case id(): transformIf(static_cast(value), temp, ExpUsage::Assignment, expList); break; @@ -1302,8 +1313,9 @@ private: if (destruct.items.size() == 1) { auto& pair = destruct.items.front(); _buf << indent(); - if (pair.isVariable && !isDefined(pair.name)) { - _buf << s("local "sv); + if (pair.isVariable) { + checkConst(pair.name, assignment); + if (!isDefined(pair.name)) _buf << s("local "sv); } _buf << pair.name << " = "sv << destruct.value << pair.structure << nll(assignment); addToScope(pair.name); @@ -1311,8 +1323,9 @@ private: } else if (_parser.match(destruct.value) && isDefined(destruct.value)) { str_list defs, names, values; for (const auto& item : destruct.items) { - if (item.isVariable && addToScope(item.name)) { - defs.push_back(item.name); + if (item.isVariable) { + checkConst(item.name, assignment); + if (addToScope(item.name)) defs.push_back(item.name); } names.push_back(item.name); values.push_back(item.structure); @@ -1331,8 +1344,9 @@ private: } else { str_list defs, names, values; for (const auto& item : destruct.items) { - if (item.isVariable && addToScope(item.name)) { - defs.push_back(item.name); + if (item.isVariable) { + checkConst(item.name, assignment); + if (addToScope(item.name)) defs.push_back(item.name); } names.push_back(item.name); values.push_back(item.structure); @@ -1711,7 +1725,7 @@ private: right = s("("sv) + right + s(")"sv); } _buf << join(temp) << indent() << left << " = "sv << left << - " "sv << _parser.toString(update->op) << " "sv << right << nll(assignment); + ' ' << _parser.toString(update->op) << ' ' << right << nll(assignment); out.push_back(clearBuf()); break; } @@ -1728,7 +1742,7 @@ private: } } } - auto defs = getAssignDefs(expList); + auto defs = transformAssignDefs(expList, DefOp::Check); if (oneLined && defs.size() == expList->exprs.objects().size()) { for (auto value : assign->values.objects()) { transformAssignItem(value, temp); @@ -2374,7 +2388,7 @@ private: } } if (info.second) { - auto defs = transformAssignDefs(info.second->expList, false); + auto defs = transformAssignDefs(info.second->expList, DefOp::Get); for (const auto& def : defs) { if (std::isupper(def[0]) && capital) { capital->decls.push_back(def); } else if (any) { @@ -4589,7 +4603,7 @@ private: if (auto statement = ast_cast(item)) { ClassDecl_t* clsDecl = nullptr; if (auto assignment = assignmentFrom(statement)) { - auto names = transformAssignDefs(assignment->expList.get()); + auto names = transformAssignDefs(assignment->expList.get(), DefOp::Mark); varDefs.insert(varDefs.end(), names.begin(), names.end()); auto info = extractDestructureInfo(assignment, true); if (!info.first.empty()) { @@ -4906,7 +4920,6 @@ private: std::string withVar; bool scoped = false; if (with->assigns) { - checkAssignable(with->valueList); auto vars = getAssignVars(with); if (vars.front().empty()) { if (with->assigns->values.objects().size() == 1) { @@ -4978,7 +4991,7 @@ private: if (auto statement = ast_cast(node)) { ClassDecl_t* clsDecl = nullptr; if (auto assignment = assignmentFrom(statement)) { - auto names = getAssignDefs(assignment->expList.get()); + auto names = transformAssignDefs(assignment->expList.get(), DefOp::Get); if (!names.empty()) { return traversal::Stop; } @@ -5132,7 +5145,7 @@ private: assignment->expList.set(expList); assignment->action.set(exportNode->assign); transformAssignment(assignment, out); - str_list names = transformAssignDefs(expList, false); + str_list names = transformAssignDefs(expList, DefOp::Get); auto info = extractDestructureInfo(assignment, true); if (!info.first.empty()) { for (const auto& destruct : info.first) @@ -5501,7 +5514,7 @@ private: } } if (objAssign) { - auto preDef = getPredefine(transformAssignDefs(expList)); + auto preDef = getPredefine(transformAssignDefs(expList, DefOp::Mark)); if (!preDef.empty()) { temp.push_back(preDef + nll(import)); } @@ -5886,6 +5899,22 @@ private: if (attrib != "close"sv && attrib != "const"sv) { throw std::logic_error(_info.errorMessage(s("unknown attribute '"sv) + attrib + '\'', localAttrib->attrib)); } + if (attrib == "const"sv) { + str_list vars; + for (auto name : localAttrib->nameList->names.objects()) { + vars.push_back(_parser.toString(name)); + } + auto varStr = join(vars, ", "sv); + auto varList = toAst(varStr, x); + auto assignment = x->new_ptr(); + assignment->expList.set(varList); + assignment->action.set(localAttrib->assign); + transformAssignment(assignment, out); + for (const auto& var : vars) { + markVarConst(var); + } + return; + } auto expList = x->new_ptr(); str_list tmpVars; str_list vars; -- cgit v1.2.3-55-g6feb