From 548ab1d9ff5b831050f14f1355a3314a41163ad6 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 4 Jun 2025 11:38:34 +0800 Subject: Added new syntax. - Slice Expression. - Reversed Indexing,. - Range Destructuring. - Table Range Matching. --- src/yuescript/yue_ast.cpp | 6 + src/yuescript/yue_ast.h | 7 +- src/yuescript/yue_compiler.cpp | 248 +++++++++++++++++++++++++++++++++++------ src/yuescript/yue_parser.cpp | 3 +- src/yuescript/yue_parser.h | 1 + 5 files changed, 232 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp index c4f133b..da99d07 100644 --- a/src/yuescript/yue_ast.cpp +++ b/src/yuescript/yue_ast.cpp @@ -870,6 +870,12 @@ std::string Exp_t::to_string(void* ud) const { } return join(temp, " "sv); } +std::string ReversedIndex_t::to_string(void* ud) const { + if (modifier) { + return "[# - "s + modifier->to_string(ud) + ']'; + } + return "[#]"s; +} std::string Callable_t::to_string(void* ud) const { return item->to_string(ud); } diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index 6bdc31b..d396d82 100644 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -643,9 +643,14 @@ AST_END(TableAppendingOp) AST_LEAF(PlainItem) AST_END(PlainItem) +AST_NODE(ReversedIndex) + ast_ptr modifier; + AST_MEMBER(ReversedIndex, &modifier) +AST_END(ReversedIndex) + AST_NODE(ChainValue) ast_ptr sep; - ast_sel_list items; AST_MEMBER(ChainValue, &sep, &items) AST_END(ChainValue) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 4bac51b..cbf976f 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -78,7 +78,7 @@ static std::unordered_set Metamethods = { "close"s // Lua 5.4 }; -const std::string_view version = "0.28.7"sv; +const std::string_view version = "0.29.0"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { @@ -1281,6 +1281,8 @@ private: Common, EndWithColon, EndWithEOP, + EndWithSlice, + HasRIndex, HasEOP, HasKeyword, HasUnicode, @@ -1299,6 +1301,9 @@ private: if (ast_is(chainValue->items.back())) { return ChainType::EndWithEOP; } + if (ast_is(chainValue->items.back())) { + return ChainType::EndWithSlice; + } if (auto dot = ast_cast(chainValue->items.back())) { if (dot->name.is()) { return ChainType::Metatable; @@ -1324,6 +1329,8 @@ private: } } else if (ast_is(item)) { return ChainType::HasEOP; + } else if (ast_is(item)) { + return ChainType::HasRIndex; } } return type; @@ -2471,12 +2478,14 @@ private: switch (type) { case ChainType::HasEOP: case ChainType::EndWithColon: + case ChainType::EndWithSlice: case ChainType::MetaFieldInvocation: { std::string preDefine = getPreDefineLine(assignment); transformChainValue(chainValue, out, ExpUsage::Assignment, expList, false, optionalDestruct); out.back().insert(0, preDefine); return false; } + case ChainType::HasRIndex: case ChainType::HasKeyword: case ChainType::HasUnicode: case ChainType::Macro: @@ -2832,17 +2841,19 @@ private: if (!tableItems) throw CompileError("invalid destructure value"sv, node); std::list pairs; int index = 0; + int count = 0; + bool hasSpread = false; auto subMetaDestruct = node->new_ptr(); for (auto pair : *tableItems) { switch (pair->get_id()) { case id(): case id(): { + ++index; Exp_t* defVal = nullptr; if (auto nd = ast_cast(pair)) { pair = nd->item.get(); defVal = nd->defVal.get(); } - ++index; bool assignable = false; try { assignable = isAssignable(static_cast(pair)); @@ -2853,13 +2864,19 @@ private: if (optional) break; throw CompileError("can't destructure value"sv, pair); } + ast_ptr indexItem; + if (hasSpread) { + int rIndex = count - index; + indexItem.set(toAst('#' + (rIndex == 0 ? Empty : "-"s + std::to_string(rIndex)), pair)); + } else { + indexItem.set(toAst(std::to_string(index), pair)); + } if (optional && varDefOnly && !assignable) { if (defVal) { throw CompileError("default value is not supported here"sv, defVal); } auto exp = static_cast(pair); auto chain = exp->new_ptr(); - auto indexItem = toAst(std::to_string(index), exp); chain->items.push_back(indexItem); pairs.push_back({exp, Empty, chain, nullptr}); break; @@ -2874,7 +2891,6 @@ private: throw CompileError("default value is not supported here"sv, defVal); } } - auto indexItem = toAst(std::to_string(index), value); for (auto& p : subPairs) { if (sep) p.structure->items.push_front(sep); p.structure->items.push_front(indexItem); @@ -2885,7 +2901,6 @@ private: auto varName = singleVariableFrom(exp, AccessType::None); if (varName == "_"sv) break; auto chain = exp->new_ptr(); - auto indexItem = toAst(std::to_string(index), exp); chain->items.push_back(indexItem); pairs.push_back({exp, varName, @@ -3010,7 +3025,13 @@ private: auto tb = static_cast(pair); ++index; auto subPairs = destructFromExp(tb, varDefOnly, optional); - auto indexItem = toAst(std::to_string(index), tb); + ast_ptr indexItem; + if (hasSpread) { + int rIndex = count - index; + indexItem.set(toAst('#' + (rIndex == 0 ? Empty : "-"s + std::to_string(rIndex)), tb)); + } else { + indexItem.set(toAst(std::to_string(index), tb)); + } for (auto& p : subPairs) { if (sep) p.structure->items.push_front(sep); p.structure->items.push_front(indexItem); @@ -3067,6 +3088,42 @@ private: subMetaDestruct->values.push_back(newPairDef); break; } + case id(): + case id(): { + ++index; + if (hasSpread) { + throw CompileError("duplicated spread expression"sv, pair); + } + hasSpread = true; + for (auto item : *tableItems) { + if (ast_is< + SpreadListExp_t, SpreadExp_t, + TableBlockIndent_t, + Exp_t, NormalDef_t>(item)) { + count++; + } + } + Exp_t* exp = nullptr; + if (auto se = ast_cast(pair)) { + exp = se->exp.get(); + } else { + exp = ast_to(pair)->exp.get(); + } + auto varName = singleVariableFrom(exp, AccessType::None); + if (varName == "_"sv) break; + int start = index; + int stop = index - count - 1; + auto chain = exp->new_ptr(); + auto slice = toAst( + '[' + (start == 1 ? Empty : std::to_string(start)) + ',' + (stop == -1 ? Empty : std::to_string(stop)) + ']', exp); + chain->items.push_back(slice); + auto nil = toAst("nil"sv, slice); + pairs.push_back({exp, + varName, + chain, + nil.get()}); + break; + } default: YUEE("AST node mismatch", pair); break; } } @@ -5706,6 +5763,45 @@ private: } } + bool transformChainEndWithSlice(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { + auto x = chainList.front(); + if (ast_is(chainList.back())) { + auto comp = x->new_ptr(); + { + auto chainValue = x->new_ptr(); + for (auto item : chainList) { + chainValue->items.push_back(item); + } + auto itemVar = getUnusedName("_item_"sv); + auto expCode = YueFormat{}.toString(chainValue); + auto compCode = '[' + itemVar + " for "s + itemVar + " in *"s + expCode + ']'; + comp.set(toAst(compCode, x)); + } + switch (usage) { + case ExpUsage::Assignment: { + auto simpleValue = x->new_ptr(); + simpleValue->value.set(comp); + auto exp = newExp(simpleValue, x); + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + assignment->action.set(assign); + transformAssignment(assignment, out); + break; + } + case ExpUsage::Return: + transformComprehension(comp, out, ExpUsage::Return); + break; + default: + transformComprehension(comp, out, ExpUsage::Closure); + break; + } + return true; + } + return false; + } + bool transformChainEndWithEOP(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { auto x = chainList.front(); if (ast_is(chainList.back())) { @@ -6369,6 +6465,93 @@ private: } return; } + break; + } + case id(): { + auto rIndex = static_cast(*it); + auto current = it; + auto prevChain = x->new_ptr(); + for (auto i = chainList.begin(); i != current; ++i) { + prevChain->items.push_back(*i); + } + auto var = singleVariableFrom(prevChain, AccessType::None); + if (!var.empty() && isLocal(var)) { + auto indexNode = toAst('#' + var, rIndex); + if (rIndex->modifier) { + auto opValue = rIndex->new_ptr(); + opValue->op.set(toAst("-"sv, rIndex)); + opValue->pipeExprs.dup(rIndex->modifier->pipeExprs); + indexNode->opValues.push_back(opValue); + indexNode->opValues.dup(rIndex->modifier->opValues); + indexNode->nilCoalesed.set(rIndex->modifier->nilCoalesed); + } + prevChain->items.push_back(indexNode); + auto next = current; + ++next; + for (auto i = next; i != chainList.end(); ++i) { + prevChain->items.push_back(*i); + } + if (usage == ExpUsage::Assignment) { + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + auto assign = x->new_ptr(); + assign->values.push_back(newExp(prevChain, x)); + assignment->action.set(assign); + transformAssignment(assignment, out); + return; + } + transformChainValue(prevChain, out, usage, assignList); + return; + } else { + auto itemVar = getUnusedName("_item_"sv); + auto asmt = assignmentFrom(toAst(itemVar, x), newExp(prevChain, x), x); + auto stmt1 = x->new_ptr(); + stmt1->content.set(asmt); + auto newChain = x->new_ptr(); + newChain->items.push_back(toAst(itemVar, x)); + auto indexNode = toAst('#' + itemVar, rIndex); + if (rIndex->modifier) { + auto opValue = rIndex->new_ptr(); + opValue->op.set(toAst("-"sv, rIndex)); + opValue->pipeExprs.dup(rIndex->modifier->pipeExprs); + indexNode->opValues.push_back(opValue); + indexNode->opValues.dup(rIndex->modifier->opValues); + indexNode->nilCoalesed.set(rIndex->modifier->nilCoalesed); + } + newChain->items.push_back(indexNode); + auto next = current; + ++next; + for (auto i = next; i != chainList.end(); ++i) { + newChain->items.push_back(*i); + } + auto expList = x->new_ptr(); + expList->exprs.push_back(newExp(newChain, x)); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto stmt2 = x->new_ptr(); + stmt2->content.set(expListAssign); + auto block = x->new_ptr(); + block->statements.push_back(stmt1); + block->statements.push_back(stmt2); + auto body = x->new_ptr(); + body->content.set(block); + auto doNode = x->new_ptr(); + doNode->body.set(body); + if (usage == ExpUsage::Assignment) { + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + auto assign = x->new_ptr(); + auto sVal = x->new_ptr(); + sVal->value.set(doNode); + assign->values.push_back(newExp(sVal, x)); + assignment->action.set(assign); + transformAssignment(assignment, out); + return; + } + transformDo(doNode, out, usage); + return; + } + break; } } } @@ -6887,6 +7070,9 @@ private: if (transformChainEndWithColonItem(chainList, out, usage, assignList)) { return; } + if (transformChainEndWithSlice(chainList, out, usage, assignList)) { + return; + } transformChainList(chainList, out, usage, assignList); } @@ -8107,7 +8293,7 @@ private: if (stopValue.empty()) { _buf << "#"sv << listVar; } else { - _buf << maxVar << " < 0 and #"sv << listVar << " + "sv << maxVar << " or "sv << maxVar; + _buf << maxVar << " < 0 and #"sv << listVar << " + "sv << maxVar << " + 1 or "sv << maxVar; } if (!stepValue.empty()) { _buf << ", "sv << stepValue; @@ -10059,24 +10245,24 @@ private: str_list rets; pushScope(); auto okVar = getUnusedName("_ok_"sv); - for (size_t i = 0; i < assignList->exprs.size(); i++) { - auto retVar = getUnusedName("_ret_"sv); - rets.emplace_back(retVar); - addToScope(retVar); - } - popScope(); - auto varList = join(rets, ","sv); - auto ifNode = toAst("if "s + okVar + ',' + varList + ":=try nil then "s + varList, x); - auto exp = ast_to(ifNode->nodes.front())->assignment->assign->values.front(); - auto sVal = simpleSingleValueFrom(exp); - auto newTry = sVal->value.to(); - newTry->func.set(tryNode->func); - newTry->catchBlock.set(tryNode->catchBlock); - auto assignment = x->new_ptr(); - assignment->expList.set(assignList); - auto assign = x->new_ptr(); - assign->values.push_back(ifNode); - assignment->action.set(assign); + for (size_t i = 0; i < assignList->exprs.size(); i++) { + auto retVar = getUnusedName("_ret_"sv); + rets.emplace_back(retVar); + addToScope(retVar); + } + popScope(); + auto varList = join(rets, ","sv); + auto ifNode = toAst("if "s + okVar + ',' + varList + ":=try nil then "s + varList, x); + auto exp = ast_to(ifNode->nodes.front())->assignment->assign->values.front(); + auto sVal = simpleSingleValueFrom(exp); + auto newTry = sVal->value.to(); + newTry->func.set(tryNode->func); + newTry->catchBlock.set(tryNode->catchBlock); + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + auto assign = x->new_ptr(); + assign->values.push_back(ifNode); + assignment->action.set(assign); transformAssignment(assignment, out); return; } @@ -10084,12 +10270,12 @@ private: auto okVar = getUnusedName("_ok_"sv); auto code = "do\n\t"s + okVar + ", ... = try nil\n\t... if "s + okVar; auto doNode = toAst(code, x); - auto block = doNode->body->content.to(); - auto asmt = static_cast(block->statements.front())->content.to(); - auto assign = asmt->action.to(); - auto sVal = simpleSingleValueFrom(assign->values.back()); - auto newTry = sVal->value.to(); - newTry->func.set(tryNode->func); + auto block = doNode->body->content.to(); + auto asmt = static_cast(block->statements.front())->content.to(); + auto assign = asmt->action.to(); + auto sVal = simpleSingleValueFrom(assign->values.back()); + auto newTry = sVal->value.to(); + newTry->func.set(tryNode->func); newTry->catchBlock.set(tryNode->catchBlock); transformDo(doNode, out, usage); return; diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index e860d55..1011a49 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -709,7 +709,8 @@ YueParser::YueParser() { chain_with_colon = +chain_item >> -colon_chain; chain_items = chain_with_colon | colon_chain; - index = '[' >> not_('[') >> space >> Exp >> space >> ']'; + index = '[' >> not_('[') >> space >> (ReversedIndex >> and_(space >> ']') | Exp) >> space >> ']'; + ReversedIndex = '#' >> space >> -('-' >> space >> Exp); chain_item = Invoke >> -ExistentialOp | DotChainItem >> -ExistentialOp | diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index 0d9db19..1057626 100644 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h @@ -365,6 +365,7 @@ private: AST_RULE(ExpOpValue); AST_RULE(Exp); AST_RULE(Callable); + AST_RULE(ReversedIndex); AST_RULE(ChainValue); AST_RULE(SimpleTable); AST_RULE(SimpleValue); -- cgit v1.2.3-55-g6feb