From aed806476fe50899c0f01750175531ac41267b9d Mon Sep 17 00:00:00 2001 From: Li Jin Date: Sun, 31 Oct 2021 17:29:45 +0800 Subject: partially fix issue #69, fix a few case the generated code leaks some temp variables. --- spec/inputs/nil_coalesing.yue | 40 ++++++ spec/outputs/export.lua | 32 ++--- spec/outputs/multiline_chain.lua | 12 +- spec/outputs/nil_coalesing.lua | 138 +++++++++++++++++++++ spec/outputs/switch.lua | 110 +++++++++-------- src/yuescript/yue_ast.h | 3 +- src/yuescript/yue_compiler.cpp | 255 +++++++++++++++++++++++++++++++++------ src/yuescript/yue_parser.cpp | 7 +- 8 files changed, 490 insertions(+), 107 deletions(-) create mode 100644 spec/inputs/nil_coalesing.yue create mode 100644 spec/outputs/nil_coalesing.lua diff --git a/spec/inputs/nil_coalesing.yue b/spec/inputs/nil_coalesing.yue new file mode 100644 index 0000000..45b34f4 --- /dev/null +++ b/spec/inputs/nil_coalesing.yue @@ -0,0 +1,40 @@ +do + a = b ?? c + +do + :a = b ?? c + +do + a = func! + a ??= false + +do + a ??= func! + +do + local a, b, c, d, e + a = b ?? c ?? d ?? e + +do + a = b ?? c ?? d ?? e + +do + func x ?? "hello" + +do + with funcA! ?? funcB! + print .field + +do + a = 1 + 2 + b ?? c + 3 + 4 + +do + a = 1 + 2 + (b ?? c) + 3 + 4 + +do + :a, :b = func! + a or= 1 + b ??= c ?? 2 + +nil + diff --git a/spec/outputs/export.lua b/spec/outputs/export.lua index 02546ec..4d81cc0 100644 --- a/spec/outputs/export.lua +++ b/spec/outputs/export.lua @@ -65,22 +65,26 @@ y = function() local k = 100 end _module_0["y"] = y -local _exp_0 = h -if 100 == _exp_0 or 150 == _exp_0 then - _module_0[#_module_0 + 1] = 200 -elseif 200 == _exp_0 then - _module_0[#_module_0 + 1] = 300 -else - _module_0[#_module_0 + 1] = 0 +do + local _exp_0 = h + if 100 == _exp_0 or 150 == _exp_0 then + _module_0[#_module_0 + 1] = 200 + elseif 200 == _exp_0 then + _module_0[#_module_0 + 1] = 300 + else + _module_0[#_module_0 + 1] = 0 + end end local Constant -local _exp_1 = value -if "good" == _exp_1 then - Constant = 1 -elseif "better" == _exp_1 then - Constant = 2 -elseif "best" == _exp_1 then - Constant = 3 +do + local _exp_0 = value + if "good" == _exp_0 then + Constant = 1 + elseif "better" == _exp_0 then + Constant = 2 + elseif "best" == _exp_0 then + Constant = 3 + end end _module_0["Constant"] = Constant local item = func(123) diff --git a/spec/outputs/multiline_chain.lua b/spec/outputs/multiline_chain.lua index fea7bc6..ecd2ca8 100644 --- a/spec/outputs/multiline_chain.lua +++ b/spec/outputs/multiline_chain.lua @@ -56,11 +56,13 @@ do _with_0:callMethod():chainCall() _with_0:callMethod():chainCall() _with_0:chainCall() - local _exp_0 = _with_0.itemFieldB:getValue() - if "Valid" == _exp_0 or _with_0:getItemState() == _exp_0 then - _with_0:itemMethodA():getValue() - else - _with_0:itemMethodB():getValue() + do + local _exp_0 = _with_0.itemFieldB:getValue() + if "Valid" == _exp_0 or _with_0:getItemState() == _exp_0 then + _with_0:itemMethodA():getValue() + else + _with_0:itemMethodB():getValue() + end end local a if _with_0.itemFieldC then diff --git a/spec/outputs/nil_coalesing.lua b/spec/outputs/nil_coalesing.lua new file mode 100644 index 0000000..36a6644 --- /dev/null +++ b/spec/outputs/nil_coalesing.lua @@ -0,0 +1,138 @@ +do + local a + do + local _exp_0 = b + if _exp_0 ~= nil then + a = _exp_0 + else + a = c + end + end +end +do + local a + do + local _exp_0 = b + if _exp_0 ~= nil then + a = _exp_0.a + else + a = c.a + end + end +end +do + local a = func() + if a == nil then + a = false + end +end +do + local a + if a == nil then + a = func() + end +end +do + local a, b, c, d, e + if b ~= nil then + a = b + else + if c ~= nil then + a = c + else + if d ~= nil then + a = d + else + a = e + end + end + end +end +do + local a + do + local _exp_0 = b + if _exp_0 ~= nil then + a = _exp_0 + else + do + local _exp_1 = c + if _exp_1 ~= nil then + a = _exp_1 + else + do + local _exp_2 = d + if _exp_2 ~= nil then + a = _exp_2 + else + a = e + end + end + end + end + end + end +end +do + func((function() + local _exp_0 = x + if _exp_0 ~= nil then + return _exp_0 + else + return "hello" + end + end)()) +end +do + do + local _with_0 + do + local _exp_0 = funcA() + if _exp_0 ~= nil then + _with_0 = _exp_0 + else + _with_0 = funcB() + end + end + print(_with_0.field) + end +end +do + local a = 1 + 2 + (function() + local _exp_0 = b + if _exp_0 ~= nil then + return _exp_0 + else + return c + 3 + 4 + end + end)() +end +do + local a = 1 + 2 + ((function() + local _exp_0 = b + if _exp_0 ~= nil then + return _exp_0 + else + return c + end + end)()) + 3 + 4 +end +do + local a, b + do + local _obj_0 = func() + a, b = _obj_0.a, _obj_0.b + end + a = a or 1 + if b == nil then + do + local _exp_0 = c + if _exp_0 ~= nil then + b = _exp_0 + else + b = 2 + end + end + end +end +return nil diff --git a/spec/outputs/switch.lua b/spec/outputs/switch.lua index 1c8839f..81f6d5a 100644 --- a/spec/outputs/switch.lua +++ b/spec/outputs/switch.lua @@ -1,70 +1,86 @@ -local _exp_0 = value -if "cool" == _exp_0 then - print("hello world") +do + local _exp_0 = value + if "cool" == _exp_0 then + print("hello world") + end end -local _exp_1 = value -if "cool" == _exp_1 then - print("hello world") -else - print("okay rad") +do + local _exp_0 = value + if "cool" == _exp_0 then + print("hello world") + else + print("okay rad") + end end -local _exp_2 = value -if "cool" == _exp_2 then - print("hello world") -elseif "yeah" == _exp_2 then - local _ = [[FFFF]] + [[MMMM]] -elseif (2323 + 32434) == _exp_2 then - print("okay") -else - print("okay rad") +do + local _exp_0 = value + if "cool" == _exp_0 then + print("hello world") + elseif "yeah" == _exp_0 then + local _ = [[FFFF]] + [[MMMM]] + elseif (2323 + 32434) == _exp_0 then + print("okay") + else + print("okay rad") + end end local out -local _exp_3 = value -if "cool" == _exp_3 then - out = print("hello world") -else - out = print("okay rad") +do + local _exp_0 = value + if "cool" == _exp_0 then + out = print("hello world") + else + out = print("okay rad") + end end -local _exp_4 = value -if "cool" == _exp_4 then - out = xxxx -elseif "umm" == _exp_4 then - out = 34340 -else - out = error("this failed big time") +do + local _exp_0 = value + if "cool" == _exp_0 then + out = xxxx + elseif "umm" == _exp_0 then + out = 34340 + else + out = error("this failed big time") + end end do local _with_0 = something - local _exp_5 = _with_0:value() - if _with_0.okay == _exp_5 then - local _ = "world" - else - local _ = "yesh" + do + local _exp_0 = _with_0:value() + if _with_0.okay == _exp_0 then + local _ = "world" + else + local _ = "yesh" + end end end fix(this) call_func((function() - local _exp_5 = something - if 1 == _exp_5 then + local _exp_0 = something + if 1 == _exp_0 then return "yes" else return "no" end end)()) -local _exp_5 = hi -if (hello or world) == _exp_5 then - local _ = greene +do + local _exp_0 = hi + if (hello or world) == _exp_0 then + local _ = greene + end end -local _exp_6 = hi -if "one" == _exp_6 or "two" == _exp_6 then - print("cool") -elseif "dad" == _exp_6 then - local _ = no +do + local _exp_0 = hi + if "one" == _exp_0 or "two" == _exp_0 then + print("cool") + elseif "dad" == _exp_0 then + local _ = no + end end -local _exp_7 = hi -if (3 + 1) == _exp_7 or hello() == _exp_7 or (function() +local _exp_0 = hi +if (3 + 1) == _exp_0 or hello() == _exp_0 or (function() return 4 -end)() == _exp_7 then +end)() == _exp_0 then return yello else return print("cool") diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 38da1fa..719b1cd 100755 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -404,7 +404,8 @@ AST_NODE(Exp) ast_ptr sep; ast_list pipeExprs; ast_list opValues; - AST_MEMBER(Exp, &sep, &pipeExprs, &opValues) + ast_ptr nilCoalesed; + AST_MEMBER(Exp, &sep, &pipeExprs, &opValues, &nilCoalesed) AST_END(Exp) class Parens_t; diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 3ff032c..415fcd3 100755 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -856,6 +856,10 @@ private: return exp->opValues.empty() && exp->pipeExprs.size() > 1; } + bool isPureNilCoalesed(Exp_t* exp) const { + return exp->nilCoalesed && exp->opValues.empty(); + } + bool isMacroChain(ChainValue_t* chainValue) const { const auto& chainList = chainValue->items.objects(); BLOCK_START @@ -1345,6 +1349,10 @@ private: auto expList = assignment->expList.get(); transformExp(exp, out, ExpUsage::Assignment, expList); return; + } else if (isPureNilCoalesed(exp)) { + auto expList = assignment->expList.get(); + transformNilCoalesedExp(exp, out, ExpUsage::Assignment, expList); + return; } auto singleVal = singleValueFrom(exp); BREAK_IF(!singleVal); @@ -2020,12 +2028,12 @@ private: auto var = singleVariableFrom(exp); BREAK_IF(!var.empty()); auto upVar = getUnusedName("_update_"sv); - auto assignment = x->new_ptr(); - assignment->expList.set(toAst(upVar, x)); + auto newAssignment = x->new_ptr(); + newAssignment->expList.set(toAst(upVar, x)); auto assign = x->new_ptr(); assign->values.push_back(exp); - assignment->action.set(assign); - transformAssignment(assignment, temp); + newAssignment->action.set(assign); + transformAssignment(newAssignment, temp); tmpChain->items.push_back(toAst(upVar, x)); itemAdded = true; BLOCK_END @@ -2034,6 +2042,17 @@ private: chain->items.clear(); chain->items.dup(tmpChain->items); } + auto op = _parser.toString(update->op); + if (op == "??"sv) { + auto defs = getPredefine(assignment); + auto rightExp = x->new_ptr(); + rightExp->pipeExprs.dup(leftExp->pipeExprs); + rightExp->opValues.dup(leftExp->opValues); + rightExp->nilCoalesed.set(update->value); + transformNilCoalesedExp(rightExp, out, ExpUsage::Assignment, assignment->expList, true); + if (!defs.empty()) out.back().insert(0, defs + nll(x)); + return; + } auto defs = getPredefine(assignment); transformValue(leftValue, temp); auto left = std::move(temp.back()); @@ -2048,7 +2067,7 @@ private: if (!defs.empty()) _buf << defs; else _buf << indent() << left; _buf << " = "sv << left << - ' ' << _parser.toString(update->op) << ' ' << right << nll(assignment); + ' ' << op << ' ' << right << nll(assignment); out.push_back(clearBuf()); break; } @@ -2142,10 +2161,10 @@ private: str_list temp; std::string* funcStart = nullptr; if (usage == ExpUsage::Closure) { + _enableReturn.push(true); pushAnonVarArg(); funcStart = &temp.emplace_back(); pushScope(); - _enableReturn.push(true); } std::list> ifCondPairs; ifCondPairs.emplace_back(); @@ -2255,11 +2274,11 @@ private: temp.push_back(indent() + "end"s + nlr(nodes.front())); } if (usage == ExpUsage::Closure) { - _enableReturn.pop(); popScope(); *funcStart = anonFuncStart() + nll(nodes.front()); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); + _enableReturn.pop(); } out.push_back(join(temp)); } @@ -2375,15 +2394,19 @@ private: } void transformExp(Exp_t* exp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - if (exp->opValues.empty()) { + if (exp->opValues.empty() && !exp->nilCoalesed) { transform_pipe_exp(exp->pipeExprs.objects(), out, usage, assignList); return; } if (usage != ExpUsage::Closure) { YUEE("invalid expression usage", exp); } + if (exp->nilCoalesed) { + transformNilCoalesedExp(exp, out, ExpUsage::Closure); + return; + } str_list temp; - transform_pipe_exp(exp->pipeExprs.objects(), temp, ExpUsage::Closure); + transform_pipe_exp(exp->pipeExprs.objects(), temp, ExpUsage::Closure); for (auto _opValue : exp->opValues.objects()) { auto opValue = static_cast(_opValue); transformBinaryOperator(opValue->op, temp); @@ -2392,6 +2415,129 @@ private: out.push_back(join(temp, " "sv)); } + void transformNilCoalesedExp(Exp_t* exp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr, bool nilBranchOnly = false) { + auto x = exp; + str_list temp; + std::string prefix; + auto left = exp->new_ptr(); + if (exp->opValues.empty()) { + left->pipeExprs.dup(exp->pipeExprs); + } else { + if (usage != ExpUsage::Closure) { + YUEE("invalid expression usage", exp); + } + transform_pipe_exp(exp->pipeExprs.objects(), temp, ExpUsage::Closure); + auto last = exp->opValues.objects().back(); + for (auto _opValue : exp->opValues.objects()) { + auto opValue = static_cast(_opValue); + transformBinaryOperator(opValue->op, temp); + if (opValue == last) { + left->pipeExprs.dup(opValue->pipeExprs); + } else { + transform_pipe_exp(opValue->pipeExprs.objects(), temp, ExpUsage::Closure); + } + } + prefix = join(temp, " "sv) + ' '; + temp.clear(); + temp.push_back(prefix); + } + std::string* funcStart = nullptr; + if (usage == ExpUsage::Closure) { + _enableReturn.push(true); + pushAnonVarArg(); + funcStart = &temp.emplace_back(); + pushScope(); + } + auto objVar = singleVariableFrom(left); + auto prepareValue = [&](bool forAssignment = false) { + if (objVar.empty() || !isLocal(objVar)) { + if (forAssignment) { + temp.push_back(indent() + "do"s + nll(x)); + pushScope(); + } + objVar = getUnusedName("_exp_"sv); + auto expList = toAst(objVar, x); + auto assign = x->new_ptr(); + assign->values.push_back(left); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + return true; + } + return false; + }; + switch (usage) { + case ExpUsage::Common: YUEE("AST node mismatch", x); return; + case ExpUsage::Return: + case ExpUsage::Closure: { + prepareValue(); + _buf << indent() << "if "sv << objVar << " ~= nil then"sv << nll(x); + _buf << indent(1) << "return "s << objVar << nll(x); + _buf << indent() << "else"s << nll(x); + temp.push_back(clearBuf()); + auto ret = x->new_ptr(); + auto retList = x->new_ptr(); + retList->exprs.push_back(exp->nilCoalesed); + ret->valueList.set(retList); + incIndentOffset(); + transformReturn(ret, temp); + decIndentOffset(); + temp.push_back(indent() + "end"s + nll(x)); + if (usage == ExpUsage::Closure) { + popScope(); + *funcStart = anonFuncStart() + nll(x); + temp.push_back(indent() + anonFuncEnd()); + popAnonVarArg(); + _enableReturn.pop(); + } + break; + } + case ExpUsage::Assignment: { + auto assign = x->new_ptr(); + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + assignment->action.set(assign); + bool extraScope = false; + if (!nilBranchOnly) { + assign->values.push_back(exp); + std::string& predefine = temp.emplace_back(); + auto defs = getPredefine(assignment); + if (!defs.empty()) predefine = defs + nll(x); + extraScope = prepareValue(true); + _buf << indent() << "if "sv << objVar << " ~= nil then"sv << nll(x); + temp.push_back(clearBuf()); + pushScope(); + assign->values.clear(); + assign->values.push_back(toAst(objVar, x)); + transformAssignment(assignment, temp); + popScope(); + temp.push_back(indent() + "else"s + nll(x)); + assign->values.clear(); + assign->values.push_back(exp->nilCoalesed); + } else { + assign->values.clear(); + assign->values.push_back(exp->nilCoalesed); + auto defs = getPredefine(assignment); + if (!defs.empty()) temp.push_back(defs + nll(x)); + prepareValue(); + _buf << indent() << "if "sv << objVar << " == nil then"sv << nll(x); + temp.push_back(clearBuf()); + } + pushScope(); + transformAssignment(assignment, temp); + popScope(); + temp.push_back(indent() + "end"s + nlr(x)); + if (extraScope) { + popScope(); + temp.push_back(indent() + "end"s + nlr(x)); + } + break; + } + } + out.push_back(join(temp)); + } + void transformValue(Value_t* value, str_list& out) { auto item = value->item.get(); switch (item->getId()) { @@ -3022,6 +3168,9 @@ private: if (isPureBackcall(exp)) { transformExp(exp, out, ExpUsage::Return); return; + } else if (isPureNilCoalesed(exp)) { + transformNilCoalesedExp(exp, out, ExpUsage::Return); + return; } } if (auto singleValue = singleValueFrom(valueList)) { @@ -3294,10 +3443,10 @@ private: str_list temp; std::string* funcStart = nullptr; if (usage == ExpUsage::Closure) { + _enableReturn.push(true); pushAnonVarArg(); funcStart = &temp.emplace_back(); pushScope(); - _enableReturn.push(true); } auto partOne = x->new_ptr(); for (auto it = chainList.begin();it != opIt;++it) { @@ -3428,11 +3577,11 @@ private: break; case ExpUsage::Closure: temp.push_back(indent() + "return nil"s + nlr(x)); - _enableReturn.pop(); popScope(); *funcStart = anonFuncStart() + nll(x); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); + _enableReturn.pop(); break; default: break; @@ -3454,10 +3603,10 @@ private: pushScope(); break; case ExpUsage::Closure: + _enableReturn.push(true); pushAnonVarArg(); funcStart = &temp.emplace_back(); pushScope(); - _enableReturn.push(true); break; default: break; @@ -3529,11 +3678,11 @@ private: temp.push_back(indent() + "end"s + nlr(x)); break; case ExpUsage::Closure: - _enableReturn.pop(); popScope(); *funcStart = anonFuncStart() + nll(x); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); + _enableReturn.pop(); break; default: break; @@ -4263,8 +4412,8 @@ private: switch (usage) { case ExpUsage::Closure: _enableReturn.push(true); - pushScope(); pushAnonVarArg(); + pushScope(); break; case ExpUsage::Assignment: pushScope(); @@ -4320,13 +4469,13 @@ private: case ExpUsage::Common: break; case ExpUsage::Closure: { - _enableReturn.pop(); out.push_back(clearBuf()); out.back().append(indent() + "return "s + accumVar + nlr(comp)); popScope(); out.back().insert(0, anonFuncStart() + nll(comp)); out.back().append(indent() + anonFuncEnd()); popAnonVarArg(); + _enableReturn.pop(); break; } case ExpUsage::Assignment: { @@ -4352,11 +4501,12 @@ private: } } - void transformForEachHead(AssignableNameList_t* nameList, ast_node* loopTarget, str_list& out) { + bool transformForEachHead(AssignableNameList_t* nameList, ast_node* loopTarget, str_list& out, bool isStatement = false) { auto x = nameList; str_list temp; str_list vars; str_list varBefore, varAfter; + bool extraScope = false; std::list>> destructPairs; for (auto _item : nameList->items.objects()) { auto item = static_cast(_item)->item.get(); @@ -4422,16 +4572,28 @@ private: temp.pop_back(); } if (listVar.empty()) { + std::string prefix; + if (isStatement) { + extraScope = true; + prefix = indent() + "do"s + nll(x); + pushScope(); + } listVar = getUnusedName("_list_"sv); varBefore.push_back(listVar); transformChainValue(chain, temp, ExpUsage::Closure); - _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); + _buf << prefix << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); } std::string maxVar; if (!stopValue.empty()) { + std::string prefix; + if (isStatement && !extraScope) { + extraScope = true; + prefix = indent() + "do"s + nll(x); + pushScope(); + } maxVar = getUnusedName("_max_"sv); varBefore.push_back(maxVar); - _buf << indent() << "local "sv << maxVar << " = "sv << stopValue << nll(nameList); + _buf << prefix << indent() << "local "sv << maxVar << " = "sv << stopValue << nll(nameList); } _buf << indent() << "for "sv << indexVar << " = "sv; _buf << startValue << ", "sv; @@ -4497,6 +4659,7 @@ private: } out.back().append(join(temp)); } + return extraScope; } void transformCompForEach(CompForEach_t* comp, str_list& out) { @@ -4651,17 +4814,17 @@ private: void transformForClosure(For_t* forNode, str_list& out) { str_list temp; + _enableReturn.push(true); pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); - _enableReturn.push(true); auto accum = transformForInner(forNode, temp); temp.push_back(indent() + "return "s + accum + nlr(forNode)); - _enableReturn.pop(); popScope(); funcStart = anonFuncStart() + nll(forNode); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); + _enableReturn.pop(); out.push_back(join(temp)); } @@ -4697,10 +4860,14 @@ private: void transformForEach(ForEach_t* forEach, str_list& out) { str_list temp; - transformForEachHead(forEach->nameList, forEach->loopValue, temp); + bool extraScope = transformForEachHead(forEach->nameList, forEach->loopValue, temp, true); transformLoopBody(forEach->body, temp, Empty, ExpUsage::Common); popScope(); out.push_back(temp.front() + temp.back() + indent() + "end"s + nlr(forEach)); + if (extraScope) { + popScope(); + out.back().append(indent() + "end"s + nlr(forEach)); + } } std::string transformForEachInner(ForEach_t* forEach, str_list& out) { @@ -4723,17 +4890,17 @@ private: void transformForEachClosure(ForEach_t* forEach, str_list& out) { str_list temp; + _enableReturn.push(true); pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); - _enableReturn.push(true); auto accum = transformForEachInner(forEach, temp); temp.push_back(indent() + "return "s + accum + nlr(forEach)); - _enableReturn.pop(); popScope(); funcStart = anonFuncStart() + nll(forEach); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); + _enableReturn.pop(); out.push_back(join(temp)); } @@ -4878,16 +5045,16 @@ private: void transformClassDeclClosure(ClassDecl_t* classDecl, str_list& out) { str_list temp; + _enableReturn.push(true); pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); - _enableReturn.push(true); transformClassDecl(classDecl, temp, ExpUsage::Return); - _enableReturn.pop(); popScope(); funcStart = anonFuncStart() + nll(classDecl); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); + _enableReturn.pop(); out.push_back(join(temp)); } @@ -5256,16 +5423,16 @@ private: void transformWithClosure(With_t* with, str_list& out) { str_list temp; + _enableReturn.push(true); pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); - _enableReturn.push(true); transformWith(with, temp, nullptr, true); - _enableReturn.pop(); popScope(); funcStart = anonFuncStart() + nll(with); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); + _enableReturn.pop(); out.push_back(join(temp)); } @@ -5674,9 +5841,9 @@ private: void transformTblComprehension(TblComprehension_t* comp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { switch (usage) { case ExpUsage::Closure: - pushScope(); _enableReturn.push(true); pushAnonVarArg(); + pushScope(); break; case ExpUsage::Assignment: pushScope(); @@ -5733,10 +5900,10 @@ private: case ExpUsage::Closure: out.push_back(clearBuf() + indent() + "return "s + tbl + nlr(comp)); popScope(); - _enableReturn.pop(); out.back().insert(0, anonFuncStart() + nll(comp)); out.back().append(indent() + anonFuncEnd()); popAnonVarArg(); + _enableReturn.pop(); break; case ExpUsage::Assignment: { out.push_back(clearBuf()); @@ -5802,10 +5969,10 @@ private: transformBody(doNode->body, temp, usage, assignList); popScope(); if (usage == ExpUsage::Closure) { - _enableReturn.pop(); *funcStart = anonFuncStart() + nll(doNode); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); + _enableReturn.pop(); } else { temp.push_back(indent() + "end"s + nlr(doNode)); } @@ -6111,10 +6278,10 @@ private: void transformWhileClosure(While_t* whileNode, str_list& out) { auto x = whileNode; str_list temp; + _enableReturn.push(true); pushAnonVarArg(); std::string& funcStart = temp.emplace_back(); pushScope(); - _enableReturn.push(true); auto accumVar = getUnusedName("_accum_"sv); addToScope(accumVar); auto lenVar = getUnusedName("_len_"sv); @@ -6131,11 +6298,11 @@ private: popScope(); temp.push_back(indent() + "end"s + nlr(whileNode)); temp.push_back(indent() + "return "s + accumVar + nlr(whileNode)); - _enableReturn.pop(); popScope(); funcStart = anonFuncStart() + nll(whileNode); temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); + _enableReturn.pop(); out.push_back(join(temp)); } @@ -6165,21 +6332,31 @@ private: } void transformSwitch(Switch_t* switchNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + auto x = switchNode; str_list temp; std::string* funcStart = nullptr; if (usage == ExpUsage::Closure) { + _enableReturn.push(true); pushAnonVarArg(); funcStart = &temp.emplace_back(); pushScope(); - _enableReturn.push(true); } + bool extraScope = false; auto objVar = singleVariableFrom(switchNode->target); if (objVar.empty() || !isLocal(objVar)) { + if (usage == ExpUsage::Common || usage == ExpUsage::Assignment) { + extraScope = true; + temp.push_back(indent() + "do"s + nll(x)); + pushScope(); + } objVar = getUnusedName("_exp_"sv); - addToScope(objVar); - transformExp(switchNode->target, temp, ExpUsage::Closure); - _buf << indent() << "local "sv << objVar << " = "sv << temp.back() << nll(switchNode); - temp.back() = clearBuf(); + auto expList = toAst(objVar, x); + auto assign = x->new_ptr(); + assign->values.push_back(switchNode->target); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); } const auto& branches = switchNode->branches.objects(); for (auto branch_ : branches) { @@ -6215,6 +6392,10 @@ private: temp.push_back(indent() + anonFuncEnd()); popAnonVarArg(); } + if (extraScope) { + popScope(); + temp.push_back(indent() + "end"s + nlr(switchNode)); + } out.push_back(join(temp)); } diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index 6ecd369..5628062 100755 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -323,7 +323,8 @@ YueParser::YueParser() { expr("&") | expr("|") | expr(">>") | - expr("<<"); + expr("<<") | + expr("??"); Update = Space >> update_op >> expr("=") >> Exp; @@ -360,7 +361,7 @@ YueParser::YueParser() { expr("//") | set("+-*/%><|&~"); exp_op_value = Space >> BinaryOperator >> *SpaceBreak >> pipe_exp; - Exp = Seperator >> pipe_exp >> *exp_op_value; + Exp = Seperator >> pipe_exp >> *exp_op_value >> -(Space >> expr("??") >> Exp); DisableChain = pl::user(true_(), [](const item_t& item) { State* st = reinterpret_cast(item.user_data); @@ -424,7 +425,7 @@ YueParser::YueParser() { Metatable = expr('#'); Metamethod = Name >> expr('#'); - existential_op = expr('?'); + existential_op = expr('?') >> not_(expr('?')); chain_call = (Callable | String) >> -existential_op >> ChainItems; chain_item = and_(set(".\\")) >> ChainItems; chain_dot_chain = DotChainItem >> -existential_op >> -ChainItems; -- cgit v1.2.3-55-g6feb