• R/O
  • SSH

YSLib: Commit

The YSLib project - main repository


Commit MetaInfo

Revision3db7f6204127bccc0b9960cc4a013cc6d6a21909 (tree)
Time2021-03-05 13:30:06
AuthorFrankHB <frankhb1989@gmai...>
CommiterFrankHB

Log Message

更新主分支版本: build 913 rev 10 。

Change Summary

Incremental Difference

diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/include/NPL/NPLA.h
--- a/YFramework/include/NPL/NPLA.h Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/include/NPL/NPLA.h Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA.h
1212 \ingroup NPL
1313 \brief NPLA 公共接口。
14-\version r8053
14+\version r8055
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 663
1717 \par 创建时间:
1818 2016-01-07 10:32:34 +0800
1919 \par 修改时间:
20- 2021-02-15 22:49 +0800
20+ 2021-03-01 02:53 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -1801,7 +1801,7 @@
18011801 提升项的值数据成员可能包含的引用值以满足返回值的内存安全要求。
18021802 提升可能转移或复制对象作为返回值,由一般表达式的值确定可转移:
18031803 转移条件等价可转移项,即按 NPL::IsMovable 判断。
1804-若参数是引用项,以项引用作为第二参数调用 NPL::LiftMoved 。
1804+若参数是引用项,以项引用作为第二参数调用 NPL::LiftMovedOther 。
18051805 */
18061806 YF_API void
18071807 LiftToReturn(TermNode&);
@@ -1878,6 +1878,7 @@
18781878
18791879 /*!
18801880 \pre 间接断言:参数为枝节点。
1881+\note 不访问项的值数据成员。若需返回值正确地反映规约状态,需确保为空。
18811882 \return ReductionStatus::Retained 。
18821883 \since build 823
18831884 */
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/include/NPL/NPLA1.h
--- a/YFramework/include/NPL/NPLA1.h Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/include/NPL/NPLA1.h Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1.h
1212 \ingroup NPL
1313 \brief NPLA1 公共接口。
14-\version r8499
14+\version r8516
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 472
1717 \par 创建时间:
1818 2014-02-02 17:58:24 +0800
1919 \par 修改时间:
20- 2021-02-02 09:06 +0800
20+ 2021-03-01 05:22 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -620,6 +620,22 @@
620620 ImplRet((term.Value = ValueObject(std::allocator_arg, term.get_allocator(),
621621 ValueToken::Unspecified)), ReductionStatus::Clean)
622622
623+/*!
624+\brief 规约到引用列表。
625+\note 不访问项的值数据成员。若需返回值正确地反映规约状态,需确保为空。
626+\return ReductionStatus::Retained
627+\since build 913
628+
629+检查第三参数指定的项表示的对象语言中的列表或列表的引用值,
630+ 处理其中的元素构造列表到第一参数中的子项。
631+结果中的列表元素是对应源列表的元素。
632+若源列表为左值,则结果中的列表元素是对应的左值引用值;
633+否则,结果中的列表元素从源列表中的元素复制初始化(按标签可发生转移)。
634+第三参数指定的源列表可能被第一参数所有。
635+*/
636+YF_API ReductionStatus
637+ReduceToReferenceList(TermNode&, ContextNode&, TermNode&);
638+
623639
624640 /*!
625641 \brief 设置跟踪深度节点:调用规约时显示深度和上下文等信息。
@@ -1166,6 +1182,9 @@
11661182 若发生 ContextHandler 调用,调用前先转移处理器保证生存期,
11671183 以允许处理器内部移除或修改之前占用的第一个子项(包括其中的 Value 数据成员)。
11681184 在被规约的项没有取得范式时,标记临时对象标签以允许转移作为操作符的函数右值。
1185+成功调用后,第一个子项指定的合并子被转移到第一参数以外的位置保存。
1186+这允许简化之后的项的处理,如可假定合并子对象可能所有的项不是第一参数的子项,
1187+ 可使用 TermNode::SetContent 代替 LiftOther 提升项。
11691188 */
11701189 //@{
11711190 YF_API ReductionStatus
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/include/NPL/NPLA1Forms.h
--- a/YFramework/include/NPL/NPLA1Forms.h Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/include/NPL/NPLA1Forms.h Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Forms.h
1212 \ingroup NPL
1313 \brief NPLA1 语法形式。
14-\version r7823
14+\version r7844
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 882
1717 \par 创建时间:
1818 2020-02-15 11:19:21 +0800
1919 \par 修改时间:
20- 2021-02-19 22:20 +0800
20+ 2021-03-01 18:47 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -731,6 +731,17 @@
731731 FirstAt(TermNode&);
732732
733733 /*!
734+\since build 913
735+
736+结果是列表的第一个元素经过转发的引用值。保留结果中的引用值。
737+
738+参考调用文法:
739+<pre>first% \<list></pre>
740+*/
741+YF_API ReductionStatus
742+FirstFwd(TermNode&);
743+
744+/*!
734745 结果是列表的第一个元素引用的对象的引用值。保留结果中的引用值。
735746
736747 参考调用文法:
@@ -750,28 +761,29 @@
750761 FirstVal(TermNode&);
751762 //@}
752763
753-/*!
754-\brief 取列表第一个元素以外的元素值构成的列表。
755-\since build 910
756-*/
764+//! \brief 取列表第一个元素以外的元素值构成的列表。
757765 //@{
758766 /*!
767+\since build 913
768+
759769 结果是列表第一个元素以外的元素值经过转发的值构成的列表。保留结果中的引用值。
760770
761771 参考调用文法:
762772 <pre>rest% \<list></pre>
763773 */
764774 YF_API ReductionStatus
765-Rest(TermNode&);
775+RestFwd(TermNode&);
766776
767777 /*!
778+\since build 913
779+
768780 结果是列表第一个元素以外的元素值的引用值构成的列表。保留结果中的引用值。
769781
770782 参考调用文法:
771783 <pre>rest& \<list></pre>
772784 */
773785 YF_API ReductionStatus
774-RestRef(TermNode&);
786+RestRef(TermNode&, ContextNode&);
775787
776788 /*!
777789 结果是列表的第一个元素以外的元素经过返回值转换的值构成的列表。不保留结果中的引用值。
@@ -1220,7 +1232,7 @@
12201232 //@}
12211233
12221234
1223-//! \since build 859
1235+//! \since build 913
12241236 //@{
12251237 /*!
12261238 \brief 包装合并子为应用子。
@@ -1230,7 +1242,7 @@
12301242 <pre>wrap \<combiner></pre>
12311243 */
12321244 YF_API ReductionStatus
1233-Wrap(TermNode&, ContextNode&);
1245+Wrap(TermNode&);
12341246
12351247 /*!
12361248 \brief 包装合并子为应用子。
@@ -1242,7 +1254,7 @@
12421254 <pre>wrap& \<combiner></pre>
12431255 */
12441256 YF_API ReductionStatus
1245-WrapRef(TermNode&, ContextNode&);
1257+WrapRef(TermNode&);
12461258
12471259 //! \exception TypeError 应用子参数的类型不符合要求。
12481260 //@{
@@ -1253,7 +1265,7 @@
12531265 <pre>wrap1 \<operative></pre>
12541266 */
12551267 YF_API ReductionStatus
1256-WrapOnce(TermNode&, ContextNode&);
1268+WrapOnce(TermNode&);
12571269
12581270 /*!
12591271 \brief 包装操作子为应用子。
@@ -1264,17 +1276,16 @@
12641276 <pre>wrap1& \<operative></pre>
12651277 */
12661278 YF_API ReductionStatus
1267-WrapOnceRef(TermNode&, ContextNode&);
1279+WrapOnceRef(TermNode&);
12681280
12691281 /*!
12701282 \brief 解包装应用子为合并子。
1271-\since build 858
12721283
12731284 参考调用文法:
12741285 <pre>unwrap \<applicative></pre>
12751286 */
12761287 YF_API ReductionStatus
1277-Unwrap(TermNode&, ContextNode&);
1288+Unwrap(TermNode&);
12781289 //@}
12791290 //@}
12801291
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/include/NPL/SContext.h
--- a/YFramework/include/NPL/SContext.h Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/include/NPL/SContext.h Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file SContext.h
1212 \ingroup NPL
1313 \brief S 表达式上下文。
14-\version r3817
14+\version r3853
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 304
1717 \par 创建时间:
1818 2012-08-03 19:55:41 +0800
1919 \par 修改时间:
20- 2021-02-06 22:59 +0800
20+ 2021-03-01 22:45 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -371,6 +371,21 @@
371371 PDefH(void, ClearContainer, ) ynothrow
372372 ImplExpr(container.clear())
373373
374+ /*!
375+ \note 允许被参数中被复制的对象直接或间接地被目标引用。
376+ \since build 913
377+ */
378+ //@{
379+ PDefH(void, CopyContainer, const TermNode& node)
380+ ImplExpr(GetContainerRef() = Container(node.GetContainer()))
381+
382+ PDefH(void, CopyContent, const TermNode& node)
383+ ImplExpr(SetContent(TermNode(node)))
384+
385+ PDefH(void, CopyValue, const TermNode& node)
386+ ImplExpr(Value = ValueObject(node.Value))
387+ //@}
388+
374389 private:
375390 static TermNode::Container
376391 ConCons(const ValueNode::Container&);
@@ -424,16 +439,41 @@
424439 }
425440
426441 /*!
427- \brief 转移参数内容。
428- \pre 断言:参数不是 *this 。
442+ \pre 断言:参数不是 \c *this 。
443+ \note 允许被参数中被转移的对象直接或间接地被目标引用。
444+ \since build 913
445+ */
446+ //@{
447+ /*!
448+ \brief 转移容器。
449+
450+ 转移参数指定的节点的容器到对象。
451+ 转移后的节点的容器是转移前的参数的容器。
452+ */
453+ void
454+ MoveContainer(TermNode&&);
455+
456+ /*!
457+ \brief 转移内容。
429458 \since build 853
430459
431- 转移参数指定的节点的内容到对象。转移后的节点内容是转移前的参数内容。
432- 允许被转移的参数直接或间接地被容器引用。
460+ 转移参数指定的节点的内容到对象。
461+ 转移后的节点的内容是转移前的参数的内容。
433462 */
434463 void
435464 MoveContent(TermNode&&);
436465
466+ /*!
467+ \brief 转移值数据成员。
468+
469+ 转移参数指定的节点的值数据成员到对象。
470+ 转移后的节点的值数据成员是转移前的参数内容。
471+ */
472+ void
473+ MoveValue(TermNode&&);
474+ //@}
475+
476+
437477 PDefH(void, Remove, const_iterator i)
438478 ImplExpr(erase(i))
439479
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/include/YSLib/Core/ValueNode.h
--- a/YFramework/include/YSLib/Core/ValueNode.h Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/include/YSLib/Core/ValueNode.h Fri Mar 05 12:30:06 2021 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2012-2020 FrankHB.
2+ © 2012-2021 FrankHB.
33
44 This file is part of the YSLib project, and may only be used,
55 modified, and distributed under the terms of the YSLib project
@@ -11,13 +11,13 @@
1111 /*! \file ValueNode.h
1212 \ingroup Core
1313 \brief 值类型节点。
14-\version r4142
14+\version r4180
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 338
1717 \par 创建时间:
1818 2012-08-03 23:03:44 +0800
1919 \par 修改时间:
20- 2020-08-30 19:37 +0800
20+ 2021-03-01 22:33 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -363,7 +363,7 @@
363363
364364 //! \since build 768
365365 //@{
366- //! \brief 复制赋值:使用参数的分配器构造的副本和交换操作。
366+ //! \brief 复制赋值:使用参数副本和交换操作。
367367 PDefHOp(ValueNode&, =, const ValueNode& node)
368368 ImplRet(ystdex::copy_and_swap(*this, node))
369369 /*!
@@ -644,6 +644,21 @@
644644 PDefH(void, ClearContainer, ) ynothrow
645645 ImplExpr(container.clear())
646646
647+ /*!
648+ \note 允许被参数中被复制的对象直接或间接地被目标引用。
649+ \since build 913
650+ */
651+ //@{
652+ PDefH(void, CopyContainer, const ValueNode& node)
653+ ImplExpr(GetContainerRef() = Container(node.GetContainer()))
654+
655+ PDefH(void, CopyContent, const ValueNode& node)
656+ ImplExpr(SetContent(ValueNode(node)))
657+
658+ PDefH(void, CopyValue, const ValueNode& node)
659+ ImplExpr(Value = ValueObject(node.Value))
660+ //@}
661+
647662 //! \brief 递归创建容器副本。
648663 //@{
649664 //! \since build 767
@@ -698,17 +713,41 @@
698713 //@}
699714
700715 /*!
701- \brief 转移参数内容。
702- \pre 断言:参数不是 *this 。
716+ \pre 断言:参数不是 \c *this 。
717+ \note 允许被参数中被转移的对象直接或间接地被目标引用。
718+ \since build 913
719+ */
720+ //@{
721+ /*!
722+ \brief 转移容器。
723+
724+ 转移参数指定的节点的容器到对象。
725+ 转移后的节点的容器是转移前的参数的容器。
726+ */
727+ void
728+ MoveContainer(ValueNode&&);
729+
730+ /*!
731+ \brief 转移内容。
703732 \since build 844
704733
705- 转移参数指定的节点的内容到对象。转移后的节点内容是转移前的参数内容。
706- 允许被转移的参数直接或间接地被容器引用。
734+ 转移参数指定的节点的内容到对象。
735+ 转移后的节点的内容是转移前的参数的内容。
707736 */
708737 void
709738 MoveContent(ValueNode&&);
710739
711740 /*!
741+ \brief 转移值数据成员。
742+
743+ 转移参数指定的节点的值数据成员到对象。
744+ 转移后的节点的值数据成员是转移前的参数内容。
745+ */
746+ void
747+ MoveValue(ValueNode&&);
748+ //@}
749+
750+ /*!
712751 \brief 若指定名称子节点不存在则按指定值初始化。
713752 \since build 781
714753 */
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/source/NPL/Dependency.cpp
--- a/YFramework/source/NPL/Dependency.cpp Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/source/NPL/Dependency.cpp Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file Dependency.cpp
1212 \ingroup NPL
1313 \brief 依赖管理。
14-\version r4288
14+\version r4355
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 623
1717 \par 创建时间:
1818 2015-08-09 22:14:45 +0800
1919 \par 修改时间:
20- 2021-02-23 00:22 +0800
20+ 2021-03-05 01:14 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -38,11 +38,12 @@
3838 // IsModifiableTerm, IsTemporaryTerm, NPL::TryAccessLeaf, LiftTermRef,
3939 // NPL::SetContentWith, Forms::CallRawUnary, LiftTerm, LiftOtherOrCopy,
4040 // std::placeholders, LiftTermValueOrCopy, ystdex::bind1, MoveResolved,
41-// ResolveIdentifier, NPL::IsMovable, LiftTermOrCopy, IsBranchedList,
42-// AccessFirstSubterm, ReferenceTerm, ThrowInsufficientTermsError,
43-// ystdex::exchange, ystdex::plus, ystdex::tolower, YSLib::IO::StreamPut,
44-// FetchEnvironmentVariable, ystdex::swap_dependent, YSLib::IO::UniqueFile,
45-// YSLib::IO::omode_convb, YSLib::uremove, ystdex::throw_error;
41+// ResolveIdentifier, ReduceToReferenceList, NPL::IsMovable, LiftTermOrCopy,
42+// IsBranchedList, AccessFirstSubterm, ReferenceTerm,
43+// ThrowInsufficientTermsError, ystdex::exchange, ystdex::plus,
44+// ystdex::tolower, YSLib::IO::StreamPut, FetchEnvironmentVariable,
45+// ystdex::swap_dependent, YSLib::IO::UniqueFile, YSLib::IO::omode_convb,
46+// YSLib::uremove, ystdex::throw_error;
4647 #include YFM_NPL_NPLA1Forms // for NPL::Forms functions;
4748 #include YFM_YSLib_Service_FileSystem // for YSLib::IO::Path;
4849 #include <ystdex/iterator.hpp> // for std::istreambuf_iterator,
@@ -627,6 +628,7 @@
627628 #endif
628629 #if NPL_Impl_NPLA1_Native_Forms
629630
631+ using namespace std::placeholders;
630632 const auto idv([](TermNode& term){
631633 return DoIdFunc(ReduceForLiftedResult, term);
632634 });
@@ -641,6 +643,11 @@
641643 RegisterStrict(renv, "idv", idv);
642644 RegisterStrict(renv, "list", ReduceBranchToListValue);
643645 RegisterStrict(renv, "list%", ReduceBranchToList);
646+ RegisterStrict(renv, "rlist", [](TermNode& term, ContextNode& ctx){
647+ return Forms::CallRawUnary([&](TermNode& tm){
648+ return ReduceToReferenceList(term, ctx, tm);
649+ }, term);
650+ });
644651 // NOTE: Lazy form '$deflazy!' is the basic operation, which may bind
645652 // parameter as unevaluated operands.
646653 RegisterForm(renv, "$deflazy!", DefineLazy);
@@ -650,6 +657,26 @@
650657 RegisterForm(renv, "$lambda%", LambdaRef);
651658 // NOTE: The sequence operator is also available as infix ';' syntax sugar.
652659 RegisterForm(renv, "$sequence", Sequence);
660+ RegisterForm(renv, "$lvalue-identifier?",
661+ [](TermNode& term, ContextNode& ctx){
662+ Forms::CallRegularUnaryAs<const TokenValue>([&](string_view id){
663+ term.Value = CheckSymbol(id, [&]() -> bool{
664+ auto pr(ResolveName(ctx, id));
665+
666+ if(pr.first)
667+ {
668+ auto& tm(*pr.first);
669+
670+ if(const auto p
671+ = NPL::TryAccessLeaf<const TermReference>(tm))
672+ return p->IsReferencedLValue();
673+ return !(bool(tm.Tags & TermTags::Unique)
674+ || bool(tm.Tags & TermTags::Temporary));
675+ }
676+ throw BadIdentifier(id);
677+ });
678+ }, term);
679+ });
653680 RegisterStrict(renv, "collapse", [](TermNode& term){
654681 return Forms::CallRawUnary([&](TermNode& tm){
655682 MoveCollapsed(term, tm);
@@ -672,6 +699,9 @@
672699 RegisterBinary<>(renv, "assign!", [](TermNode& x, TermNode& y){
673700 return DoAssign([&](TermNode& nd_x){
674701 ResolveTerm([&](TermNode& nd_y, ResolvedTermReferencePtr p_ref_y){
702+ // NOTE: Self-assignment is not checked directly. This allows
703+ // copy assignment to fail as expected. Anyway, the destination
704+ // is not modified if the source copy fails.
675705 LiftTermOrCopy(nd_x, nd_y, NPL::IsMovable(p_ref_y));
676706 }, y);
677707 }, x);
@@ -682,9 +712,10 @@
682712 RegisterStrict(renv, "forward-first%", ForwardFirst);
683713 RegisterStrict(renv, "first", First);
684714 RegisterStrict(renv, "first@", FirstAt);
715+ RegisterStrict(renv, "first%", FirstFwd);
685716 RegisterStrict(renv, "first&", FirstRef);
686717 RegisterStrict(renv, "firstv", FirstVal);
687- RegisterStrict(renv, "rest%", Rest);
718+ RegisterStrict(renv, "rest%", RestFwd);
688719 RegisterStrict(renv, "rest&", RestRef);
689720 RegisterStrict(renv, "restv", RestVal);
690721 // NOTE: Like 'set-car!' in Kernel, with no references.
@@ -782,10 +813,11 @@
782813 // (first-class referents) from prvalues and all terms can be accessed as
783814 // objects with arbitrary longer lifetime.
784815 R"NPL(
785- $def! id wrap ($vau% (%x) #ignore $if (bound-lvalue? x) x (move! x));
816+ $def! id wrap ($vau% (%x) #ignore $move-resolved! x);
786817 $def! idv wrap $quote;
787- $def! list wrap ($vau (.x) #ignore x);
818+ $def! list wrap ($vau (.x) #ignore move! x);
788819 $def! list% wrap ($vau &x #ignore x);
820+ $def! rlist wrap ($vau ((.&x)) #ignore move! x);
789821 )NPL"
790822 # else
791823 );
@@ -793,10 +825,11 @@
793825 RegisterForm(renv, "$lambda%", LambdaRef);
794826 context.ShareCurrentSource("<root:basic-derived-1>");
795827 context.Perform(R"NPL(
796- $def! id $lambda% (%x) $if (bound-lvalue? x) x (move! x);
828+ $def! id $lambda% (%x) $move-resolved! x;
797829 $def! idv $lambda (&x) x;
798- $def! list $lambda (.x) x;
830+ $def! list $lambda (.x) move! x;
799831 $def! list% $lambda &x x;
832+ $def! rlist $lambda ((.&x)) move! x;
800833 )NPL"
801834 # endif
802835 // XXX: The operative '$defv!' is same to following derivations in
@@ -838,12 +871,12 @@
838871 eval (list $set! d f $lambda formals (move! body)) d;
839872 $defv! $defl%! (&f &formals .&body) d
840873 eval (list $set! d f $lambda% formals (move! body)) d;
874+ $defv! $lvalue-identifier? (&s) d
875+ eval (list bound-lvalue? (list $resolve-identifier s)) d;
841876 $defl%! collapse (%x)
842877 $if (uncollapsed? ($resolve-identifier x)) (idv x) x;
843- $defl%! forward (%x)
844- $if (bound-lvalue? ($resolve-identifier x)) x (idv x);
845- $defl%! forward! (%x)
846- $if (bound-lvalue? ($resolve-identifier x)) x (move! x);
878+ $defl%! forward (%x) $if ($lvalue-identifier? x) x (idv x);
879+ $defl%! forward! (%x) $if ($lvalue-identifier? x) x (move! x);
847880 $defl! assign! (&x &y) assign@! (forward! x) (idv (collapse y));
848881 $defl! assign%! (&x &y) assign@! (forward! x) (forward! (collapse y));
849882 $defl%! apply (&appv &arg .&opt)
@@ -861,21 +894,24 @@
861894 eval (list $set! d f wrap (list* $vau% formals ef (move! body))) d;
862895 $defw%! forward-first% (&appv (&x .)) d
863896 apply (forward! appv) (list% ($move-resolved! x)) d;
864- $defl%! first@ (&l) ($lambda% ((@x .)) x) (check-list-reference l);
865897 $defl%! first (%l)
866898 ($lambda% (fwd) forward-first% forward! (fwd l))
867- ($if (bound-lvalue? ($resolve-identifier l)) id expire);
868- $defl%! first& (&l) ($lambda% ((&x .)) x) (check-list-reference l);
899+ ($if ($lvalue-identifier? l) id expire);
900+ $defl%! first@ (&l)
901+ ($lambda% ((@x .)) x) (check-list-reference (forward! l));
902+ $defl%! first% (%l)
903+ ($lambda% (fwd (@x .)) idv (fwd x))
904+ ($if ($lvalue-identifier? l) id expire) l;
905+ $defl%! first& (&l)
906+ ($lambda% ((&x .)) x) (check-list-reference (forward! l));
869907 $defl! firstv ((&x .)) x;
870908 $defl! rest% ((#ignore .%x)) move! x;
871- $defl! rest& (&l) ($lambda ((#ignore .&x)) x) (check-list-reference l);
909+ $defl%! rest& (&l)
910+ ($lambda% ((#ignore .&x)) x) (check-list-reference (forward! l));
872911 $defl! restv ((#ignore .x)) move! x;
873- $defl! set-first! (&l x)
874- assign@! (first@ (check-list-reference l)) (move! x);
875- $defl! set-first@! (&l &x)
876- assign@! (first@ (check-list-reference l)) (forward! x);
877- $defl! set-first%! (&l &x)
878- assign%! (first@ (check-list-reference l)) (forward! x);
912+ $defl! set-first! (&l x) assign@! (first@ (forward! l)) (move! x);
913+ $defl! set-first@! (&l &x) assign@! (first@ (forward! l)) (forward! x);
914+ $defl! set-first%! (&l &x) assign%! (first@ (forward! l)) (forward! x);
879915 $defl! equal? (&x &y) $if ($if (branch? x) (branch? y) #f)
880916 ($if (equal? (first& x) (first& y)) (equal? (rest& x) (rest& y)) #f)
881917 (eqv? x y);
@@ -897,12 +933,12 @@
897933 $defl! not? (&x) eqv? x #f;
898934 $defv%! $and? &x d $cond
899935 ((null? x) #t)
900- ((nullv? (rest& x)) eval% (first (forward! x)) d)
936+ ((null? (rest& x)) eval% (first (forward! x)) d)
901937 ((eval% (first& x) d) apply (wrap $and?) (rest% (forward! x)) d)
902938 (#t #f);
903939 $defv%! $or? &x d $cond
904940 ((null? x) #f)
905- ((nullv? (rest& x)) eval% (first (forward! x)) d)
941+ ((null? (rest& x)) eval% (first (forward! x)) d)
906942 (#t ($lambda% (&r) $if r (forward! r) (apply (wrap $or?)
907943 (rest% (forward! x)) d)) (eval% (move! (first& x)) d));
908944 $defw%! accl (&l &pred? &base &head &tail &sum) d
@@ -917,7 +953,7 @@
917953 pred? (forward! base) head tail sum) d)) d);
918954 $defw%! foldr1 (&kons &knil &l) d
919955 apply accr
920- (list% (forward! l) null? (forward! knil) first rest% kons) d;
956+ (list% (forward! l) null? (forward! knil) first% rest% kons) d;
921957 $defw%! map1 (&appv &l) d
922958 foldr1 ($lambda (&x &xs) cons%
923959 (apply appv (list% (forward! x)) d) xs) () (forward! l);
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/source/NPL/NPLA.cpp
--- a/YFramework/source/NPL/NPLA.cpp Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/source/NPL/NPLA.cpp Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA.cpp
1212 \ingroup NPL
1313 \brief NPLA 公共接口。
14-\version r3477
14+\version r3490
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 663
1717 \par 创建时间:
1818 2016-01-07 10:32:45 +0800
1919 \par 修改时间:
20- 2021-01-25 22:33 +0800
20+ 2021-03-01 22:47 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -647,7 +647,7 @@
647647 TermNode
648648 PrepareCollapse(TermNode& term, const shared_ptr<Environment>& p_env)
649649 {
650- if(const auto p = NPL::TryAccessLeaf<const TermReference>(term))
650+ if(term.Value.type() == ystdex::type_id<const TermReference>())
651651 return term;
652652 return NPL::AsTermNode(term.get_allocator(),
653653 TermReference(p_env->MakeTermTags(term), term, NPL::Nonnull(p_env)));
@@ -737,7 +737,12 @@
737737 if(move)
738738 LiftOther(term, tm);
739739 else
740- term.SetContent(tm);
740+ // NOTE: Although %tm is required to be not same to %term, it can still
741+ // be an object exclusively owned by %term, e.g. when %term represents
742+ // a reference value with irregular representation and %tm is the term
743+ // referenced uniquely by %term.Value. Explicit copy is necessary for
744+ // such cases to avoid invalidate subterm reference prematurely.
745+ term.CopyContent(tm);
741746 }
742747
743748 void
@@ -747,7 +752,7 @@
747752 if(move)
748753 LiftTerm(term, tm);
749754 else
750- term.MoveContent(TermNode(tm));
755+ term.CopyContent(tm);
751756 }
752757
753758 void
@@ -769,7 +774,7 @@
769774 auto pr(Collapse(std::move(ref)));
770775
771776 if(!ystdex::ref_eq<>()(term, tm))
772- term.MoveContent(TermNode(std::move(tm.GetContainerRef()),
777+ term.SetContent(TermNode(std::move(tm.GetContainerRef()),
773778 std::move(pr.first)));
774779 else if(pr.second)
775780 term.Value = std::move(pr.first);
@@ -779,7 +784,7 @@
779784 MoveCollapsed(TermNode& term, TermNode& tm)
780785 {
781786 if(const auto p = NPL::TryAccessLeaf<TermReference>(tm))
782- term.MoveContent(TermNode(std::move(tm.GetContainerRef()),
787+ term.SetContent(TermNode(std::move(tm.GetContainerRef()),
783788 Collapse(std::move(*p)).first));
784789 else
785790 LiftOther(term, tm);
@@ -815,7 +820,9 @@
815820 // the operation here is idempotent for qualified expressions.
816821 // TODO: Detect lifetime escape to perform copy elision?
817822 if(const auto p = NPL::TryAccessLeaf<const TermReference>(term))
818- LiftMoved(term, *p, p->IsMovable());
823+ // XXX: Using %LiftMovedOther instead of %LiftMoved is safe, because the
824+ // referent is not allowed to be same to %term in NPLA.
825+ LiftMovedOther(term, *p, p->IsMovable());
819826 }
820827
821828 void
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/source/NPL/NPLA1.cpp
--- a/YFramework/source/NPL/NPLA1.cpp Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/source/NPL/NPLA1.cpp Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1.cpp
1212 \ingroup NPL
1313 \brief NPLA1 公共接口。
14-\version r20326
14+\version r20407
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 473
1717 \par 创建时间:
1818 2014-02-02 18:02:47 +0800
1919 \par 修改时间:
20- 2021-02-17 02:51 +0800
20+ 2021-03-01 19:58 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -500,6 +500,7 @@
500500 if(sigil != '@')
501501 {
502502 const bool can_modify(!bool(o_tags & TermTags::Nonmodifying));
503+ const auto a(o.get_allocator());
503504
504505 // NOTE: Subterms in arguments retained are also transferred for
505506 // values.
@@ -515,11 +516,13 @@
515516 if(can_modify && temp)
516517 // NOTE: Reference collapsed by move.
517518 mv(std::move(o.GetContainerRef()),
518- TermReference(ref_tags, std::move(*p)));
519+ ValueObject(std::allocator_arg, a, in_place_type<
520+ TermReference>, ref_tags, std::move(*p)));
519521 else
520522 // NOTE: Reference collapsed by copy.
521523 mv(TermNode::Container(o.GetContainer()),
522- TermReference(ref_tags, *p));
524+ ValueObject(std::allocator_arg, a,
525+ in_place_type<TermReference>, ref_tags, *p));
523526 }
524527 else
525528 {
@@ -562,7 +565,7 @@
562565 // ignored normally except for future internal use. Note
563566 // that %TermTags::Temporary can be provided by a bound
564567 // object (indicated by %o) in an environment.
565- ValueObject(std::allocator_arg, o.get_allocator(),
568+ ValueObject(std::allocator_arg, a,
566569 in_place_type<TermReference>,
567570 GetLValueTagsOf(o.Tags | o_tags), o, Referenced));
568571 else
@@ -1033,6 +1036,66 @@
10331036 }
10341037
10351038
1039+ReductionStatus
1040+ReduceToReferenceList(TermNode& term, ContextNode& ctx, TermNode& tm)
1041+{
1042+ return ResolveTerm(
1043+ [&](TermNode& nd, ResolvedTermReferencePtr p_ref) YB_FLATTEN{
1044+ if(IsList(nd))
1045+ {
1046+ // XXX: As %BindParameterObject.
1047+ const bool temp(!p_ref || p_ref->IsTemporary());
1048+
1049+ if(temp || p_ref->IsMovable())
1050+ // NOTE: This allows %nd owned by %term.
1051+ term.MoveContainer(std::move(nd));
1052+ else
1053+ {
1054+ const auto a(term.get_allocator());
1055+ TermNode::Container con(a);
1056+ const bool can_modify(!p_ref || p_ref->IsModifiable());
1057+ const auto& r_env(p_ref ? p_ref->GetEnvironmentReference()
1058+ : ctx.WeakenRecord());
1059+ auto o_tags(p_ref ? p_ref->GetTags()
1060+ & (TermTags::Unique | TermTags::Nonmodifying)
1061+ : TermTags::Temporary);
1062+
1063+ // XXX: As %BindParameter.
1064+ for(auto& o : nd)
1065+ {
1066+ if(const auto p = NPL::TryAccessLeaf<TermReference>(o))
1067+ {
1068+ if(can_modify && temp)
1069+ con.emplace_back(std::move(o.GetContainerRef()),
1070+ ValueObject(std::allocator_arg, a,
1071+ in_place_type<TermReference>, std::move(*p)));
1072+ else
1073+ con.emplace_back(o.GetContainer(), ValueObject(
1074+ std::allocator_arg, a, in_place_type<
1075+ TermReference>, BindReferenceTags(*p), *p));
1076+ }
1077+ else if(can_modify && temp)
1078+ {
1079+ con.emplace_back(std::move(o.GetContainerRef()),
1080+ std::move(o.Value));
1081+ con.back().Tags |= TermTags::Temporary;
1082+ }
1083+ else
1084+ con.emplace_back(TermNode::Container(o.get_allocator()),
1085+ ValueObject(std::allocator_arg, a,
1086+ in_place_type<TermReference>,
1087+ GetLValueTagsOf(o.Tags | o_tags), o, r_env));
1088+ }
1089+ con.swap(term.GetContainerRef());
1090+ }
1091+ return ReductionStatus::Retained;
1092+ }
1093+ else
1094+ ThrowListTypeErrorForNonlist(nd, p_ref);
1095+ }, tm);
1096+}
1097+
1098+
10361099 void
10371100 SetupTraceDepth(ContextState& cs, const string& name)
10381101 {
@@ -1300,6 +1363,7 @@
13001363 if(const auto p = NPL::TryAccessLeaf<const TermReference>(bound))
13011364 {
13021365 p_rterm = &p->get();
1366+ // XXX: It is assumed that %term is not an ancestor of %bound.
13031367 term.SetContent(bound.GetContainer(),
13041368 EnsureLValueReference(TermReference(*p)));
13051369 }
@@ -1359,6 +1423,8 @@
13591423 auto& fm(AccessFirstSubterm(term));
13601424 const auto p_ref_fm(NPL::TryAccessLeaf<const TermReference>(fm));
13611425
1426+ // NOTE: If this call returns normally, the combiner object implied by %fm
1427+ // is not owned by %term.
13621428 // XXX: %SetupNextTerm is to be called in %CombinerReturnThunk.
13631429 // NOTE: The tag is intended to be consumed by %VauHandler in %NPLA1Forms.
13641430 // As the irregular representation has a handler of %RefContextHandler,
@@ -1389,14 +1455,15 @@
13891455 YAssert(ystdex::ref_eq<>()(NPL::Deref(NPL::Access<
13901456 shared_ptr<TermNode>>(*fm.begin())), referenced),
13911457 "Invalid subobject reference found.");
1392- // XXX: %TermNode::SetContent cannot be used here due to subterm
1393- // invalidation.
1394- fm.MoveContent(TermNode(referenced));
1458+ // XXX: Explicit copy is necessary as in the implementation of
1459+ // %LiftOtherOrCopy.
1460+ fm.CopyContent(referenced);
13951461 YAssert(IsLeaf(fm), "Wrong result of irregular"
13961462 " representation conversion found.");
13971463 }
13981464 else
13991465 #endif
1466+ // NOTE: The combiner object is in an lvalue. It is not saved by %term.
14001467 if(const auto p_handler
14011468 = NPL::TryAccessLeaf<const ContextHandler>(p_ref_fm->get()))
14021469 // NOTE: This is neutral to %NPL_Impl_NPLA1_Enable_Thunked.
@@ -1404,8 +1471,12 @@
14041471 }
14051472 else
14061473 term.Tags |= TermTags::Temporary;
1407- // NOTE: Converted terms are also handled here.
1408- // NOTE: To allow being moved, this shall be not qualified by 'const'.
1474+ // NOTE: The combiner object is in a prvalue. It will be moved away from
1475+ // the combining term (i.e. %term) by the call to %CombinerReturnThunk.
1476+ // The implementation varies on different configurations.
1477+ // XXX: Converted terms (if used, see above) are also handled here as in
1478+ // prvalues.
1479+ // NOTE: To allow being moved, %p_handler is not qualified by 'const'.
14091480 if(const auto p_handler = NPL::TryAccessTerm<ContextHandler>(fm))
14101481 #if NPL_Impl_NPLA1_Enable_TCO
14111482 return
@@ -1532,8 +1603,9 @@
15321603
15331604 if(!id.empty())
15341605 {
1606+ const auto a(o_tm.get_allocator());
15351607 const auto last(o_tm.end());
1536- TermNode::Container con(t.get_allocator());
1608+ TermNode::Container con(a);
15371609
15381610 // XXX: As %TermReference::IsMovable for non-temporary objects.
15391611 if((o_tags & (TermTags::Unique | TermTags::Nonmodifying))
@@ -1563,7 +1635,10 @@
15631635 });
15641636 if(sigil == '&')
15651637 {
1566- const auto a(o_tm.get_allocator());
1638+ // NOTE: Irregular representation is constructed for the
1639+ // list subobject reference.
1640+ // XXX: As %ReduceAsSubobjectReference in
1641+ // NPLA1Internals.
15671642 auto p_sub(YSLib::allocate_shared<TermNode>(a,
15681643 std::move(con)));
15691644 auto& sub(NPL::Deref(p_sub));
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/source/NPL/NPLA1Forms.cpp
--- a/YFramework/source/NPL/NPLA1Forms.cpp Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/source/NPL/NPLA1Forms.cpp Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Forms.cpp
1212 \ingroup NPL
1313 \brief NPLA1 语法形式。
14-\version r20746
14+\version r21015
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 882
1717 \par 创建时间:
1818 2014-02-15 11:19:51 +0800
1919 \par 修改时间:
20- 2021-02-23 23:18 +0800
20+ 2021-03-03 03:15 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -884,6 +884,8 @@
884884 }
885885
886886 private:
887+ // TODO: Avoid TCO when the static environment has something with side
888+ // effects on destruction, to prevent semantic changes.
887889 //! \since build 847
888890 ReductionStatus
889891 DoCall(TermNode& term, ContextNode& ctx, EnvironmentGuard& gd) const
@@ -916,35 +918,40 @@
916918 // environment by setting the parent environment pointer.
917919 ctx.GetRecordRef().Parent = parent;
918920 AssertNextTerm(ctx, term);
919-#if NPL_Impl_NPLA1_Enable_TCO
921+
922+ // NOTE: Saved the no lift flag here to avoid branching in the
923+ // following implementation.
924+ const bool no_lift(NoLifting);
925+
926+ // NOTE: Because the only possible (non-administrative) owner of the
927+ // evaluation structure visible in the object language is the combiner
928+ // object which is not possible here in the %term, the evaluation
929+ // structure shall share nothing to the combining term being reduced
930+ // here (i.e. %term), so it is safe to use %TermNode::SetContent
931+ // instead of %LiftOther. See %ReduceCombinedBranch in NPLA1.cpp for
932+ // details.
933+ // XXX: Using %LiftOtherOrCopy is less efficient.
920934 // XXX: Assume the last function is this object.
921- // TODO: Avoid TCO when the static environment has something with side
922- // effects on destruction.
923935 if(move)
924936 {
925- TermNode eval_struct(std::move(Deref(p_eval_struct)));
937+ // NOTE: The evaluation structure does not need to be saved to the
938+ // continuation, since it would be used immediately in
939+ // the call to %RelayForCall.
940+ term.SetContent(std::move(Deref(p_eval_struct)));
941+#if NPL_Impl_NPLA1_Enable_TCO
942+
926943 auto& act(RefTCOAction(ctx));
927- const bool no_lift(NoLifting);
928944
929945 save(*this, act);
930946 // XXX: This would make '*this' invalid.
931947 yunused(act.MoveFunction());
932- // XXX: The evaluation structure does not need to be saved, since it
933- // would be used immediately in %RelayForCall. The pointer is moved
934- // to indicate the error condition when it is called again.
935- LiftOther(term, eval_struct);
936- // NOTE: The precondition is same to the last call in
937- // %EvalImplUnchecked.
938- return RelayForCall(ctx, term, std::move(gd), no_lift);
948+#endif
939949 }
940950 else
941951 term.SetContent(Deref(p_eval_struct));
942-#else
943- // XXX: Ditto.
944- LiftOtherOrCopy(term, Deref(p_eval_struct), move);
945-#endif
946- // NOTE: Ditto.
947- return RelayForCall(ctx, term, std::move(gd), NoLifting);
952+ // NOTE: The precondition is same to the last call in
953+ // %EvalImplUnchecked.
954+ return RelayForCall(ctx, term, std::move(gd), no_lift);
948955 }
949956
950957 //! \since build 909
@@ -1060,14 +1067,15 @@
10601067 h.target_type().name()));
10611068 }
10621069
1070+//! \since build 913
10631071 ReductionStatus
1064-UnwrapResolved(TermNode& term, ContextNode& ctx, FormContextHandler& fch,
1072+UnwrapResolved(TermNode& term, FormContextHandler& fch,
10651073 ResolvedTermReferencePtr p_ref)
10661074 {
10671075 if(fch.Wrapping != 0)
10681076 {
10691077 return MakeValueOrMove(p_ref, [&]{
1070- return ReduceForCombinerRef(term, ctx, NPL::Deref(p_ref),
1078+ return ReduceForCombinerRef(term, NPL::Deref(p_ref),
10711079 fch.Handler, fch.Wrapping - 1);
10721080 }, [&]{
10731081 --fch.Wrapping;
@@ -1240,22 +1248,22 @@
12401248 }
12411249 #endif
12421250
1243-//! \since build 897
1251+//! \since build 913
12441252 void
1245-ForwardToUnwrapped(TermNode& comb, ContextNode& ctx)
1253+ForwardToUnwrapped(TermNode& comb)
12461254 {
12471255 using namespace std::placeholders;
12481256
12491257 RegularizeTerm(comb, WrapUnwrapResolve(comb, std::bind(UnwrapResolved,
1250- std::ref(comb), std::ref(ctx), _1, _2), ThrowForUnwrappingFailure));
1258+ std::ref(comb), _1, _2), ThrowForUnwrappingFailure));
12511259 }
12521260
12531261 // XXX: Preserving %con is better for performance, at least in code generation
12541262 //`by x86_64-pc-linux G++ 10.2.
1255-//! \since build 899
1263+//! \since build 913
12561264 YB_ATTR_nodiscard TermNode
12571265 EvaluateBoundUnwrappedLValueDispatch(TermNode::allocator_type a,
1258- const TermNode::Container& con, TermReference ref, ContextNode& ctx,
1266+ const TermNode::Container& con, TermReference ref,
12591267 TermNode& nd)
12601268 {
12611269 auto& h(NPL::AccessRegular<ContextHandler>(nd, true));
@@ -1268,7 +1276,7 @@
12681276 {
12691277 TermNode term(std::allocator_arg, a, con);
12701278
1271- ReduceForCombinerRef(term, ctx, ref, fch.Handler, fch.Wrapping - 1);
1279+ ReduceForCombinerRef(term, ref, fch.Handler, fch.Wrapping - 1);
12721280 return term;
12731281 }
12741282 throw TypeError("Unwrapping failed on an operative argument.");
@@ -1277,16 +1285,16 @@
12771285 }
12781286
12791287 // NOTE: As %EvaluateBoundLValue and %ForwardToUnwrapped.
1280-//! \since build 899
1288+//! \since build 913
12811289 YB_ATTR_nodiscard TermNode
1282-EvaluateBoundLValueUnwrapped(TermNode& term, ContextNode& ctx,
1290+EvaluateBoundLValueUnwrapped(TermNode& term,
12831291 const shared_ptr<Environment>& p_env)
12841292 {
12851293 if(const auto p = NPL::TryAccessLeaf<TermReference>(term))
12861294 return EvaluateBoundUnwrappedLValueDispatch(
1287- term.get_allocator(), term.GetContainer(), *p, ctx, p->get());
1295+ term.get_allocator(), term.GetContainer(), *p, p->get());
12881296 return EvaluateBoundUnwrappedLValueDispatch(term.get_allocator(),
1289- {}, TermReference(term.Tags, term, NPL::Nonnull(p_env)), ctx, term);
1297+ {}, TermReference(term.Tags, term, NPL::Nonnull(p_env)), term);
12901298 }
12911299
12921300
@@ -1605,8 +1613,9 @@
16051613 return ReductionStatus::Clean;
16061614 }
16071615
1616+//! \since build 913
16081617 ReductionStatus
1609-WrapN(TermNode& term, ContextNode&, ResolvedTermReferencePtr p_ref,
1618+WrapN(TermNode& term, ResolvedTermReferencePtr p_ref,
16101619 FormContextHandler& fch, size_t n)
16111620 {
16121621 return WrapH(term, MakeValueOrMove(p_ref, [&]{
@@ -1617,12 +1626,13 @@
16171626 }));
16181627 }
16191628
1629+//! \since build 913
16201630 ReductionStatus
1621-WrapRefN(TermNode& term, ContextNode& ctx, ResolvedTermReferencePtr p_ref,
1631+WrapRefN(TermNode& term, ResolvedTermReferencePtr p_ref,
16221632 FormContextHandler& fch, size_t n)
16231633 {
16241634 if(p_ref)
1625- return ReduceForCombinerRef(term, ctx, *p_ref, fch.Handler, n);
1635+ return ReduceForCombinerRef(term, *p_ref, fch.Handler, n);
16261636 term.Value = ContextHandler(std::allocator_arg, term.get_allocator(),
16271637 FormContextHandler(std::move(fch.Handler), n));
16281638 return ReductionStatus::Clean;
@@ -1637,33 +1647,33 @@
16371647 return DispatchContextHandler(h, p_ref, f, f2);
16381648 }, term);
16391649 }
1640-
1650+//@}
1651+
1652+//! \since build 913
16411653 template<typename _func>
16421654 ReductionStatus
1643-WrapOrRef(TermNode& term, ContextNode& ctx, ReductionStatus
1644- (&fwrap)(TermNode&, ContextNode&, ResolvedTermReferencePtr,
1645- FormContextHandler&, size_t), _func f)
1655+WrapOrRef(TermNode& term, ReductionStatus(&fwrap)(TermNode&,
1656+ ResolvedTermReferencePtr, FormContextHandler&, size_t), _func f)
16461657 {
16471658 return WrapUnwrap(term,
16481659 [&](FormContextHandler& fch, ResolvedTermReferencePtr p_ref){
1649- return fwrap(term, ctx, p_ref, fch, AddWrapperCount(fch.Wrapping));
1660+ return fwrap(term, p_ref, fch, AddWrapperCount(fch.Wrapping));
16501661 }, f);
16511662 }
16521663
1664+//! \since build 913
16531665 ReductionStatus
1654-WrapOnceOrOnceRef(TermNode& term, ContextNode& ctx, ReductionStatus
1655- (&fwrap)(TermNode&, ContextNode&, ResolvedTermReferencePtr,
1656- FormContextHandler&, size_t))
1666+WrapOnceOrOnceRef(TermNode& term, ReductionStatus(&fwrap)(TermNode&,
1667+ ResolvedTermReferencePtr, FormContextHandler&, size_t))
16571668 {
16581669 return WrapUnwrap(term,
16591670 [&](FormContextHandler& fch, ResolvedTermReferencePtr p_ref){
1660- return fch.Wrapping == 0 ? fwrap(term, ctx, p_ref, fch, 1)
1671+ return fch.Wrapping == 0 ? fwrap(term, p_ref, fch, 1)
16611672 : ThrowForWrappingFailure(ystdex::type_id<FormContextHandler>());
16621673 }, [](const ContextHandler& h) YB_ATTR_LAMBDA(noreturn) -> ReductionStatus{
16631674 ThrowForWrappingFailure(h.target_type());
16641675 });
16651676 }
1666-//@}
16671677
16681678
16691679 //! \since build 834
@@ -1825,9 +1835,9 @@
18251835 PDefHOp(bool, ==, const Decapsulate& x, const Decapsulate& y) ynothrow
18261836 ImplRet(x.Get() == y.Get())
18271837
1828- //! \since build 856
1838+ //! \since build 913
18291839 ReductionStatus
1830- operator()(TermNode& term, ContextNode& ctx) const
1840+ operator()(TermNode& term) const
18311841 {
18321842 return CallRegularUnaryAs<const Encapsulation>(
18331843 [&](const Encapsulation& enc, ResolvedTermReferencePtr p_ref){
@@ -1847,7 +1857,7 @@
18471857 // XXX: Allocators are not used here for performance in
18481858 // most cases.
18491859 term.Value = TermReference(tm,
1850- FetchTailEnvironmentReference(*p_ref, ctx));
1860+ p_ref->GetEnvironmentReference());
18511861 return ReductionStatus::Clean;
18521862 }
18531863 return ReductionStatus::Retained;
@@ -1870,7 +1880,7 @@
18701880 auto i(term.begin());
18711881 auto& comb(NPL::Deref(++i));
18721882
1873- ForwardToUnwrapped(comb, ctx);
1883+ ForwardToUnwrapped(comb);
18741884
18751885 TermNode expr(std::allocator_arg, term.get_allocator(), {std::move(comb)});
18761886
@@ -1919,30 +1929,32 @@
19191929 auto& l(*++i);
19201930 auto& pred(*++i);
19211931 const auto& d(ctx.GetRecordPtr());
1922- auto lv_pred(EvaluateBoundLValueUnwrapped(pred, ctx, d));
19231932 auto& con(term.GetContainerRef());
19241933 auto& lv_l(con.back());
1934+ const auto nterm_cons_combine([&, d](TermNode& tm){
1935+ TermNode::Container tcon(nterm.get_allocator());
1936+
1937+ tcon.push_back(EvaluateBoundLValueUnwrapped(tm, d));
1938+ tcon.push_back(lv_l);
1939+ nterm.GetContainerRef() = std::move(tcon);
1940+ nterm.Value.Clear();
1941+ });
19251942
19261943 // NOTE: This shall be stored separatedly to %l because %l is abstract,
19271944 // which can be a non-list.
19281945 lv_l = EvaluateLocalObject(l, d);
1929- nterm
1930- = TermNode::Container({std::move(lv_pred), lv_l}, term.get_allocator());
1946+ nterm_cons_combine(pred);
19311947 // TODO: Blocked. Use C++14 lambda initializers to simplify the
19321948 // implementation.
19331949 return Combine<NonTailCall>::ReduceCallSubsequent(nterm, ctx,
19341950 EnvironmentGuard(ctx, d), A1::NameTypedReducerHandler(
19351951 // XXX: Capture of %d by copy is a slightly more efficient.
1936- std::bind([&, d, f](TNIter& i_0) -> ReductionStatus{
1952+ std::bind([&, d, f, nterm_cons_combine](TNIter& i_0) -> ReductionStatus{
19371953 auto& base(*++i_0);
19381954
19391955 if(!ExtractBool(nterm))
19401956 {
1941- auto& head(*++i_0);
1942- auto lv_head(EvaluateBoundLValueUnwrapped(head, ctx, d));
1943-
1944- nterm.GetContainerRef() = {std::move(lv_head), lv_l},
1945- nterm.Value.Clear();
1957+ nterm_cons_combine(*++i_0);
19461958 return Combine<NonTailCall>::ReduceCallSubsequent(nterm, ctx, d,
19471959 A1::NameTypedReducerHandler(
19481960 std::bind([&, d, f](TNIter& i_1){
@@ -1966,17 +1978,29 @@
19661978 return DoAcc([&](TermNode& l, TermNode& base, TermNode& lv_l, TermNode&
19671979 nterm, const shared_ptr<Environment>& d, TNIter& i) YB_FLATTEN{
19681980 auto& tail(*++i);
1969- auto lv_sum(EvaluateBoundLValueUnwrapped(*++i, ctx, d));
1970-
1971- base.GetContainerRef() = TermNode::Container({std::move(lv_sum),
1972- std::move(nterm), std::move(base)}, term.get_allocator()),
1981+ auto lv_sum(EvaluateBoundLValueUnwrapped(*++i, d));
1982+
1983+ base.GetContainerRef() = [&]{
1984+ TermNode::Container tcon(base.get_allocator());
1985+
1986+ tcon.push_back(std::move(lv_sum));
1987+ tcon.push_back(std::move(nterm));
1988+ tcon.push_back(std::move(base));
1989+ return tcon;
1990+ }(),
19731991 base.Value.Clear();
19741992 return Combine<NonTailCall>::ReduceCallSubsequent(base, ctx, d,
19751993 // XXX: Capture of %d by copy is a slightly more efficient.
19761994 A1::NameTypedReducerHandler([&, d]() YB_FLATTEN{
1977- auto lv_tail(EvaluateBoundLValueUnwrapped(tail, ctx, d));
1978-
1979- nterm.GetContainerRef() = {std::move(lv_tail), std::move(lv_l)},
1995+ auto lv_tail(EvaluateBoundLValueUnwrapped(tail, d));
1996+
1997+ nterm.GetContainerRef() = [&]{
1998+ TermNode::Container tcon(nterm.get_allocator());
1999+
2000+ tcon.push_back(std::move(lv_tail));
2001+ tcon.push_back(std::move(lv_l));
2002+ return tcon;
2003+ }(),
19802004 nterm.Value.Clear();
19812005 return Combine<NonTailCall>::ReduceCallSubsequent(nterm, ctx, d,
19822006 A1::NameTypedReducerHandler([&]() YB_FLATTEN{
@@ -1998,21 +2022,29 @@
19982022 auto& con(term.GetContainerRef());
19992023 const auto a(term.get_allocator());
20002024 auto& n2term(*std::next(con.rbegin()));
2001- auto lv_tail(EvaluateBoundLValueUnwrapped(*++i, ctx, d));
2025+ auto lv_tail(EvaluateBoundLValueUnwrapped(*++i, d));
20022026 const auto& lv_sum_op(*++i);
20032027
2004- n2term.GetContainerRef()
2005- = TermNode::Container({std::move(lv_tail), std::move(lv_l)}, a),
2028+ n2term.GetContainerRef() = [&]{
2029+ TermNode::Container tcon(n2term.get_allocator());
2030+
2031+ tcon.push_back(std::move(lv_tail));
2032+ tcon.push_back(std::move(lv_l));
2033+ return tcon;
2034+ }(),
20062035 n2term.Value.Clear();
20072036 return Combine<NonTailCall>::ReduceCallSubsequent(n2term, ctx, d,
20082037 // XXX: Capture of %d by copy is a slightly more efficient.
2009- A1::NameTypedReducerHandler([&, a, d]() YB_FLATTEN{
2038+ A1::NameTypedReducerHandler([&, d]() YB_FLATTEN{
20102039 l = std::move(n2term);
2011-
2012- auto& rterm(*term.emplace(ystdex::exchange(con, TermNode::Container(
2013- {lv_sum_op, std::move(nterm)}, a))));
2014-
2015- return A1::ReduceCurrentNext(rterm, ctx, DoAccR,
2040+ return
2041+ A1::ReduceCurrentNext(*term.emplace(ystdex::exchange(con, [&]{
2042+ TermNode::Container tcon(con.get_allocator());
2043+
2044+ tcon.push_back(lv_sum_op);
2045+ tcon.push_back(std::move(nterm));
2046+ return tcon;
2047+ }())), ctx, DoAccR,
20162048 A1::NameTypedReducerHandler([&, d]() YB_FLATTEN{
20172049 return Combine<NonTailCall>::ReduceEnvSwitch(term, ctx, d);
20182050 }, "eval-accr-sum"));
@@ -2020,110 +2052,74 @@
20202052 }, term, ctx);
20212053 }
20222054
2023-//! \since build 899
2024-void
2025-LiftFirst(TermNode& term, TermNode& tm, bool move)
2055+/*!
2056+\brief 准备递归调用使用的列表对象:绑定对象为列表临时对象。
2057+\since build 913
2058+*/
2059+YB_FLATTEN void
2060+PrepareFoldRList(TermNode& term)
20262061 {
2027- if(const auto p = NPL::TryAccessLeaf<const TermReference>(tm))
2028- {
2029- if(!p->IsReferencedLValue())
2030- {
2031- LiftMovedOther(term, *p, p->IsMovable());
2032- return;
2033- }
2034- }
2035- // XXX: Term tags are currently not respected in prvalues.
2036- LiftOtherOrCopy(term, tm, move);
2037-}
2038-
2039-//! \brief 绑定对象为列表临时对象的引用值。
2040-YB_FLATTEN void
2041-BindListToLocalRef(TermNode& term, ContextNode& ctx)
2042-{
2043- // NOTE: As using the term reference returned by %EvaluateLocalObject.
2044- const auto p(NPL::TryAccessLeaf<TermReference>(term));
2045- auto& nd(p ? p->get() : term);
2046-
2047- if(IsList(nd))
2048- {
2049- if(p)
2062+ // NOTE: The 1st call can be applied to reference to list, but the nested
2063+ // application is only for non-list objects due to the rest list is extract
2064+ // as if a call of 'rest%'.
2065+ NPL::ResolveTerm(
2066+ [&](TermNode& nd, ResolvedTermReferencePtr p_ref) YB_FLATTEN{
2067+ // NOTE: This is only needed for the outermost call.
2068+ if(IsList(nd))
20502069 {
2051- if(bool(p->GetTags() & (TermTags::Unique | TermTags::Temporary)))
2052- LiftOther(term, nd);
2053- else
2054- {
2055- // NOTE: The subterms of the source are to be copied as
2056- // (collapsed) reference values to form a new list replacing
2057- // the value of %term.
2058- const auto a(nd.get_allocator());
2059- TermNode::Container ncon(a);
2060-
2061- for(auto j{nd.begin()}; j != nd.end(); ++j)
2062- {
2063- auto& tm(*j);
2064-
2065- if(IsReferenceTerm(tm))
2066- ncon.emplace_back(tm);
2067- else
2068- // NOTE: Same to %ReduceToReferenceAt.
2069- ncon.emplace_back(NPL::AsTermNode(a,
2070- TermReference(tm.Tags, tm, ctx.GetRecordPtr())));
2071- }
2072- term.Tags |= TermTags::Nonmodifying;
2073- term.Value.Clear();
2074- term.GetContainerRef() = std::move(ncon);
2075- }
2070+ if(p_ref)
2071+ LiftOtherOrCopy(term, nd, p_ref->IsMovable());
20762072 }
2077- term.Tags |= TermTags::Temporary;
2078- // NOTE: Otherwise, the source list %nd is not an lvalue. Keep it as-is.
2079- }
2080- else
2081- // NOTE: Always treat the list as an lvalue as in the derivation. This
2082- // is done only once here, since all recursive calls still keep %term
2083- // as a list.
2084- ThrowInsufficientTermsError(nd, true);
2073+ else
2074+ // NOTE: Always treat the list as an lvalue as in the derivation.
2075+ // This is done only once here, since all recursive calls still
2076+ // keep %term as a list.
2077+ ThrowInsufficientTermsError(nd, true);
2078+ }, term);
20852079 }
20862080
2081+// XXX: Now terms are treated always movable without check for
2082+// %TermTags::Nonmodifying, respecting copy elision with 'first%'.
20872083 YB_FLATTEN void
2088-ExtractFirstOrCopy(TermNode& term, TermNode& tm, bool move)
2084+ExtractFirst(TermNode& term, TermNode& tm)
20892085 {
20902086 YAssert(IsBranchedList(tm), "Invalid term found.");
2091- // NOTE: The source list %nd is not an lvalue. Copy or move the 1st
2087+ // NOTE: The source list %tm is not an lvalue. Copy or move the 1st
20922088 // subterm of the source directly.
2093- LiftFirst(term, AccessFirstSubterm(tm), move);
2089+ // NOTE: As %FirstFwd. There should ne no cycle in the caller sites, so
2090+ // %term and the 1st subterm of %tm is not the same.
2091+ // XXX: Term tags are currently not respected in prvalues.
2092+ LiftOther(term, AccessFirstSubterm(tm));
20942093 tm.GetContainerRef().pop_front();
20952094 }
20962095
2097-inline PDefH(void, ExtractFirst, TermNode& term, TermNode& tm)
2098- ImplExpr(ExtractFirstOrCopy(term, tm,
2099- !bool(tm.Tags & TermTags::Nonmodifying)))
2100-
21012096 YB_FLATTEN ReductionStatus
21022097 DoFoldR1(TermNode& term, ContextNode& ctx)
21032098 {
2104- // NOTE: Subterms are %nterm, the underlying combiner of 'kons', 'knil',
2105- // 'l'.
2099+ // NOTE: Subterms are %nterm, the underlying combiner of 'kons', 'knil', 'l'.
21062100 YAssert(term.size() == 4, "Invalid recursive call found.");
21072101
21082102 auto& tm(term.GetContainerRef().back());
21092103 auto i(term.begin());
21102104
21112105 ++i;
2112- // XXX: This should have been guaranteed by the call of %BindListToLocalRef.
2106+ // XXX: This should have been guaranteed by the call of %PrepareFoldRList.
21132107 YAssert(IsList(tm), "Invalid non-list term found.");
21142108 if(IsBranch(tm))
21152109 {
21162110 auto& nterm(term.GetContainerRef().front());
21172111
21182112 ExtractFirst(nterm, tm);
2119-
2120- auto& rterm(*term.emplace(ystdex::exchange(term.GetContainerRef(),
2121- TermNode::Container({*i, std::move(nterm)},
2122- term.get_allocator()))));
2123-
21242113 // TODO: Blocked. Use C++14 lambda initializers to simplify the
21252114 // implementation.
2126- return A1::ReduceCurrentNext(rterm, ctx, DoFoldR1,
2115+ return A1::ReduceCurrentNext(
2116+ *term.emplace(ystdex::exchange(term.GetContainerRef(), [&]{
2117+ TermNode::Container tcon(term.get_allocator());
2118+
2119+ tcon.push_back(*i);
2120+ tcon.push_back(std::move(nterm));
2121+ return tcon;
2122+ }())), ctx, DoFoldR1,
21272123 A1::NameTypedReducerHandler(
21282124 std::bind([&](shared_ptr<Environment>& d) YB_FLATTEN{
21292125 return
@@ -2142,7 +2138,7 @@
21422138
21432139 auto& tm(term.GetContainerRef().back());
21442140
2145- // XXX: This should have been guaranteed by the call of %BindListToLocalRef.
2141+ // XXX: This should have been guaranteed by the call of %PrepareFoldRList.
21462142 YAssert(IsList(tm), "Invalid non-list term found.");
21472143 if(IsBranch(tm))
21482144 {
@@ -2150,14 +2146,17 @@
21502146 auto& nterm(term.GetContainerRef().front());
21512147
21522148 ExtractFirst(nterm, tm);
2153-
2154- const auto a(term.get_allocator());
2155- auto& rterm(*term.emplace(ystdex::exchange(term.GetContainerRef(),
2156- TermNode::Container({TermNode({*++i, std::move(nterm)},a)}, a))));
2157-
21582149 // TODO: Blocked. Use C++14 lambda initializers to simplify the
21592150 // implementation.
2160- return A1::ReduceCurrentNext(rterm, ctx, DoMap1,
2151+ return A1::ReduceCurrentNext(
2152+ *term.emplace(ystdex::exchange(term.GetContainerRef(), [&]{
2153+ TermNode::Container tcon(term.get_allocator());
2154+ auto& subtcon(tcon.emplace_back().GetContainerRef());
2155+
2156+ subtcon.push_back(*++i);
2157+ subtcon.push_back(std::move(nterm));
2158+ return tcon;
2159+ }())), ctx, DoMap1,
21612160 A1::NameTypedReducerHandler(
21622161 std::bind([&](shared_ptr<Environment>& d) YB_FLATTEN{
21632162 return Combine<NonTailCall>::ReduceCallSubsequent(*term.begin(),
@@ -2175,7 +2174,7 @@
21752174 {
21762175 auto& sum(*std::next(rterm.begin(), n));
21772176
2178- sum = EvaluateBoundLValueUnwrapped(*term.emplace(std::move(sum)), ctx,
2177+ sum = EvaluateBoundLValueUnwrapped(*term.emplace(std::move(sum)),
21792178 ctx.GetRecordPtr());
21802179 }
21812180
@@ -2206,13 +2205,13 @@
22062205
22072206 // XXX: Like %AccL.
22082207 BindMoveNextNLocalSubobjectInPlace(con.begin(), 2);
2208+ // NOTE: Bind the last argument as the local reference to list temporary
2209+ // object.
2210+ PrepareFoldRList(con.back());
22092211
22102212 auto& rterm(*term.emplace(ystdex::exchange(con,
22112213 TermNode::Container(term.get_allocator()))));
22122214
2213- // NOTE: Bind the last argument as the local reference to list temporary
2214- // object.
2215- BindListToLocalRef(rterm.GetContainerRef().back(), ctx);
22162215 // NOTE: Save 'kons' (for %FoldR1) or 'appv' (for %Map1).
22172216 PrepareSumOp(term, ctx, rterm, 1);
22182217 return DoRLiftSum(term, ctx, rterm, f);
@@ -2227,21 +2226,22 @@
22272226 auto i(term.begin());
22282227 auto& tm(*++i);
22292228
2230- // XXX: This should have been guaranteed by the call of %BindListToLocalRef.
2229+ // XXX: This should have been guaranteed by the call of %PrepareFoldRList.
22312230 YAssert(IsList(tm), "Invalid non-list term found.");
22322231 if(IsBranch(tm))
22332232 {
22342233 auto& nterm(term.GetContainerRef().front());
22352234
22362235 ExtractFirst(nterm, tm);
2237-
2238- auto& rterm(*term.emplace(
2239- ystdex::exchange(term.GetContainerRef(), TermNode::Container(
2240- {std::move(nterm)}, term.get_allocator()))));
2241-
22422236 // TODO: Blocked. Use C++14 lambda initializers to simplify the
22432237 // implementation.
2244- return A1::ReduceCurrentNext(rterm, ctx, DoListConcat,
2238+ return A1::ReduceCurrentNext(
2239+ *term.emplace(ystdex::exchange(term.GetContainerRef(), [&]{
2240+ TermNode::Container tcon(term.get_allocator());
2241+
2242+ tcon.push_back(std::move(nterm));
2243+ return tcon;
2244+ }())), ctx, DoListConcat,
22452245 A1::NameTypedReducerHandler([&]() YB_FLATTEN{
22462246 return ConsMoveSubNext(term);
22472247 }, "eval-list-concat-cons"));
@@ -2263,16 +2263,20 @@
22632263 {
22642264 auto& nterm(term.GetContainerRef().front());
22652265
2266- ExtractFirstOrCopy(nterm, ls, true);
2266+ ExtractFirst(nterm, ls);
22672267 // NOTE: Bind the first term as the local reference to list temporary
22682268 // object.
2269- BindListToLocalRef(nterm, ctx);
2270- return A1::ReduceCurrentNext(*term.emplace(ystdex::exchange(
2271- term.GetContainerRef(), TermNode::Container({{}, std::move(nterm)},
2272- term.get_allocator()))), ctx, DoAppend, A1::NameTypedReducerHandler(
2273- [&]() YB_FLATTEN{
2274- return DoListConcat(term, ctx);
2275- }, "eval-append-list-concat"));
2269+ PrepareFoldRList(nterm);
2270+ return A1::ReduceCurrentNext(
2271+ *term.emplace(ystdex::exchange(term.GetContainerRef(), [&]{
2272+ TermNode::Container tcon(term.get_allocator());
2273+
2274+ tcon.emplace_back();
2275+ tcon.push_back(std::move(nterm));
2276+ return tcon;
2277+ }())), ctx, DoAppend, A1::NameTypedReducerHandler([&]() YB_FLATTEN{
2278+ return DoListConcat(term, ctx);
2279+ }, "eval-append-list-concat"));
22762280 }
22772281 term.Clear();
22782282 return ReductionStatus::Regular;
@@ -2495,7 +2499,7 @@
24952499 assign_term, p_ref->GetEnvironmentReference());
24962500 else
24972501 BindMoveLocalObject(x, assign_term);
2498- ForwardToUnwrapped(appv, ctx);
2502+ ForwardToUnwrapped(appv);
24992503 term.GetContainerRef() = {std::move(appv), std::move(op)};
25002504 // NOTE: See the precondition of
25012505 // %Combine<TailCall>::ReduceEnvSwitch.
@@ -2520,7 +2524,8 @@
25202524 {
25212525 if(list_not_move)
25222526 {
2523- term.SetContent(tm);
2527+ // NOTE: As %LiftOtherOrCopy.
2528+ term.CopyContent(tm);
25242529 return ReductionStatus::Retained;
25252530 }
25262531 if(!p->IsReferencedLValue())
@@ -2565,6 +2570,20 @@
25652570 }
25662571
25672572 ReductionStatus
2573+FirstFwd(TermNode& term)
2574+{
2575+ return FirstOrVal(term, [&](TermNode& tm, ResolvedTermReferencePtr p_ref){
2576+ // XXX: Using %LiftOtherOrCopy instead of %LiftTermOrCopy is safe,
2577+ // because the referent is not allowed to have cyclic reference to
2578+ // %term. And %LiftTermOrCopy is in that case is still inapproiate
2579+ // anyway because copy should be elided in a forwarding operation.
2580+ // XXX: Term tags are currently not respected in prvalues.
2581+ LiftOtherOrCopy(term, tm, NPL::IsMovable(p_ref));
2582+ return ReductionStatus::Retained;
2583+ });
2584+}
2585+
2586+ReductionStatus
25682587 FirstRef(TermNode& term)
25692588 {
25702589 return FirstAtRef(term, ReduceToReference);
@@ -2580,7 +2599,7 @@
25802599 }
25812600
25822601 ReductionStatus
2583-Rest(TermNode& term)
2602+RestFwd(TermNode& term)
25842603 {
25852604 return RestOrVal(term, [](TermNode&) ynothrow{},
25862605 [&](TermNode& dst, TermNode& tm, ResolvedTermReferencePtr p_ref){
@@ -2589,18 +2608,22 @@
25892608 }
25902609
25912610 ReductionStatus
2592-RestRef(TermNode& term)
2611+RestRef(TermNode& term, ContextNode& ctx)
25932612 {
25942613 return CallResolvedUnary([&](TermNode& nd, ResolvedTermReferencePtr p_ref){
25952614 CheckResolvedListReference(nd, p_ref);
25962615
2597- TermNode::Container con(term.get_allocator());
2616+ const auto a(term.get_allocator());
2617+ auto p_sub(YSLib::allocate_shared<TermNode>(a));
2618+ auto& con(p_sub->GetContainerRef());
25982619
25992620 for(auto i(std::next(nd.begin())); i != nd.end(); ++i)
26002621 // XXX: Same to %FirstRef.
26012622 ReduceToReference(con.emplace_back(), *i, p_ref);
2602- con.swap(term.GetContainerRef());
2603- return ReductionStatus::Retained;
2623+ // NOTE: Irregular representation is constructed for the list subobject
2624+ // reference.
2625+ return ReduceAsSubobjectReference(term, std::move(p_sub),
2626+ p_ref ? p_ref->GetEnvironmentReference() : ctx.WeakenRecord());
26042627 }, term);
26052628 }
26062629
@@ -2816,9 +2839,9 @@
28162839
28172840
28182841 ReductionStatus
2819-Wrap(TermNode& term, ContextNode& ctx)
2842+Wrap(TermNode& term)
28202843 {
2821- return WrapOrRef(term, ctx, WrapN,
2844+ return WrapOrRef(term, WrapN,
28222845 [&](ContextHandler& h, ResolvedTermReferencePtr p_ref){
28232846 return WrapH(term, MakeValueOrMove(p_ref, [&]{
28242847 return FormContextHandler(h, 1);
@@ -2829,34 +2852,34 @@
28292852 }
28302853
28312854 ReductionStatus
2832-WrapRef(TermNode& term, ContextNode& ctx)
2855+WrapRef(TermNode& term)
28332856 {
2834- return WrapOrRef(term, ctx, WrapRefN,
2857+ return WrapOrRef(term, WrapRefN,
28352858 [&](ContextHandler& h, ResolvedTermReferencePtr p_ref){
2836- return p_ref ? ReduceForCombinerRef(term, ctx, *p_ref, h, 1)
2859+ return p_ref ? ReduceForCombinerRef(term, *p_ref, h, 1)
28372860 : WrapH(term, FormContextHandler(std::move(std::move(h)), 1));
28382861 });
28392862 }
28402863
28412864 ReductionStatus
2842-WrapOnce(TermNode& term, ContextNode& ctx)
2865+WrapOnce(TermNode& term)
28432866 {
2844- return WrapOnceOrOnceRef(term, ctx, WrapN);
2867+ return WrapOnceOrOnceRef(term, WrapN);
28452868 }
28462869
28472870 ReductionStatus
2848-WrapOnceRef(TermNode& term, ContextNode& ctx)
2871+WrapOnceRef(TermNode& term)
28492872 {
2850- return WrapOnceOrOnceRef(term, ctx, WrapRefN);
2873+ return WrapOnceOrOnceRef(term, WrapRefN);
28512874 }
28522875
28532876 ReductionStatus
2854-Unwrap(TermNode& term, ContextNode& ctx)
2877+Unwrap(TermNode& term)
28552878 {
28562879 using namespace std::placeholders;
28572880
28582881 return WrapUnwrap(term, std::bind(UnwrapResolved, std::ref(term),
2859- std::ref(ctx), _1, _2), ThrowForUnwrappingFailure);
2882+ _1, _2), ThrowForUnwrappingFailure);
28602883 }
28612884
28622885
@@ -3035,7 +3058,7 @@
30353058
30363059 LiftToReturn(*ri);
30373060 // NOTE: Bind 'x' as the local reference to list temporary object.
3038- BindListToLocalRef(*++ri, ctx);
3061+ PrepareFoldRList(*++ri);
30393062 return DoListConcat(term, ctx);
30403063 }
30413064
@@ -3046,8 +3069,12 @@
30463069 const auto a(con.get_allocator());
30473070
30483071 RemoveHead(term);
3049- term.emplace(ystdex::exchange(term.GetContainerRef(),
3050- TermNode::Container({{}}, term.get_allocator())));
3072+ term.emplace(ystdex::exchange(term.GetContainerRef(), [&]{
3073+ TermNode::Container tcon(term.get_allocator());
3074+
3075+ tcon.emplace_back().GetContainerRef().emplace_back();
3076+ return tcon;
3077+ }()));
30513078 return DoAppend(term, ctx);
30523079 }
30533080
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/source/NPL/NPLA1Internals.cpp
--- a/YFramework/source/NPL/NPLA1Internals.cpp Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/source/NPL/NPLA1Internals.cpp Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Internals.cpp
1212 \ingroup NPL
1313 \brief NPLA1 内部接口。
14-\version r20403
14+\version r20439
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 473
1717 \par 创建时间:
1818 2020-02-15 13:20:08 +0800
1919 \par 修改时间:
20- 2021-02-17 04:45 +0800
20+ 2021-03-01 18:44 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 非公开模块名称:
@@ -98,9 +98,8 @@
9898 ystdex::retry_on_cond(ystdex::id<>(), [&]() -> bool{
9999 bool collected = {};
100100
101- Traverse(*p_root, p_root->Parent,
102- [&](const shared_ptr<Environment>& p_dst, Environment& src,
103- ValueObject& parent) -> bool{
101+ Traverse(*p_root, p_root->Parent, [&](const shared_ptr<Environment>&
102+ p_dst, Environment& src, ValueObject& parent) -> bool{
104103 auto& dst(NPL::Deref(p_dst));
105104
106105 if(accessed.insert(src).second)
@@ -205,7 +204,6 @@
205204 }
206205 return RecordList.size() != orig_size;
207206 });
208-
209207 }
210208
211209 TCOAction&
@@ -224,34 +222,46 @@
224222 #endif
225223
226224
227-EnvironmentReference
228-FetchTailEnvironmentReference(const TermReference& ref, ContextNode& ctx)
225+ReductionStatus
226+ReduceAsSubobjectReference(TermNode& term, shared_ptr<TermNode> p_sub,
227+ const EnvironmentReference& r_env)
229228 {
230- auto r_env(ref.GetEnvironmentReference());
229+ YAssert(bool(p_sub),
230+ "Invalid subterm to form a subobject reference found.");
231231
232- // NOTE: Unsafe term reference is enforced to be safe with current
233- // environment as the holder.
234- return
235- r_env.GetAnchorPtr() ? r_env : EnvironmentReference(ctx.GetRecordPtr());
236-}
232+ // NOTE: Irregular representation is constructed for the subobject
233+ // reference.
234+ const auto a(term.get_allocator());
235+ auto& sub(NPL::Deref(p_sub));
237236
238-ReductionStatus
239-ReduceForCombinerRef(TermNode& term, ContextNode& ctx,
240- const TermReference& ref, const ContextHandler& h, size_t n)
241-{
242- const auto& r_env(FetchTailEnvironmentReference(ref, ctx));
243- const auto a(term.get_allocator());
244- auto p_sub(YSLib::allocate_shared<TermNode>(a, NPL::AsTermNode(a,
245- ContextHandler(std::allocator_arg, a,
246- FormContextHandler(RefContextHandler(h, r_env), n)))));
247- auto& sub(NPL::Deref(p_sub));
237+#if true
248238 TermNode tm(std::allocator_arg, a, {NPL::AsTermNode(a, std::move(p_sub))},
249239 std::allocator_arg, a, TermReference(sub, r_env));
250240
251241 term.SetContent(std::move(tm));
242+#else
243+ term = TermNode(std::allocator_arg, a,
244+ {NPL::AsTermNode(a, std::move(p_sub))},
245+ std::allocator_arg, a, TermReference(sub, r_env));
246+#endif
252247 return ReductionStatus::Retained;
253248 }
254249
250+ReductionStatus
251+ReduceForCombinerRef(TermNode& term, const TermReference& ref,
252+ const ContextHandler& h, size_t n)
253+{
254+ // NOTE: Irregular representation is constructed for the combiner subobject
255+ // reference.
256+ const auto& r_env(ref.GetEnvironmentReference());
257+ const auto a(term.get_allocator());
258+
259+ return ReduceAsSubobjectReference(term, YSLib::allocate_shared<TermNode>(a,
260+ NPL::AsTermNode(a, ContextHandler(std::allocator_arg, a,
261+ FormContextHandler(RefContextHandler(h, r_env), n)))),
262+ ref.GetEnvironmentReference());
263+}
264+
255265 } // inline namespace Internals;
256266
257267 } // namesapce A1;
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/source/NPL/NPLA1Internals.h
--- a/YFramework/source/NPL/NPLA1Internals.h Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/source/NPL/NPLA1Internals.h Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Internals.h
1212 \ingroup NPL
1313 \brief NPLA1 内部接口。
14-\version r20914
14+\version r20923
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 882
1717 \par 创建时间:
1818 2020-02-15 13:20:08 +0800
1919 \par 修改时间:
20- 2021-02-17 02:51 +0800
20+ 2021-03-01 18:44 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 非公开模块名称:
@@ -309,8 +309,8 @@
309309 ReductionStatus
310310 operator()(ContextNode&) const;
311311
312- //! \since build 909
313- DefGetter(const ynothrowv, TermNode&, TermRef, term_guard.func.func.TermRef)
312+ //! \since build 913
313+ DefGetter(const ynothrow, TermNode&, TermRef, term_guard.func.func.TermRef)
314314
315315 //! \since build 857
316316 void
@@ -958,13 +958,15 @@
958958 };
959959
960960
961-YB_ATTR_nodiscard YB_PURE EnvironmentReference
962-FetchTailEnvironmentReference(const TermReference&, ContextNode&);
961+//! \since build 913
962+ReductionStatus
963+ReduceAsSubobjectReference(TermNode&, shared_ptr<TermNode>,
964+ const EnvironmentReference&);
963965
964-//! \since build 878
966+//! \since build 913
965967 ReductionStatus
966-ReduceForCombinerRef(TermNode&, ContextNode&,
967- const TermReference&, const ContextHandler&, size_t);
968+ReduceForCombinerRef(TermNode&, const TermReference&, const ContextHandler&,
969+ size_t);
968970
969971
970972 //! \since build 881
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/source/NPL/SContext.cpp
--- a/YFramework/source/NPL/SContext.cpp Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/source/NPL/SContext.cpp Fri Mar 05 12:30:06 2021 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2012-2020 FrankHB.
2+ © 2012-2021 FrankHB.
33
44 This file is part of the YSLib project, and may only be used,
55 modified, and distributed under the terms of the YSLib project
@@ -11,13 +11,13 @@
1111 /*! \file SContext.cpp
1212 \ingroup NPL
1313 \brief S 表达式上下文。
14-\version r2019
14+\version r2037
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 329
1717 \par 创建时间:
1818 2012-08-03 19:55:59 +0800
1919 \par 修改时间:
20- 2020-10-05 23:01 +0800
20+ 2020-02-28 19:24 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -75,17 +75,39 @@
7575 }
7676
7777 void
78+TermNode::MoveContainer(TermNode&& node)
79+{
80+ YAssert(!ystdex::ref_eq<>()(*this, node), "Invalid self move found.");
81+
82+ // NOTE: Similar to %ValueNode::MoveContainer.
83+ const auto t(std::move(GetContainerRef()));
84+
85+ container = std::move(node.container);
86+}
87+
88+void
7889 TermNode::MoveContent(TermNode&& node)
7990 {
8091 YAssert(!ystdex::ref_eq<>()(*this, node), "Invalid self move found.");
8192
8293 // NOTE: Similar to %ValueNode::MoveContent.
83- const auto t(std::move(GetContainerRef()));
94+ const auto t(std::move(*this));
8495
8596 SetContent(std::move(node));
8697 }
8798
8899 void
100+TermNode::MoveValue(TermNode&& node)
101+{
102+ YAssert(!ystdex::ref_eq<>()(*this, node), "Invalid self move found.");
103+
104+ // NOTE: Similar to %ValueNode::MoveValue.
105+ const auto t(std::move(Value));
106+
107+ Value = std::move(node.Value);
108+}
109+
110+void
89111 TermNode::SwapContent(TermNode& term) ynothrowv
90112 {
91113 SwapContainer(term),
diff -r 9d414ed272d3 -r 3db7f6204127 YFramework/source/YSLib/Core/ValueNode.cpp
--- a/YFramework/source/YSLib/Core/ValueNode.cpp Wed Feb 24 00:04:45 2021 +0800
+++ b/YFramework/source/YSLib/Core/ValueNode.cpp Fri Mar 05 12:30:06 2021 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2012-2019 FrankHB.
2+ © 2012-2019, 2021 FrankHB.
33
44 This file is part of the YSLib project, and may only be used,
55 modified, and distributed under the terms of the YSLib project
@@ -11,13 +11,13 @@
1111 /*! \file ValueNode.cpp
1212 \ingroup Core
1313 \brief 值类型节点。
14-\version r852
14+\version r875
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 338
1717 \par 创建时间:
1818 2012-08-03 23:04:03 +0800
1919 \par 修改时间:
20- 2019-04-15 13:08 +0800
20+ 2021-03-01 00:19 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -52,26 +52,48 @@
5252 }
5353
5454 void
55+ValueNode::MoveContainer(ValueNode&& node)
56+{
57+ YAssert(!ystdex::ref_eq<>()(*this, node), "Invalid self move found.");
58+
59+ // NOTE: As %ValueNode::MoveContent, but only the container is respected.
60+ const auto t(std::move(GetContainerRef()));
61+
62+ container = std::move(node.container);
63+}
64+
65+void
5566 ValueNode::MoveContent(ValueNode&& node)
5667 {
5768 YAssert(!ystdex::ref_eq<>()(*this, node), "Invalid self move found.");
5869
5970 // NOTE: This is required for the case when the moved-to object (referenced
6071 // by '*this') is an ancestor of the moved-from node (referenced by the
61- // parameter). In such cases, an object moved from the moved-to object
62- // container is needed to preserve the content of the moved-from node
63- // living sufficient long, since the move %operator= of %Container does not
64- // guarantee the old content of the container (as it can clear the old
72+ // parameter). In such cases, an object moved from the container of the
73+ // moved-to object is needed to preserve the content of the moved-from node
74+ // living sufficiently long, since the move %operator= of %Container does
75+ // not guarantee the old content of the container (as it can clear the old
6576 // content in the container before moving elements). This also avoids
6677 // cyclic references even when the move %operator= of %Container does
6778 // preserved the old content of the container longer (e.g. by copy and
68- // swap), which would leak resources instead of d.
69- const auto t(std::move(GetContainerRef()));
79+ // swap), which would leak resources (rather than destroy them properly).
80+ const auto t(std::move(*this));
7081
7182 SetContent(std::move(node));
7283 }
7384
7485 void
86+ValueNode::MoveValue(ValueNode&& node)
87+{
88+ YAssert(!ystdex::ref_eq<>()(*this, node), "Invalid self move found.");
89+
90+ // NOTE: As %ValueNode::MoveContent, but only %Value is respected.
91+ const auto t(std::move(Value));
92+
93+ Value = std::move(node.Value);
94+}
95+
96+void
7597 ValueNode::SwapContent(ValueNode& node) ynothrowv
7698 {
7799 SwapContainer(node),
diff -r 9d414ed272d3 -r 3db7f6204127 doc/ChangeLog.V0.9.txt
--- a/doc/ChangeLog.V0.9.txt Wed Feb 24 00:04:45 2021 +0800
+++ b/doc/ChangeLog.V0.9.txt Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file ChangeLog.V0.9.txt
1212 \ingroup Documentation
1313 \brief 版本更新历史记录 - V0.9 。
14-\version r2773
14+\version r2967
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 800
1717 \par 创建时间:
1818 2020-10-12 17:19:23 +0800
1919 \par 修改时间:
20- 2021-02-23 23:29 +0800
20+ 2021-03-05 12:26 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -32,6 +32,198 @@
3232
3333 $now
3434 (
35+ / %YFramework $=
36+ (
37+ / @ "class %ValueNode" @ %YSLib.ValueNode $=
38+ (
39+ * "missing save %Value data member" @ "function %MoveContent"
40+ $since b803,
41+ + "function %MoveContainer",
42+ // As the old %MoveContent.
43+ + "functions %(MoveValue, CopyContainer, CopyContent, \
44+ CopyValue)"
45+ ),
46+ / %NPL $=
47+ (
48+ / @ "class %TermNode" @ %SContext $=
49+ (
50+ // Similar to %ValueNode. Note it is not easy to get this \
51+ bug because %Value normally does not hold a value of \
52+ %TermNode (and referring to some other term).
53+ * "missing save %Value data member" @ "function %MoveContent"
54+ $since b853,
55+ + "function %MoveContainer",
56+ // As the old %MoveContent.
57+ + "functions %(MoveValue, CopyContainer, CopyContent, \
58+ CopyValue)"
59+ ),
60+ / %NPLA $=
61+ (
62+ / DLDI "simplified function %PrepareCollapse" @ %NPLA
63+ !^ "%NPL::TryAccessLeaf",
64+ / DLI "simplified function %LiftTermOrCopy"
65+ ^ (($dep_from "%TermNode::CopyContent"
66+ ~ "%TermNode::MoveContent") @ %SContext),
67+ / DLI "simplified functions %(LiftCollapsed, MoveCollapsed)"
68+ ^ "%TermNode::SetContent" ~ "%TermNode::MoveContent",
69+ (
70+ * $re_add(b876) "missing support of irregular representation \
71+ for term copy" @ ("function %LiftOtherOrCopy";
72+ $comp "function %LiftMovedOther") $since b876
73+ $= (/ $impl ^ (($dep_from "TermNode::CopyContent"
74+ ~ "%TermNode::SetContent") @ %SContext));
75+ / DLI "optimized function %LiftToReturn" ^ "%LiftMovedOther"
76+ ~ "%LiftMoved"
77+ // This relies on the fix of %LiftOtherOrCopy, otherwise \
78+ operations like the unwrappng with non-native 'list' \
79+ would crash.
80+ )
81+ ),
82+ / %NPLA1 $=
83+ (
84+ + "function %ReduceToReferenceList"
85+ ^ $dep_from ("%TermNode::MoveContainer" @ %SContext),
86+ / DLI "term reference initlaization" @ "functions \
87+ %(BindParameter, MatchParameter)" ^ "allocator"
88+ ),
89+ / %NPLA1Internals $=
90+ (
91+ / DLD 'ynothrowv' @ "member function %TCOAction::GetTermRef"
92+ @ NPLA1Internals -> 'ynothrow',
93+ / @ "function %ReduceForCombinerRef" $=
94+ (
95+ / !^ "%FetchTailEnvironmentReference"
96+ $dep_to "removal of tail environment check";
97+ - "context parameteter" $dep_to
98+ "simplified combiner subobjct reference construction"
99+ ),
100+ - $revert(b869) "function %FetchTailEnvironmentReference",
101+ // See $2021-03 @ %Documentation::Workflow.
102+ (
103+ + "function %ReduceAsSubobjectReference";
104+ / DLDI "simplified function %ReduceForCombinerRef"
105+ )
106+ ),
107+ / @ "namespace %Forms" @ %NPLA1Forms $=
108+ (
109+ / "function %Rest" => "%RestFwd",
110+ + "function %FirstFwd",
111+ (
112+ / "avoided move the referent of list element"
113+ @ "functions %(FoldR1, Map1, ListConcat, Append)";
114+ // This is required for the change of the semantics of \
115+ several high-level operations, see %Dependency.
116+ * $comp "behavior of native list operations for 'foldr1' \
117+ derviations mismatch with alternative derivations"
118+ $since b912
119+ // Notably, list in the 1st turn of the call entry should \
120+ not be treated as list of reference values, although \
121+ the alternative derivation of 'foldr1' may also buggy \
122+ (see below).
123+ ),
124+ / DLI "optimized %operator()" @ "vau handler"
125+ ^ "%TermNode::SetContent" ~ "%(LiftOther, LiftOtherOrCopy)",
126+ / DLI "decapsulation operation"
127+ !^ "%FetchTailEnvironmentReference"
128+ @ "function %MakeEncapsulationType"
129+ $dep_to "removal of tail environment check",
130+ (
131+ / $forced DLI "simplified combiner subobject reference \
132+ construction" $dep_from "simplified combiner subobjct \
133+ reference construction" $effective @ "functions (AccL, \
134+ AccR, Apply, FoldR1, ForwardFirst, Map1, Unwrap, Wrap, \
135+ WrapOnce, WrapOnceRef, WrapRef)";
136+ - "context parameter" @ "functions %(Unwrap, Wrap, WrapOnce, \
137+ WrapOnceRef, WrapRef)"
138+ ),
139+ * $re_add(b876) "missing support of irregular representation \
140+ for term copy" @ "function %First" $since b874,
141+ ^ (($dep_from "%TermNode::CopyContent"
142+ ~ "%TermNode::MoveContent") @ %SContext),
143+ // As %LiftOtherOrCopy.
144+ / @ "function %RestRef" $=
145+ (
146+ + "context parameter";
147+ / "preserved reference of the result"
148+ )
149+ ),
150+ / @ "function %LoadGroundContext" @ %Dependency $=
151+ (
152+ * "redundant copy" @ "alternative dervations"
153+ @ "applicative %list" $since b828
154+ $= (/ $impl ^ 'move!')
155+ + "applicative 'rlist'"
156+ ^ $dep_from ("%ReduceToReferenceList" @ %NPLA1),
157+ (
158+ + "operative '$lvalue-identifier?'";
159+ / DLDI "simplified combiners ()" ^ '$lvalue-identifier?'
160+ ~ ('bound-lvalue?', '$resolve-identifier')
161+ $effective @ ('forward', 'forward!', 'first')
162+ ),
163+ + "applicative 'first%'" $dep_from ("%FirstFwd" @ %NPLA1Forms),
164+ / @ "applicatives derived from 'foldr1'" $=
165+ (
166+ (
167+ / $revert_ex(b858) "alternative derivation" @ "applicative \
168+ 'foldr1'" ^ $dep_from 'first%' ~ 'first',
169+ / $comp "native derivations" ($dep_from
170+ "%(FoldR1, Map1, ListConcat, Append)" @ %NPLA1Forms)
171+ );
172+ / $comp "supported copy elision of the list elements",
173+ * "safety guarantee broken on list lvalues handled by the \
174+ safe operation subset" $since b858
175+ // See $2021-02 @ %Documentation::Workflow.
176+ ),
177+ / "preserved reference of the result" @ "applicative 'rest&'" $=
178+ $dep_from ("%First" @ %NPLA1Forms)
179+ // Irregular representation should be supported, esp. for \
180+ list operands.
181+ (
182+ / $comp "native derivation" $dep_from
183+ ("%RestRef" @ %NPLA1Forms),
184+ / "alternative derivation"
185+ $= (/ $impl ^ ('$def%l!', '$lambda%')
186+ ~ ('$defl', '$lambda'))
187+ ),
188+ (
189+ / DLDI "simplified ('set-first!', 'set-first@!', 'set-first%!')"
190+ ^ 'forward!' ~ 'check-list-reference';
191+ * "missing check caused by wrong operand used for \
192+ 'check-list-reference'" $effective @ "alternative \
193+ derivation of appications ('set-first!', 'set-first@!', \
194+ 'set-first%!', 'first@', 'first&')" $since b876
195+ $= (/ $impl ^ 'forward!')
196+ ),
197+ * "wrong result due to missing support of list subobject \
198+ reference" @ "alternative derivation"
199+ @ "operatives ('$and?', '$or?')" $since b912
200+ $= (/ $impl ^ 'null?' ~ 'nullv?'),
201+ / @ "alternative derivations" @ "applicative %id" $=
202+ (
203+ / $impl "simplified" ^ '$move-resolved!' ~ ('$if', 'move!');
204+ * $comp "qualifier tags missing in the result" $since b873
205+ // Since b873, %expire could make %TermTags::Unique \
206+ visible in the object language.
207+ )
208+ ),
209+ / "avoided unexpected copy by list initialization via \
210+ %std::initializer_list instance"
211+ $effective "%TermNode construction" @ %NPLA1Forms $=
212+ // See $2021-03 @ %Documentation::Workflow.
213+ (
214+ * "move-only object not allowed in terms handled by functions \
215+ as native implemention of applicatives"
216+ @ (("%AccL", "%AccR") $since b898, ("%FoldR1", "%Map1")
217+ $since b899, ("%ListConcat", "%Append") $since b912),
218+ / DLI "optimized without list initialization" $=
219+ @ "functions %(SetRest, SetRestRef, MakeEncapsulationType)"
220+ )
221+ )
222+ )
223+),
224+
225+b912
226+(
35227 / $fmt @ "%.clang-format" $=
36228 (
37229 + 'SpaceBeforeCpp11BracedList: false',
@@ -187,9 +379,8 @@
187379 (
188380 - "call to %HandleResultRequests"
189381 @ "function %PrepareTCOEvaluation";
190- * $comp "invalid lifting requests for TCO"
191- $orig (@ %NPLA1 $since b854)
192- $dep_to "redundant TCO result lifting",
382+ * $comp "invalid lifting requests for TCO" $orig (@ %NPLA1
383+ $since b854) $dep_to "redundant TCO result lifting",
193384 // See $2021-02 @ %Documentation::Workflow.
194385 / @ "class %TCOAction" $=
195386 (
diff -r 9d414ed272d3 -r 3db7f6204127 doc/NPL.txt
--- a/doc/NPL.txt Wed Feb 24 00:04:45 2021 +0800
+++ b/doc/NPL.txt Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPL.txt
1212 \ingroup Documentation
1313 \brief NPL 规范和实现规格说明。
14-\version r19823
14+\version r19851
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 304
1717 \par 创建时间:
1818 2012-04-25 10:34:20 +0800
1919 \par 修改时间:
20- 2021-02-23 23:17 +0800
20+ 2021-03-05 01:20 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -2153,8 +2153,8 @@
21532153 @5.6.3 项引用(term reference) :
21542154 宿主语言中的 NPL::TermReference 是项引用。
21552155 NPL::TermReference 和所在的项中可能包含的子对象是引用值的宿主值(@5.5) 类型。
2156-其中,子项只被 @5.6.3.5 的情形使用。
2157-子对象引用(@5.6.3.5) 使用的这种形式的表示通常因为需要更多的操作比其它引用值的类似操作低效,但这种表示可避免依赖宿主语言中的本机对象内部表示(如成员布局)的依赖。
2156+其中,子项只被子对象引用(@5.6.3.5) 的情形使用。
2157+子对象引用使用的这种形式的表示通常因为需要更多的操作比其它引用值的类似操作低效,但这种表示可避免依赖宿主语言中的本机对象内部表示(如成员布局)的依赖。
21582158 一般地,在 C++ 的意义上不存在能满足语义的更有效的可移植表示,所以这种表示在和宿主语言的互操作(@5.2.1) 上是必要的。
21592159 对 NPL::TermReference 相关操作详见 @6.4.4 ;可能使用 NPL::TermReference 访问项的操作详见 @6.4.5 。
21602160
@@ -3256,7 +3256,8 @@
32563256
32573257 @7.4.7 NPLA1 辅助规约函数:
32583258 类似 NPLA ,NPLA1 提供若干辅助规约函数(@6.7) 包括:
3259-函数 A1::ReduceReturnUnspecified
3259+A1::ReduceReturnUnspecified
3260+A1::ReduceToRList
32603261
32613262 @7.4.8 其它辅助函数:
32623263 A1::SetupTraceDepth 设置跟踪深度节点,主要用于调试。
@@ -4056,8 +4057,8 @@
40564057 @8.1.2 间接值(@5.7.5) 访问:
40574058 基本规则参见 @7.7.1 。
40584059 部分 API 同时提供结果中消除引用值和保留引用值的版本,这些函数以 API 后缀名称区分:
4059-以 At 和 Ref 的后缀表示保留未折叠和折叠(@5.6.3.2) 的引用值;
4060-部分不带有特定后缀的函数为参数和返回值转发(@5.5.2.2) 操作(仅当参数指定引用值时保留参数值),同时以 Val 的后缀表示不保留引用值;
4060+以 At 、Fwd 和 Ref 的后缀表示保留未折叠的引用值(@5.6.3.2) 、转发(@5.5.2.2) 参数值和保留折叠的引用值;
4061+部分不带有特定后缀的函数为参数和返回值转发操作(仅当参数指定引用值时保留参数值),同时以 Val 的后缀表示不保留引用值;
40614062 其它不带有特定后缀的函数不保留引用值。
40624063 保留的引用值若来自参数且被折叠,通常经过调用 NPL::Collapse(@6.4.4) 折叠。
40634064 此处保留引用值和对象语言中的定义(@10.4.3) 应保持一致。
@@ -4768,7 +4769,11 @@
47684769 @9.8.3.1 赋值(assignment) :
47694770 NPLA1 的赋值操作专指以引用值操作数指定对象且不引起同一性(@4.1) 改变的对象修改(@9.8.3) 。
47704771 注意这和 Kernel 的赋值操作包含以特定对象进行替换(可使用项的转移(@5.5.3) 实现)而使对象被修改的情形不同。
4772+类似宿主语言,使用复制和转移的赋值称为复制赋值(copy assignment) 和转移赋值(move assignment) 。
4773+被修改的对象由赋值操作的目的操作数决定。赋值操作的值来自源操作数。
4774+复制赋值时不会复制消除(@5.5.6.3) 对象。若被赋值的源操作数的值在复制出错,目的操作数引用的对象不被修改。
47714775 注意避免使用引用值作为操作数的自赋值(self assignment) 引起循环引用(@9.9.1.1) 。
4776+复制赋值在自赋值时的源操作数复制仍可能出错。使用基本提升操作(@6.6.1) 实现时,一般使用 NPL::LiftTermOrCopy 而不是检查自赋值的 NPL::LiftOtherOrCopy 。
47724777 类似宿主语言,除非另行指定,赋值操作不保留源操作数的值类别(@9.7.2) 和可修改性。
47734778 注意,赋值不保证子对象(@9.8.2) 的同一性不被改变。
47744779
@@ -5167,7 +5172,7 @@
51675172 @10.7.1 以引用标记字符结尾的函数名:
51685173 为满足适用性(@1.4.5.2) ,同时考虑避免误用和允许使用引用避免复制,对一些操作显式使用以 % 或 & 结尾的函数名称以得到特别关注。
51695174 名称以引用标记字符结尾的操作属于以下分类之一:
5170-可能直接保留引用值的操作:结果(@4.4.3.1)及作用(@4.4.3.1) 依赖实际参数(需要时经过隐含的左值到右值转换)的值;
5175+可能直接保留引用值的操作:结果(@4.4.3.1) 及作用(@4.4.3.1) 依赖实际参数(需要时经过隐含的左值到右值转换)的值;
51715176 可能间接保留引用值的操作:可涉及不同的环境,结果和作用依赖实际参数(需要时经过隐含的左值到右值转换)在这些环境中被求值后确定。
51725177 对可能在返回值中间接保留引用值的操作,以 % 结尾表示所在的函数返回时不要求返回非引用值(@10.4.2) 。
51735178 其它可能在返回值中直接保留引用值的提供不同引用标记字符的多个变体的操作:
@@ -5460,12 +5465,13 @@
54605465 @11.2.1.2 可能使结果包含引用值的容器元素访问器(@10.7.2.2) :
54615466 包括:
54625467 first(@11.4.1)
5468+first@(@11.4.1)
5469+first%(@11.4.1)
54635470 first&(@11.4.1)
5464-first@(@11.4.1)
54655471 rest%(@11.4.1)
54665472 rest&(@11.4.1)
54675473 restv(@11.4.1)
5468-因为正规表示(@5.9.4) 的限制,restv 、rest& 和 rest% 重新构造列表,并不直接返回子对象(@9.8.2) ;其它访问器若带有引用标记字符,可直接返回引用值。
5474+注意 restv 和 rest% 总是构造列表,并不直接返回子对象(@9.8.2) 的引用(另见引用值构造(@11.2.4) );其它访问器若带有引用标记字符,可直接返回引用值。
54695475 此外,@11.2.2.4 中部分函数也符合容器元素访问器的要求,但当前不提供带有后缀标记字符的变体。这些函数包括:
54705476 unwrap(@11.3.8)
54715477 unbox(@11.4.3)
@@ -5539,7 +5545,8 @@
55395545
55405546 @11.2.4 引用值构造:
55415547 当前构造子对象引用(@5.6.3.5) 的操作有:
5542-基本操作中的 unwrap(@11.3.8) 创建合并子的子对象引用;
5548+unwrap(@11.3.8) 创建合并子的子对象引用;
5549+rest&(@11.4.1) 创建列表的子对象引用;
55435550 使用带有省略(@7.7.3.3) 的形式参数树(@7.7.3)(如 <formals>(@9.2.2.1) )绑定操作数为结尾列表(@7.7.3.3) ,创建列表的子对象引用(@7.7.3.5) 。
55445551 子对象引用具有非平凡非正规表示(@5.9.4.1) 。
55455552 注意子对象引用在宿主语言中不存在有效的共享状态的表示(@5.6.3) 。
@@ -5633,8 +5640,8 @@
56335640 uncollapsed? <object> :判断操作数是否为未折叠的引用值。
56345641 bound-lvalue? <object> :判断操作数是否为被引用的被绑定对象左值。
56355642 绑定临时对象(@5.5.6) 的引用类型的参数不被视为左值引用。
5636-配合 $resolve-identifier(@11.3.7) 和 % 引用标记绑定(@7.7.3.5) 的变量,可确定实际参数是否为左值。
5637-单独使用 bound-lvalue? 和 & 引用标记字符绑定(@7.7.3.5) 的变量,可确定实际参数是否为引用。
5643+配合 $resolve-identifier(@11.3.7) 和 % 引用标记绑定(@7.7.3.5) 的变量,可确定实际参数是否为左值;参见 $lvalue-identifier?(@11.4.1) 。
5644+使用 bound-lvalue? 和 & 引用标记字符绑定(@7.7.3.5) 的变量,可确定实际参数是否为引用。
56385645 unique? <object> :判断操作数是否为唯一引用(@5.4.2.2) 。
56395646 modifiable? <object> :判断操作数是否为可修改对象或可修改对象的引用值。
56405647 temporary? <object> :判断操作数是否为临时对象或临时对象的引用值。
@@ -5690,7 +5697,7 @@
56905697 eval <expression> <environment> :在参数指定的环境中求值,结果作为函数值。
56915698 注意 <expression> 若为元素中有引用值的列表,元素不会被特殊处理,不隐含左值到右值转换(@10.4.1) 。
56925699 eval% <expression> <environment> :同 eval ,但保留引用值。
5693-$resolve-identifier <symbol> :解析标识符。
5700+$resolve-identifier <symbol> :解析当前环境中的标识符。
56945701 直接保留解析结果中项的类型,不按成员访问规则确定值类别,因此和解析名称表达式的结果总是左值(@11.2) 不同,可保留消亡值(@5.7.6.1) 。
56955702 $move-resolved! <symbol> :转移解析标识符的对象。
56965703 和 $resolve-identifier 类似,但直接取被绑定的对象并从环境中转移。
@@ -5750,6 +5757,7 @@
57505757 wrap% <combiner> :同 wrap ,但参数不隐含左值到右值转换,在结果保留引用值。
57515758 unwrap <applicative> :解包装(@4.5.3.2) 应用子为底层的合并子(@7.6.1.1) 。
57525759 和 Kernel 不同,参数是右值时子对象(@9.8.2) 被复制,由这些合并子创建的操作子当前仍不足以取代内置的一等操作子,因为不支持只能转移而不能复制的对象。传递这些对象作为操作数会引起构造失败的异常。
5760+左值参数解包装的结果是合并子的子对象引用(@11.2.4) 。
57535761
57545762 @11.3.9 错误处理和检查:
57555763 raise-invalid-syntax-error <string> :引发包含参数指定的字符串内容的语法错误(@9.5.1) 。
@@ -5804,6 +5812,8 @@
58045812 使用 idv 可指定在返回值中保留引用值的不安全操作(@10.8.1) 的个别操作数不再保留引用值。
58055813 list <object>... :创建列表(类型为 <list> )对象。
58065814 list% <object>... :同 list ,但每个参数都不隐含左值到右值转换,在结果保留引用值。
5815+rlist <list> :转换参数为引用列表元素的列表。
5816+若参数是左值,则结果是参数的元素的引用构成的列表;否则,结果同 idv 。
58075817 $deflazy! <definiend> <body> :修改绑定。
58085818 同 $def! ,但不求值参数;在添加绑定前仍对冻结环境进行检查(@11.3.7) 。
58095819 $set! <parent> <formals> <body> :修改指定环境的变量绑定。
@@ -5821,6 +5831,8 @@
58215831 操作数非空时结果是最后的参数,可能是引用值。
58225832 类似 Kernel 的同名操作。
58235833 求值每个 <object> 的副作用包括其中临时对象(@5.5.6) 的销毁都被顺序限制,类似宿主语言的语句而不是保证子表达式中的临时对象的生存期延迟到完全表达式求值结束的逗号表达式。这也允许实现和 [RnRK] 同名操作类似的 PTC 要求。
5834+$lvalue-identifier? <symbol>
5835+解析当前环境中的标识符(同 $resolve-identifier(@11.3.7) )并判断是否为左值(同 bound-lvalue?(@11.3.4) )。
58245836 collapse <object> :折叠可能是引用的值。
58255837 forward <object> :转发(@10.5.4) 可能是引用的值(@11.2.2.4) 。
58265838 按在所在的环境中解析的操作数的类型可选地提升项(@6.6) 作为结果,其作用 id 或 idv 之一。
@@ -5847,6 +5859,7 @@
58475859 其中,调用 appv 的底层合并子(@4.5.3.2) 的动态环境同调用 forward-first% 的动态环境。
58485860 assign%! <reference> <object> :同 assign@!(@11.3.4) ,但 <object> 是引用时被折叠。
58495861 assign! <reference> <object> :同 assign%! ,但 <object> 隐含左值到右值转换。
5862+因为左值到右值转换,即便 <object> 指定的值来自 <reference> ,也可以赋值而不因此引起未定义行为。
58505863 set-first! <list> <object> :修改列表的第一个元素。
58515864 和 Kernel 的 set-car! 类似,但可派生,检查列表是左值,且不保留引用值。
58525865 set-first@! <list> <object> :同 set-first%! ,但保留未折叠的引用值。
@@ -5856,11 +5869,13 @@
58565869 类似传统 Lisp 及 Kernel 的 car 。命名和 SRFI-1 及 Clojure 等现代变体一致。
58575870 当 <list> 是左值时结果是折叠的引用值,否则结果是返回值转换(@5.7.6.4) 后的值。
58585871 first@ <list> :同 first ,但结果总是未折叠的引用值。
5872+first% <list> :同 first ,但结果总是转发的值。
5873+转发的值是经过折叠但没有返回值转换的值,无论参数是否为左值。
58595874 first& <list> :同 first ,但结果总是折叠的引用值。
58605875 首先调用 check-list-reference(@11.3.9) 检查参数是列表引用,对右值的抛出异常。
58615876 firstv <list> :同 first ,但结果总是返回值转换后的值。
58625877 rest% <list> :取列表第一个元素以外的元素值经过转发的值构成的列表。
5863-rest& <list> :取列表第一个元素以外的元素值的引用值构成的列表。
5878+rest& <list> :取列表第一个元素以外的元素值的引用值构成的列表的子对象引用(@11.2.4) 。
58645879 首先调用 check-list-reference 检查参数是列表引用,对右值的抛出异常。
58655880 restv <list> :取列表第一个元素以外的元素值构成的列表。
58665881 equal? <object> <object> :一般相等关系。类似 eqv?(@11.3.2) ,但同时支持表示中具有子项作为子对象(@9.8.2) 的对象。
diff -r 9d414ed272d3 -r 3db7f6204127 doc/Workflow.txt
--- a/doc/Workflow.txt Wed Feb 24 00:04:45 2021 +0800
+++ b/doc/Workflow.txt Fri Mar 05 12:30:06 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file Workflow.txt
1212 \ingroup Documentation
1313 \brief 工作流汇总报告。
14-\version r4235
14+\version r4265
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 433
1717 \par 创建时间:
1818 2013-07-31 01:27:41 +0800
1919 \par 修改时间:
20- 2021-02-12 12:05 +0800
20+ 2021-03-03 01:32 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -1001,7 +1001,7 @@
10011001 This allowed both instances of 'std::reference_wrapper' and 'ystdex::lref' to be the function argument type.
10021002 To minimize dependencies, no further conversions is supported specially.
10031003 If more generic interface is needed, SFINAE with 'ystded::wrapped_traits' can be used.
1004- The declaration 'ystdex::lref' is introduced in namespace "YSLib" by using-declarations in YSLib::Adaptor::YReference unconditionally. However, the 'std::ref' and 'std::cref' are not quite same.
1004+ The declaration 'ystdex::lref' is introduced in namespace 'YSLib' by using-declarations in YSLib::Adaptor::YReference unconditionally. However, the 'std::ref' and 'std::cref' are not quite same.
10051005 They clashed with names in 'std'.
10061006 In library code they should be always used with qualified prefix, otherwise the ADL can make troubles when the behavior of found entity are not equivalent as expected.
10071007 Note 'ystdex::ref' and 'ystdex::cref' are different with 'ystdex::make_*' for smart pointers.
@@ -3837,9 +3837,9 @@
38373837 report.impl:
38383838 A problem of memory comsumption in the compilation has been worked around.
38393839 GCC uses huge memory on stage 1 SHBuild of some translation units containing complicated code.
3840- The concrete instance is the compilation on some functions with deep nested lambda expressions (e.g. AccL) in the YFramework module NPL::NPLA1Forms.
3840+ The concrete instance is the compilation on some functions with deep nested lambda expressions (e.g. 'AccL') in the YFramework module NPL::NPLA1Forms.
38413841 With i686 target, it leads to crash of the compiler due to running out of the available address space.
3842- With x86_64 target, the translation unit containing the definitions of the 4 offending functions (AccL, AccR, FoldR1 and Map1) needs more than 5.3 GiB for the frontend (cc1plus) and more than 2.1 GiB for the assembler (as).
3842+ With x86_64 target, the translation unit containing the definitions of the 4 offending functions ('AccL', 'AccR', 'FoldR1' and 'Map1') needs more than 5.3 GiB for the frontend (cc1plus) and more than 2.1 GiB for the assembler (as).
38433843 There are also warnings "variable tracking size limit exceeded with ‘-fvar-tracking-assignments’".
38443844 Variable tracking causes huge memory used in the compilation in debug with optimization modes.
38453845 For VTA, see https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/developer_guide/ch-debug-vta.
@@ -4185,7 +4185,7 @@
41854185 This fixes the bug that uncollapsed reference values can have different result (to correct ones not in TCO actions) after one operation of result handling.
41864186 Like adding the combiner wrapping count ('A1::FormContextHandler::Wrapping'), if the count overflow, an exception is thrown.
41874187 The redundant lifting request in the operation compression (TCO action preparation) is removed.
4188- This was originally introduced in internal 'RelayForEval' in module NPL::NPLA1 since build 854.
4188+ This was originally introduced in the internal function 'RelayForEval' in module NPL::NPLA1 since build 854.
41894189 The bug would cause the result handling incorrectly by missing lifts when the lifts only requested in some enclosing calls.
41904190 In particular, it would lead to invalid reference returned without lifting, e.g. in '$bindings/p->environment' with derived '$sequence'.
41914191 With native implementation '$sequence' there would be no invalid reference because the native implementation using no TCO actions inside.
@@ -4196,6 +4196,35 @@
41964196 The bug would cause an assertion failure in 'A1::ReduceCombinedBranch' debug configurations due to wrong term used.
41974197 Currently, this would affect functions 'AccL', 'AccR', 'FoldR1', 'Map1' and 'ForwardListFirst' in namespace 'A1::Forms'.
41984198 The fix is as the change in functions 'A1::RelayForEval' and 'A1::RelayForCall' since build 909.
4199+A bug of bronken safety guarantee is fixed.
4200+ A fragment of NPLA1 program should be safe with only safe operations (whose name having no '%', for example).
4201+ This is broken by list operations based on 'foldr1', possibly preserving invalid reference values by list constructor (as 'cons%') in the implementation.
4202+ Some cases are:
4203+ $let () ($def! li list 1; map1 id li);
4204+ $let () ($def! li list 1; append li);
4205+ To fix the bug, a new accessor 'first%' is used instead of the old 'first' in the derivation of 'foldr1', which avoids accessing the referent of the list elements (if any).
4206+ This actually reverts the change in build 858, but the applicative 'first%' is not exactly same to the old one which was then renamed to 'first'.
4207+ The native fix are concerned with functions 'FoldR1', 'Map1', 'ListConcat' and 'Append' in namespace 'Forms' in module NPL::NPLA1Forms.
4208+ The logic of access the list elements is also simpler and more efficient after the fix.
4209+
4210+$2021-03:
4211+
4212+report.impl:
4213+Module NPL is continuously improved.
4214+ Envrionment reference argument in construction of 'TermReference' in NPLA1 implementation is simplified.
4215+ The operation to perserve the tail anchor (originally introduced in module NPL::NPLA1 in build 869) is removed.
4216+ It is not safer once an unsafe term reference (without the associated environment reference) has been involved.
4217+ Due to lacking of presice information of the associated environment in the term reference, it is exactly unhelpful for more precise weak reference count used by TCO, which is the original purpose.
4218+ This also makes it more consistent with 'TermReference' construction sites.
4219+ Notably, 'ReduceToReferenceAt' module NPL::NPLA (but not NPLA1) has no context parameter. It seems unwise to add one just for this special purpose.
4220+ Uses of list initialization via 'std::initialization_list' is checked to avoid unexpected copy caused by the improper use cases.
4221+ The bug makes it fail to copy 'TermNode' having move-only objects, which should have been supported.
4222+ As now, it has only effected some native implementations in module NPL::NPLA1Forms.
4223+ This makes several applicatives (like 'accr') behaves differently than its alternative derivation.
4224+ They are fixed by avoiding list initialization.
4225+ Other cases in NPL (e.g. APIs in the nested namespace 'SXML') may be not optimal, but they are not bugs as rejecting move-only objects.
4226+ There are similar cases in the uses of other types (e.g. 'ValueNode'), but regularly they do not uses move-only objects, so they are not checked now.
4227+
41994228
42004229 ////
42014230
Show on old repository browser