• R/O
  • SSH

YSLib: Commit

The YSLib project - main repository


Commit MetaInfo

Revisionc91b75c1e8bc7c22e4ced96c52374cd55265b86e (tree)
Time2021-02-06 17:04:05
AuthorFrankHB <frankhb1989@gmai...>
CommiterFrankHB

Log Message

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

Change Summary

Incremental Difference

diff -r 2d5fea7ba0cd -r c91b75c1e8bc YBase/include/ystdex/iterator.hpp
--- a/YBase/include/ystdex/iterator.hpp Sat Jan 30 14:55:15 2021 +0800
+++ b/YBase/include/ystdex/iterator.hpp Sat Feb 06 16:04:05 2021 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2011-2020 FrankHB.
2+ © 2011-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 iterator.hpp
1212 \ingroup YStandardEx
1313 \brief 通用迭代器。
14-\version r6135
14+\version r6148
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since 早于 build 189
1717 \par 创建时间:
1818 2011-01-27 23:01:00 +0800
1919 \par 修改时间:
20- 2020-05-12 15:59 +0800
20+ 2021-02-06 15:29 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -293,22 +293,22 @@
293293 transformed_iterator&
294294 operator=(transformed_iterator&&) = default;
295295
296- //! \since build 600
297- friend transformed_iterator&
298- operator+=(transformed_iterator& i, difference_type n)
299- ynoexcept(noexcept(decltype(i)(i)) && noexcept(i.get() += n))
296+ //! \since build 910
297+ transformed_iterator&
298+ operator+=(difference_type n)
299+ ynoexcept(noexcept(std::declval<transformed_iterator&>().get() += n))
300300 {
301- i.get() += n;
302- return i;
301+ get() += n;
302+ return *this;
303303 }
304304
305- //! \since build 600
306- friend transformed_iterator&
307- operator-=(transformed_iterator& i, difference_type n)
308- ynoexcept(noexcept(decltype(i)(i)) && noexcept(i.get() -= n))
305+ //! \since build 910
306+ transformed_iterator&
307+ operator-=(difference_type n)
308+ ynoexcept(noexcept(std::declval<transformed_iterator&>().get() -= n))
309309 {
310- i.get() -= n;
311- return i;
310+ get() -= n;
311+ return *this;
312312 }
313313
314314 //! \since build 585
diff -r 2d5fea7ba0cd -r c91b75c1e8bc YBase/include/ystdex/operators.hpp
--- a/YBase/include/ystdex/operators.hpp Sat Jan 30 14:55:15 2021 +0800
+++ b/YBase/include/ystdex/operators.hpp Sat Feb 06 16:04:05 2021 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2011-2016, 2018 FrankHB.
2+ © 2011-2016, 2018, 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 operators.hpp
1212 \ingroup YStandardEx
1313 \brief 重载操作符。
14-\version r2841
14+\version r2856
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 260
1717 \par 创建时间:
1818 2011-11-13 14:58:05 +0800
1919 \par 修改时间:
20- 2018-07-14 22:58 +0800
20+ 2021-02-06 15:21 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -39,7 +39,7 @@
3939 #ifndef YB_INC_ystdex_operators_hpp_
4040 #define YB_INC_ystdex_operators_hpp_ 1
4141
42-#include "addressof.hpp" // for "type_traits.hpp", false_, true_, _t,
42+#include "addressof.hpp" // for "addressof.hpp", false_, true_, _t,
4343 // empty_base, ystdex::addressof;
4444 #include "integer_sequence.hpp" // for index_sequence, vseq::defer_apply_t,
4545 // vseq::_a, vseq::fold_t;
@@ -215,22 +215,22 @@
215215 YB_Impl_Operators_bin_spec(ops_bin_id(_id YPP_Comma _off), \
216216 YB_Impl_Operators_f, _op, _type, __VA_ARGS__)
217217 #define YB_Impl_Operators_bin(_id, _op) \
218- YB_Impl_Operators_bin_tmpl(_id, 0, _op, x _op##= y, _type x, \
218+ YB_Impl_Operators_bin_tmpl(_id, 0, _op, std::move(x) _op##= y, _type x, \
219219 const _type2& y) \
220- YB_Impl_Operators_bin_tmpl(_id, 1, _op, x _op##= std::move(y), _type x, \
221- _type2&& y)
220+ YB_Impl_Operators_bin_tmpl(_id, 1, _op, std::move(x) _op##= std::move(y), \
221+ _type x, _type2&& y)
222222 #define YB_Impl_Operators_con(_id, _op) \
223223 YB_Impl_Operators_bin(_id, _op) \
224- YB_Impl_Operators_bin_tmpl(_id, 2, _op, y _op##= x, const _type2& x, \
225- _type y) \
226- YB_Impl_Operators_bin_tmpl(_id, 3, _op, y _op##= std::move(x), _type2&& x, \
227- _type y)
224+ YB_Impl_Operators_bin_tmpl(_id, 2, _op, std::move(y _op##= x), \
225+ const _type2& x, _type y) \
226+ YB_Impl_Operators_bin_tmpl(_id, 3, _op, std::move(y _op##= std::move(x)), \
227+ _type2&& x, _type y)
228228 #define YB_Impl_Operators_left(_id, _op) \
229229 YB_Impl_Operators_bin(_id, _op) \
230230 YB_Impl_Operators_bin_tmpl(_id, 4, _op, _type(x) _op##= y, \
231- const _type2& x, const _type& y) \
231+ const _type2& x, _type y) \
232232 YB_Impl_Operators_bin_tmpl(_id, 5, _op, \
233- _type(x) _op##= std::move(y), const _type2& x, _type&& y)
233+ _type(std::move(x)) _op##= std::move(y), _type2&& x, _type y)
234234
235235 YB_Impl_Operators_con(0, +)
236236 YB_Impl_Operators_left(1, -)
@@ -301,8 +301,10 @@
301301 #define YB_Impl_Operators_Left(_id, _name) \
302302 YB_Impl_Operators_H2_Alias_de(_name, _t<details::ops_seq<_type, _type2, \
303303 _tOpt, details::ops_bin_id_seq<_id, 0 YPP_Comma 1>>>) \
304- YB_Impl_Operators_H2_Alias_de(_name##_left, _t<details::ops_seq<_type, \
305- _type2, _tOpt, details::ops_bin_id_seq<_id, 4 YPP_Comma 5>>>)
304+ YB_Impl_Operators_H2_Alias_de(_name##_left, _t< \
305+ details::ops_seq<_type, _type2, _tOpt, cond_t<is_same<_type, _type2>, \
306+ details::ops_bin_id_seq<_id>, \
307+ details::ops_bin_id_seq<_id, 4 YPP_Comma 5>>>>)
306308
307309 YB_Impl_Operators_Commutative(0, addable)
308310
diff -r 2d5fea7ba0cd -r c91b75c1e8bc YFramework/include/NPL/NPLA1.h
--- a/YFramework/include/NPL/NPLA1.h Sat Jan 30 14:55:15 2021 +0800
+++ b/YFramework/include/NPL/NPLA1.h Sat Feb 06 16:04:05 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1.h
1212 \ingroup NPL
1313 \brief NPLA1 公共接口。
14-\version r8493
14+\version r8499
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 472
1717 \par 创建时间:
1818 2014-02-02 17:58:24 +0800
1919 \par 修改时间:
20- 2021-01-28 22:40 +0800
20+ 2021-02-02 09:06 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -833,7 +833,7 @@
833833 \note 使用 YSLib::AreEqualHeld 。
834834 \since build 756
835835 */
836- YB_ATTR_nodiscard friend YB_PURE PDefHOp(bool, ==,
836+ YB_ATTR_nodiscard YB_PURE friend PDefHOp(bool, ==,
837837 const WrappedContextHandler& x, const WrappedContextHandler& y)
838838 ImplRet(YSLib::AreEqualHeld(x.Handler, y.Handler))
839839
@@ -970,7 +970,10 @@
970970 ImplRet(CallN(Wrapping, term, ctx))
971971
972972 private:
973- //! \since build 859
973+ /*!
974+ \pre 断言:对异步实现,参数指定的项和下一求值项相同。
975+ \since build 859
976+ */
974977 ReductionStatus
975978 CallN(size_t, TermNode&, ContextNode&) const;
976979
@@ -1212,7 +1215,7 @@
12121215
12131216 /*!
12141217 \pre ContextNode& 类型的参数引用的对象是 NPLA1 上下文状态或 public 继承的派生类。
1215-\pre 对 TCO 实现,存在 TCOAction 当前动作。
1218+\pre TCO 实现:当前动作是 TCO 动作,且其中的当前项和被规约的项相同。
12161219 \note 参数分别表示规约上下文、被规约的项和待被保存的当前求值环境守卫。
12171220 \note 第四参数指定避免规约后提升结果。
12181221 \note 对 TCO 实现利用 TCOAction 以尾上下文进行规约。
diff -r 2d5fea7ba0cd -r c91b75c1e8bc YFramework/include/NPL/NPLA1Forms.h
--- a/YFramework/include/NPL/NPLA1Forms.h Sat Jan 30 14:55:15 2021 +0800
+++ b/YFramework/include/NPL/NPLA1Forms.h Sat Feb 06 16:04:05 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Forms.h
1212 \ingroup NPL
1313 \brief NPLA1 语法形式。
14-\version r7761
14+\version r7797
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 882
1717 \par 创建时间:
1818 2020-02-15 11:19:21 +0800
1919 \par 修改时间:
20- 2021-01-25 01:05 +0800
20+ 2021-02-04 15:15 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -50,11 +50,17 @@
5050 \pre 除非另行指定支持保存当前动作,若存在子项,关联的上下文中的尾动作为空。
5151 \pre 设置为处理器调用的操作在进入调用前应确保设置尾上下文等内部状态。
5252 \pre 作为操作数的项的子项不包含引用或非正规表示引入的对操作数内的子项的循环引用。
53+\pre 作为 NPLA1 规约函数的函数的参数符合规约函数约定。
54+\sa ContextState
55+\see %Documentation::NPL.
5356 \since build 732
5457
5558 提供支持 NPLA1 对象语言文法的操作的接口。
5659 提供的操作用于实现操作子或应用子底层的操作子。
5760 除非另行指定,操作子的参数被通过直接转移项的形式转发。
61+在 NPLA1 规约函数约定的基础上,除非另行指定:
62+ 在异步实现中都要求下一项项和参数指定的项相同;
63+ 对上述约定可隐含使用间接的断言检查。
5864 */
5965 namespace Forms
6066 {
@@ -607,7 +613,8 @@
607613 \brief 逻辑非。
608614 \since build 861
609615
610-当子项求值为 true 时返回 false ,否则返回子项。
616+操作数直接由函数参数指定。
617+当项求值为 true 时返回 false ,否则返回 true 。
611618
612619 参考调用文法:
613620 <pre>not? \<test></pre>
@@ -698,7 +705,7 @@
698705 ForwardListFirst(TermNode&, ContextNode&);
699706
700707 /*!
701-\brief 取参数指定的列表中的第一元素的引用值。
708+\brief 取参数指定的列表中的第一元素的值。
702709 \since build 859
703710 */
704711 //@{
@@ -732,6 +739,7 @@
732739 */
733740 YF_API ReductionStatus
734741 FirstRef(TermNode&);
742+//@}
735743
736744 /*!
737745 结果是列表的第一个元素经过返回值转换的值。不保留结果中的引用值。
@@ -742,6 +750,38 @@
742750 YF_API ReductionStatus
743751 FirstVal(TermNode&);
744752 //@}
753+
754+/*!
755+\brief 取列表第一个元素以外的元素值构成的列表。
756+\since build 910
757+*/
758+//@{
759+/*!
760+结果是列表第一个元素以外的元素值经过转发的值构成的列表。保留结果中的引用值。
761+
762+参考调用文法:
763+<pre>rest% \<list></pre>
764+*/
765+YF_API ReductionStatus
766+Rest(TermNode&);
767+
768+/*!
769+结果是列表第一个元素以外的元素值的引用值构成的列表。保留结果中的引用值。
770+
771+参考调用文法:
772+<pre>rest& \<list></pre>
773+*/
774+YF_API ReductionStatus
775+RestRef(TermNode&);
776+
777+/*!
778+结果是列表的第一个元素以外的元素经过返回值转换的值构成的列表。不保留结果中的引用值。
779+
780+参考调用文法:
781+<pre>restv \<list></pre>
782+*/
783+YF_API ReductionStatus
784+RestVal(TermNode&);
745785 //@}
746786 //@}
747787
diff -r 2d5fea7ba0cd -r c91b75c1e8bc YFramework/source/NPL/Dependency.cpp
--- a/YFramework/source/NPL/Dependency.cpp Sat Jan 30 14:55:15 2021 +0800
+++ b/YFramework/source/NPL/Dependency.cpp Sat Feb 06 16:04:05 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file Dependency.cpp
1212 \ingroup NPL
1313 \brief 依赖管理。
14-\version r4120
14+\version r4189
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 623
1717 \par 创建时间:
1818 2015-08-09 22:14:45 +0800
1919 \par 修改时间:
20- 2021-01-27 01:33 +0800
20+ 2021-02-05 15:59 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -260,10 +260,12 @@
260260 ReductionStatus
261261 ReduceToLoadExternal(TermNode& term, ContextNode& ctx, REPLContext& context)
262262 {
263+ Forms::RetainN(term);
263264 term = context.Load(context, ctx,
264265 NPL::ResolveRegular<string>(NPL::Deref(std::next(term.begin()))));
265266 // NOTE: This is explicitly not same to klisp. This is also friendly to PTC.
266- return A1::ReduceOnce(term, ctx);
267+ // XXX: Same to %A1::ReduceOnce, without setup the next term.
268+ return ContextState::Access(ctx).ReduceOnce.Handler(term, ctx);
267269 }
268270
269271 ReductionStatus
@@ -681,6 +683,9 @@
681683 RegisterStrict(renv, "first@", FirstAt);
682684 RegisterStrict(renv, "first&", FirstRef);
683685 RegisterStrict(renv, "firstv", FirstVal);
686+ RegisterStrict(renv, "rest%", Rest);
687+ RegisterStrict(renv, "rest&", RestRef);
688+ RegisterStrict(renv, "restv", RestVal);
684689 // NOTE: Like 'set-car!' in Kernel, with no references.
685690 RegisterStrict(renv, "set-first!", SetFirst);
686691 // NOTE: Like 'set-car!' in Kernel, with reference not collapsed.
@@ -700,6 +705,30 @@
700705 RegisterStrict(renv, "accr", AccR);
701706 RegisterStrict(renv, "foldr1", FoldR1);
702707 RegisterStrict(renv, "map1", Map1);
708+ RegisterUnary<>(renv, "first-null?", [](TermNode& x){
709+ return ResolveTerm(
710+ [&](TermNode& nd, ResolvedTermReferencePtr p_ref){
711+ if(IsBranchedList(nd))
712+ return IsEmpty(ReferenceTerm(AccessFirstSubterm(nd)));
713+ ThrowInsufficientTermsError(nd, p_ref);
714+ }, x);
715+ });
716+ RegisterStrict(renv, "make-standard-environment",
717+ // TODO: Blocked. Use C++14 lambda initializers to simplify the
718+ // implementation.
719+ ystdex::bind1(
720+ [](TermNode& term, const EnvironmentReference& ce) YB_FLATTEN{
721+ using namespace Forms;
722+
723+ Retain(term);
724+
725+ // XXX: Simlar to %Forms::MakeEnvironment.
726+ ValueObject parent;
727+
728+ parent.emplace<EnvironmentReference>(ce);
729+ term.Value
730+ = NPL::AllocateEnvironment(term.get_allocator(), std::move(parent));
731+ }, context.Root.WeakenRecord()));
703732 #else
704733 context.ShareCurrentSource("<root:basic-derived>");
705734 context.Perform(
@@ -773,9 +802,8 @@
773802 (eval (cons $vau% (cons formals (cons ignore (move! body)))) d);
774803 )NPL"
775804 # endif
776- // XXX: The operatives '$defl!', '$defl%!', '$defw%!', and '$defv%!', as
777- // well as the applicatives 'rest&' and 'rest%' are same to following
778- // derivations in %LoadCore.
805+ // XXX: The operatives '$defl!', '$defl%!', '$defw%!', and '$defv%!' are
806+ // same to following derivations in %LoadCore.
779807 // NOTE: Use of 'eqv?' is more efficient than '$if'.
780808 R"NPL(
781809 $def! $sequence
@@ -824,8 +852,9 @@
824852 ($if (bound-lvalue? ($resolve-identifier l)) id expire);
825853 $defl%! first& (&l) ($lambda% ((&x .)) x) (check-list-reference l);
826854 $defl! firstv ((&x .)) x;
855+ $defl! rest% ((#ignore .%x)) move! x;
827856 $defl! rest& (&l) ($lambda ((#ignore .&x)) x) (check-list-reference l);
828- $defl! rest% ((#ignore .%x)) x;
857+ $defl! restv ((#ignore .x)) move! x;
829858 $defl! set-first! (&l x)
830859 assign@! (first@ (check-list-reference l)) (move! x);
831860 $defl! set-first@! (&l &x)
@@ -877,7 +906,24 @@
877906 $defw%! map1 (&appv &l) d
878907 foldr1 ($lambda (&x &xs) cons%
879908 (apply appv (list% (forward! x)) d) xs) () (forward! l);
880- )NPL");
909+ $defl! first-null? (&l) null? (first l);
910+ )NPL"
911+# if NPL_Impl_NPLA1_Use_LockEnvironment
912+ R"NPL(
913+ $defl! make-standard-environment () () lock-current-environment;
914+ )NPL"
915+# else
916+ // XXX: Ground environment is passed by 'ce'.
917+ R"NPL(
918+ $def! make-standard-environment
919+ ($lambda (&se &e)
920+ ($lambda #ignore $lambda/e se () make-environment ce)
921+ ($set! se ce e))
922+ (make-environment (() get-current-environment))
923+ (() get-current-environment);
924+ )NPL"
925+# endif
926+ );
881927 #endif
882928 }
883929
@@ -953,36 +999,16 @@
953999 eval (list $set! d f $lambda/e e formals (move! body)) d;
9541000 $defv! $defl/e%! (&f &e &formals .&body) d
9551001 eval (list $set! d f $lambda/e% e formals (move! body)) d;
956- $defl! restv ((#ignore .x)) move! x;
957- $defl! rest& (&l) ($lambda ((#ignore .&x)) x) (check-list-reference l);
958- $defl! rest% ((#ignore .%x)) move! x;
9591002 )NPL");
9601003 // XXX: Keep %ContextNode::Perform calls here. This does not benefit from
9611004 // the removal of the calls (tested with G++ 10.2 in x86_64-pc-linux),
9621005 // whether %NPL_Impl_NPLA1_Native_Forms is set. However, the code below for
9631006 // 'derive-environment' is not the same.
964- context.Perform(
965-#if NPL_Impl_NPLA1_Use_LockEnvironment
966- R"NPL(
967- $defl! make-standard-environment () () lock-current-environment;
968- )NPL"
969-#else
970- // XXX: Ground environment is passed by 'ce'.
971- R"NPL(
972- $def! make-standard-environment
973- ($lambda (&se &e)
974- ($lambda #ignore $lambda/e se () make-environment ce)
975- ($set! se ce e))
976- (make-environment (() get-current-environment))
977- (() get-current-environment);
978- )NPL"
979-#endif
980- R"NPL(
1007+ context.Perform(R"NPL(
9811008 $def! (box% box? unbox) () make-encapsulation-type;
9821009 $defl! box (&x) box% x;
983- $defl! first-null? (&l) null? (first l);
984- $defl! list-rest% (&x) list% (rest% x);
985- $defl! list-concat (&x &y) foldr1 cons% y (forward! x);
1010+ $defl! list-rest% (&x) list% (rest% (forward! x));
1011+ $defl! list-concat (&x &y) foldr1 cons% (forward! y) (forward! x);
9861012 $defl! append (.&ls) foldr1 list-concat () (move! ls);
9871013 $defl%! assv (&x &alist) $cond ((null? alist) ())
9881014 ((eqv? x (first& (first& alist))) first alist)
@@ -1020,9 +1046,6 @@
10201046 )NPL"
10211047 #endif
10221048 R"NPL(
1023- $defv! $as-environment (.&body) d
1024- eval (list $let () (list $sequence (move! body)
1025- (list () lock-current-environment))) d;
10261049 $defv%! $let (&bindings .&body) d
10271050 eval% (list* () (list* $lambda (map1 firstv bindings)
10281051 (list (move! body))) (map1 list-rest% bindings)) d;
@@ -1049,6 +1072,9 @@
10491072 $defv%! $letrec% (&bindings .&body) d
10501073 eval% (list $let% () $sequence (list $def! (map1 firstv bindings)
10511074 (list* () list (map1 rest% bindings))) (move! body)) d;
1075+ $defv! $as-environment (.&body) d
1076+ eval (list $let () (list $sequence (move! body)
1077+ (list () lock-current-environment))) d;
10521078 $defv! $bindings/p->environment (&parents .&bindings) d $sequence
10531079 ($def! res apply make-environment (map1 ($lambda% (x) eval% x d)
10541080 parents))
@@ -1239,13 +1265,13 @@
12391265 to_lwr(y);
12401266 return x.find(y) != string::npos;
12411267 });
1242- RegisterUnary<>(renv, "string->symbol", [](TermNode& term){
1268+ RegisterUnary<>(renv, "string->symbol", [](TermNode& x){
12431269 return ResolveTerm([&](TermNode& nd, ResolvedTermReferencePtr p_ref){
12441270 auto& s(NPL::AccessRegular<string>(nd, p_ref));
12451271
12461272 return NPL::IsMovable(p_ref) ? StringToSymbol(std::move(s))
12471273 : StringToSymbol(s);
1248- }, term);
1274+ }, x);
12491275 });
12501276 RegisterUnary<Strict, const TokenValue>(renv, "symbol->string",
12511277 SymbolToString);
@@ -1280,12 +1306,7 @@
12801306 });
12811307 #if true
12821308 RegisterStrict(renv, "load", [&](TermNode& term, ContextNode& ctx){
1283- RetainN(term);
1284-# if NPL_Impl_NPLA1_Enable_Thunked
1285- return RelayToLoadExternal(ctx, term, context);
1286-# else
12871309 return ReduceToLoadExternal(term, ctx, context);
1288-# endif
12891310 });
12901311 #else
12911312 RegisterUnary<Strict, string>(renv, "load", [&](string& filename){
diff -r 2d5fea7ba0cd -r c91b75c1e8bc YFramework/source/NPL/NPLA1.cpp
--- a/YFramework/source/NPL/NPLA1.cpp Sat Jan 30 14:55:15 2021 +0800
+++ b/YFramework/source/NPL/NPLA1.cpp Sat Feb 06 16:04:05 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1.cpp
1212 \ingroup NPL
1313 \brief NPLA1 公共接口。
14-\version r20275
14+\version r20305
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 473
1717 \par 创建时间:
1818 2014-02-02 18:02:47 +0800
1919 \par 修改时间:
20- 2021-01-30 13:47 +0800
20+ 2021-02-04 17:22 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -144,11 +144,16 @@
144144
145145 PushActionsRange(First, Last, term, ctx);
146146
147+ // NOTE: Calling the handler may change %ctx.LastStauts, so it should be
148+ // saved at first.
149+ const auto r(ctx.LastStatus);
147150 const auto res(HandlerRef(term, ctx));
148151
149152 if(res != ReductionStatus::Partial)
153+ // NOTE: This does maintain the right reduction status for each term,
154+ // once the result of the call to %HandlerRef is trusted.
150155 ctx.LastStatus
151- = CombineSequenceReductionResult(ctx.LastStatus, res);
156+ = CombineSequenceReductionResult(r, res);
152157 return ctx.LastStatus;
153158 }
154159 #endif
@@ -204,6 +209,7 @@
204209 CombinerReturnThunk(const ContextHandler& h, TermNode& term, ContextNode& ctx,
205210 _tParams&&... args)
206211 {
212+ // NOTE: See $2021-01 @ %Documentation::Workflow.
207213 static_assert(sizeof...(args) < 2, "Unsupported owner arguments found.");
208214 #if NPL_Impl_NPLA1_Enable_TCO
209215 auto& act(EnsureTCOAction(ctx, term));
@@ -216,32 +222,38 @@
216222 // error: Segmentation fault.
217223 yunseq(0, (lf = NPL::make_observer(
218224 &act.AttachFunction(std::forward<_tParams>(args)).get()), 0)...);
219- act.RequestCombined();
220225 SetupNextTerm(ctx, term);
221226 // XXX: %A1::RelayCurrentOrDirect is not used to allow the underlying
222227 // handler optimized with %NPL_Impl_NPLA1_Enable_InlineDirect.
223228 return RelaySwitched(ctx, Continuation(std::ref(lf ? *lf : h), ctx));
224-#elif NPL_Impl_NPLA1_Enable_Thunked
229+#else
225230
226231 ContextState::Access(ctx).ClearCombiningTerm();
227232 term.Value.Clear();
233+
234+ auto gd(ystdex::unique_guard([&]() ynothrow{
235+ term.Clear();
236+ }));
237+# if NPL_Impl_NPLA1_Enable_Thunked
228238 // XXX: %A1::ReduceCurrentNext is not used to allow the underlying
229239 // handler optimized with %NPL_Impl_NPLA1_Enable_InlineDirect.
230240 SetupNextTerm(ctx, term);
231241 // TODO: Blocked. Use C++14 lambda initializers to simplify the
232242 // implementation.
233- RelaySwitched(ctx,
234- A1::NameTypedReducerHandler(std::bind([&](const _tParams&...){
243+ RelaySwitched(ctx, A1::NameTypedReducerHandler(
244+ std::bind([&](decltype(gd)& g, const _tParams&...){
245+ ystdex::dismiss(g);
235246 // NOTE: Captured argument pack is only needed when %h actually shares.
236247 return RegularizeTerm(term, ctx.LastStatus);
237- }, std::move(args)...), "eval-combine-return"));
248+ }, std::move(gd), std::move(args)...), "eval-combine-return"));
238249 return RelaySwitched(ctx, Continuation(std::ref(h), ctx));
239-#else
250+# else
251+ const auto res(RegularizeTerm(term, h(term, ctx)));
240252
253+ ystdex::dismiss(gd);
241254 yunseq(0, args...);
242- ContextState::Access(ctx).ClearCombiningTerm();
243- term.Value.Clear();
244- return RegularizeTerm(term, h(term, ctx));
255+ return res;
256+# endif
245257 #endif
246258 }
247259
@@ -625,7 +637,7 @@
625637
626638 private:
627639 // XXX: The initial %o_tags shall have %TermTags::Temporary unless it is
628- // known to bound to some non-temporary objects not stroed in the term tree
640+ // known to bound to some non-temporary objects not stored in the term tree
629641 // to be reduced.
630642 void
631643 Match(const TermNode& t, TermNode& o, TermTags o_tags,
@@ -997,7 +1009,7 @@
9971009 return ReductionStatus::Retained;
9981010 # else
9991011 AssertNextTerm(ctx, term);
1000- return A1::RelayCurrentNext(term, ctx, Continuation(
1012+ return A1::RelayCurrentNext(ctx, term, Continuation(
10011013 static_cast<ReductionStatus(&)(TermNode&, ContextNode&)>(
10021014 ReduceChildrenOrdered), ctx), A1::NameTypedReducerHandler([&]{
10031015 ReduceOrderedResult(term);
@@ -1179,7 +1191,7 @@
11791191 // XXX: Assume the term has been setup by the caller.
11801192 return RelayCurrentOrDirect(ctx, Continuation(std::ref(Handler), ctx),
11811193 term);
1182- return A1::RelayCurrentNext(term, ctx,
1194+ return A1::RelayCurrentNext(ctx, term,
11831195 Continuation([&](TermNode& t, ContextNode& c){
11841196 YAssert(!t.empty(), "Invalid term found.");
11851197 ReduceChildrenOrderedAsyncUnchecked(std::next(t.begin()), t.end(), c);
@@ -1523,7 +1535,9 @@
15231535 const auto last(o_tm.end());
15241536 TermNode::Container con(t.get_allocator());
15251537
1526- if(bool(o_tags & (TermTags::Unique | TermTags::Temporary)))
1538+ // XXX: As %TermReference::IsMovable for non-temporary objects.
1539+ if((o_tags & (TermTags::Unique | TermTags::Nonmodifying))
1540+ == TermTags::Unique || bool(o_tags & TermTags::Temporary))
15271541 {
15281542 if(sigil == char())
15291543 LiftSubtermsToReturn(o_tm);
diff -r 2d5fea7ba0cd -r c91b75c1e8bc YFramework/source/NPL/NPLA1Forms.cpp
--- a/YFramework/source/NPL/NPLA1Forms.cpp Sat Jan 30 14:55:15 2021 +0800
+++ b/YFramework/source/NPL/NPLA1Forms.cpp Sat Feb 06 16:04:05 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Forms.cpp
1212 \ingroup NPL
1313 \brief NPLA1 语法形式。
14-\version r19959
14+\version r20138
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 882
1717 \par 创建时间:
1818 2014-02-15 11:19:51 +0800
1919 \par 修改时间:
20- 2021-01-27 13:30 +0800
20+ 2021-02-04 18:34 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -624,41 +624,48 @@
624624 }
625625
626626
627-//! \since build 898
628-//@{
629627 // XXX: Same to %RelayForEvalOrDirect, except that %no_lift is always %true, and
630628 // no TCO is enforced.
629+//! \since build 898
631630 template<typename _fNext>
632631 YB_FLATTEN inline ReductionStatus
633632 RelayNextGuarded(ContextNode& ctx, TermNode& term, EnvironmentGuard&& gd,
634- _fNext&& next)
633+ _fNext&& cur)
635634 {
636635 // XXX: See %RelayForEvalOrDirect.
637636 #if NPL_Impl_NPLA1_Enable_Thunked
638637 // TODO: Blocked. Use C++14 lambda initializers to simplify the
639638 // implementation.
640- return A1::RelayCurrentNext(term, ctx, yforward(next), MakeMoveGuard(gd));
639+ return A1::RelayCurrentNext(ctx, term, yforward(cur), MakeMoveGuard(gd));
641640 #else
642641 yunused(gd);
643- return A1::RelayDirect(ctx, next, term);
642+ return A1::RelayDirect(ctx, cur, term);
644643 #endif
645644 }
646645
647646 // XXX: Same to %RelayForEvalOrDirect, except that %no_lift is always %true.
647+/*!
648+\pre TCO 实现:当前动作是 TCO 动作,且其中的当前项和被规约的项相同。
649+\since build 898
650+*/
648651 template<typename _fNext>
649652 YB_FLATTEN inline ReductionStatus
650653 RelayNextGuardedTail(ContextNode& ctx, TermNode& term,
651- EnvironmentGuard&& gd, _fNext&& next)
654+ EnvironmentGuard&& gd, _fNext&& cur)
652655 {
653656 // XXX: See %RelayForEvalOrDirect.
654657 #if NPL_Impl_NPLA1_Enable_TCO
658+ // TODO: Simplified without term regularization if call sites do not need
659+ // it?
655660 PrepareTCOEvaluation(ctx, term, std::move(gd));
656- return A1::RelayCurrentOrDirect(ctx, yforward(next), term);
661+ return A1::RelayCurrentOrDirect(ctx, yforward(cur), term);
657662 #else
658- return A1::RelayNextGuarded(ctx, term, std::move(gd), yforward(next));
663+ return A1::RelayNextGuarded(ctx, term, std::move(gd), yforward(cur));
659664 #endif
660665 }
661666
667+//! \since build 910
668+//@{
662669 // XXX: A combination of an operative and its operand shall always be evaluated
663670 // in a tail context. However, for a combiner call, sometimes it is not in the
664671 // tail context in the enclosing context. If the call needs no creation of
@@ -667,8 +674,14 @@
667674 // and this can be an optimization.
668675 #if false
669676 YB_FLATTEN inline ReductionStatus
670-RelayCombined(ContextNode& ctx, TermNode& term, shared_ptr<Environment> p_env)
677+ReduceCombindEnv(TermNode& term, ContextNode& ctx,
678+ shared_ptr<Environment> p_env)
671679 {
680+ // NOTE: The next term is set here unless direct inlining call of
681+ // %ReduceCombinedBranch is used.
682+#if !NPL_Impl_NPLA1_Enable_InlineDirect
683+ SetupNextTerm(ctx, term);
684+#endif
672685 // NOTE: %ReduceFirst and other passes would not be called again. The
673686 // unwrapped combiner should have been evaluated and it would not be
674687 // wrongly evaluated again.
@@ -681,19 +694,57 @@
681694 }
682695 #endif
683696
684-YB_FLATTEN inline ReductionStatus
685-RelayCombinedTail(ContextNode& ctx, TermNode& term,
697+// XXX: Do not use %YB_FLATTEN here for LTO compiling performance.
698+//! \pre TCO 实现:当前动作是 TCO 动作,且其中的当前项和被规约的项相同。
699+inline ReductionStatus
700+RelayCombindEnvTail(ContextNode& ctx, TermNode& term,
686701 shared_ptr<Environment> p_env)
687702 {
688- // NOTE: See %RelayCombined.
689- // NOTE: Term is set in %A1::RelayNextGuardedTail, even for direct
690- // inlining call of %ReduceCombinedBranch.
703+ // NOTE: The precondition is similar to the last call in
704+ // %EvalImplUnchecked in the presense of the assumption that %term is from
705+ // the current term saved in the context.
691706 return A1::RelayNextGuardedTail(ctx, term,
692707 EnvironmentGuard(ctx, ctx.SwitchEnvironment(std::move(p_env))),
693708 std::ref(ReduceCombinedBranch));
694709 }
710+
711+// XXX: Do not use %YB_FLATTEN here for LTO compiling performance.
712+//! \pre TCO 实现:当前动作是 TCO 动作,且其中的当前项和被规约的项相同。
713+inline ReductionStatus
714+ReduceCombindEnvTail(TermNode& term, ContextNode& ctx,
715+ shared_ptr<Environment> p_env)
716+{
717+ // NOTE: See %ReduceCombindEnv.
718+#if !NPL_Impl_NPLA1_Enable_InlineDirect
719+ SetupNextTerm(ctx, term);
720+#endif
721+ return RelayCombindEnvTail(ctx, term, std::move(p_env));
722+}
723+
724+//! \pre TCO 实现:当前动作非 TCO 动作。
725+YB_FLATTEN inline ReductionStatus
726+ReduceCombindEnvNonTail(TermNode& term, ContextNode& ctx,
727+ shared_ptr<Environment> p_env)
728+{
729+#if NPL_Impl_NPLA1_Enable_TCO
730+ YAssert(!AccessTCOAction(ctx), "Non-tail context expected.");
731+#endif
732+ // XXX: Assume %next is not a %TCOAction.
733+ ctx.LastStatus = ReductionStatus::Neutral;
734+ // NOTE: This does not rely on the existence of the %TCOAction, as it
735+ // will call %EnsureTCOAction. Since the call would rely on %TCOAction,
736+ // anyway, creating the TCO action here instead of a new action
737+ // to restore the environment is more efficient.
738+#if NPL_Impl_NPLA1_Enable_TCO
739+ SetupTailTCOAction(ctx, term, {});
740+#endif
741+ // NOTE: The precondition is same to the current continuation call in
742+ // %ReduceCombindEnvTail.
743+ return ReduceCombindEnvTail(term, ctx, std::move(p_env));
744+}
695745 //@}
696746
747+
697748 /*!
698749 \param no_lift 指定是否避免保证规约后提升结果。
699750 \since build 835
@@ -710,6 +761,9 @@
710761 ResolveTerm([&](TermNode& nd, ResolvedTermReferencePtr p_ref){
711762 LiftOtherOrCopy(term, nd, NPL::IsMovable(p_ref));
712763 }, NPL::Deref(i));
764+ // NOTE: On %NPL_Impl_NPLA1_Enable_TCO, this assumes %term is same to the
765+ // current term in %TCOAction, which is initialized by %CombinerReturnThunk
766+ // in NPLA1.cpp.
713767 return RelayForEvalOrDirect(ctx, term, EnvironmentGuard(ctx,
714768 ctx.SwitchEnvironment(std::move(p_env))), no_lift,
715769 std::ref(ContextState::Access(ctx).ReduceOnce));
@@ -980,7 +1034,7 @@
9801034 {
9811035 TermNode eval_struct(std::move(Deref(p_eval_struct)));
9821036 auto& act(RefTCOAction(ctx));
983- const bool no_lifting(NoLifting);
1037+ const bool no_lift(NoLifting);
9841038
9851039 save(*this, act);
9861040 // XXX: This would make '*this' invalid.
@@ -989,7 +1043,9 @@
9891043 // would be used immediately in %RelayForCall. The pointer is moved
9901044 // to indicate the error condition when it is called again.
9911045 LiftOther(term, eval_struct);
992- return RelayForCall(ctx, term, std::move(gd), no_lifting);
1046+ // NOTE: The precondition is same to the last call in
1047+ // %EvalImplUnchecked.
1048+ return RelayForCall(ctx, term, std::move(gd), no_lift);
9931049 }
9941050 else
9951051 term.SetContent(Deref(p_eval_struct));
@@ -997,6 +1053,7 @@
9971053 // XXX: Ditto.
9981054 LiftOtherOrCopy(term, Deref(p_eval_struct), move);
9991055 #endif
1056+ // NOTE: Ditto.
10001057 return RelayForCall(ctx, term, std::move(gd), NoLifting);
10011058 }
10021059
@@ -1036,8 +1093,7 @@
10361093 SaveOwningPtr(shared_ptr<Environment>& p_static, TCOAction& act)
10371094 {
10381095 if(p_static.use_count() == 1)
1039- act.RecordList.emplace_front(ContextHandler(),
1040- std::move(p_static));
1096+ act.RecordList.emplace_front(ContextHandler(), std::move(p_static));
10411097 }
10421098 #endif
10431099 };
@@ -1230,10 +1286,9 @@
12301286 return EvaluateToLValueReference(term, p_env);
12311287 }
12321288
1233-//! \since build 897
1234-//@{
12351289 // NOTE: The bound term cannot be reused later because %term can be the
12361290 // referent.
1291+//! \since build 897
12371292 YB_ATTR_nodiscard TermNode
12381293 EvaluateBoundLValueMoved(TermNode& term, const shared_ptr<Environment>& p_env)
12391294 {
@@ -1263,6 +1318,7 @@
12631318 }
12641319
12651320 // NOTE: See %BindMoveLocalObjectInPlace and %EvaluateBoundLValueMoved.
1321+//! \since build 897
12661322 YB_ATTR_nodiscard TermNode
12671323 EvaluateLocalObjectMoved(TermNode& o, const shared_ptr<Environment>& p_env)
12681324 {
@@ -1278,6 +1334,7 @@
12781334 return EvaluateToLValueReference(o, p_env);
12791335 }
12801336
1337+//! \since build 897
12811338 void
12821339 ForwardToUnwrapped(TermNode& comb, ContextNode& ctx)
12831340 {
@@ -1287,10 +1344,9 @@
12871344 std::ref(comb), std::ref(ctx), _1, _2), ThrowForUnwrappingFailure));
12881345 }
12891346
1290-//! \since build 899
1291-//@{
12921347 // XXX: Preserving %con is better for performance, at least in code generation
12931348 //`by x86_64-pc-linux G++ 10.2.
1349+//! \since build 899
12941350 YB_ATTR_nodiscard TermNode
12951351 EvaluateBoundUnwrappedLValueDispatch(TermNode::allocator_type a,
12961352 const TermNode::Container& con, TermReference ref, ContextNode& ctx,
@@ -1315,6 +1371,7 @@
13151371 }
13161372
13171373 // NOTE: As %EvaluateBoundLValue and %ForwardToUnwrapped.
1374+//! \since build 899
13181375 YB_ATTR_nodiscard TermNode
13191376 EvaluateBoundLValueUnwrapped(TermNode& term, ContextNode& ctx,
13201377 const shared_ptr<Environment>& p_env)
@@ -1325,12 +1382,10 @@
13251382 return EvaluateBoundUnwrappedLValueDispatch(term.get_allocator(),
13261383 {}, TermReference(term.Tags, term, NPL::Nonnull(p_env)), ctx, term);
13271384 }
1328-//@}
1329-
1330-//! \since build 875
1331-//@{
1385+
13321386 // NOTE: As %ReduceSubsequent.
13331387 // NOTE: This does not guarantee PTC.
1388+//! \since build 875
13341389 template<typename _fCurrent>
13351390 ReductionStatus
13361391 ReduceCallSubsequent(TermNode& term, ContextNode& ctx,
@@ -1342,16 +1397,7 @@
13421397 // implementation.
13431398 return A1::ReduceCurrentNext(term, ctx,
13441399 std::bind([](shared_ptr<Environment>& p_e, TermNode& t, ContextNode& c){
1345-#if true
1346- // NOTE: This does not rely on the existence of the %TCOAction, as it
1347- // will call %EnsureTCOAction. Since the call would rely on %TCOAction,
1348- // anyway, creating the TCO action here instead of a new action
1349- // to restore the environment is more efficient.
1350- SetupTailContext(c, t);
1351- return RelayCombinedTail(c, t, std::move(p_e));
1352-#else
1353- return RelayCombined(c, t, std::move(p_e));
1354-#endif
1400+ return ReduceCombindEnvNonTail(t, c, std::move(p_e));
13551401 }, std::move(p_env), _1, _2), yforward(next));
13561402 }
13571403
@@ -1370,7 +1416,8 @@
13701416 std::bind([](shared_ptr<Environment>& p_e, TermNode& t, ContextNode& c){
13711417 // NOTE: This does not calls %EnsureTCOAction, so an %TCOAction shall be
13721418 // already existed in the context.
1373- return RelayCombinedTail(c, t, std::move(p_e));
1419+ // NOTE: See the precondition for %ReduceCombindEnvTail.
1420+ return ReduceCombindEnvTail(t, c, std::move(p_e));
13741421 }, std::move(p_env), _1, _2), yforward(next));
13751422 }
13761423 #endif
@@ -1387,7 +1434,6 @@
13871434 ThrowInsufficientTermsError(nd, p_ref);
13881435 }, term);
13891436 }
1390-//@}
13911437
13921438 //! \since build 859
13931439 void
@@ -1417,6 +1463,48 @@
14171463 }, term);
14181464 }
14191465
1466+//! \since build 910
1467+void
1468+DoFirstRestVal(TermNode& term, TermNode& tm, bool has_ref)
1469+{
1470+ // XXX: Simple 'ReduceToValue(term, tm)' is wrong because it may move
1471+ // non-unqiue reference object away.
1472+ if(const auto p = NPL::TryAccessLeaf<const TermReference>(tm))
1473+ LiftMovedOther(term, *p, !has_ref && p->IsMovable());
1474+ else
1475+ // XXX: Term tags are currently not respected in prvalues.
1476+ LiftOtherOrCopy(term, tm, !has_ref);
1477+}
1478+
1479+//! \since build 910
1480+template<typename _fLift, typename _fInsert>
1481+YB_FLATTEN ReductionStatus
1482+RestOrVal(TermNode& term, _fLift lift, _fInsert insert)
1483+{
1484+ return CallResolvedUnary([&](TermNode& nd, ResolvedTermReferencePtr p_ref){
1485+ if(IsBranchedList(nd))
1486+ {
1487+ TermNode::Container con(term.get_allocator());
1488+
1489+ auto first(nd.begin());
1490+ const auto last(nd.end());
1491+
1492+ ++first;
1493+ if(!p_ref || p_ref->IsMovable() || p_ref->IsTemporary())
1494+ {
1495+ lift(nd);
1496+ con.splice(con.end(), nd.GetContainerRef(), first, last);
1497+ }
1498+ else
1499+ for(; first != last; ++first)
1500+ insert(con.emplace_back(), *first, p_ref);
1501+ con.swap(term.GetContainerRef());
1502+ return ReductionStatus::Retained;
1503+ }
1504+ ThrowInsufficientTermsError(nd, p_ref);
1505+ }, term);
1506+}
1507+
14201508 //! \since build 834
14211509 template<typename _func>
14221510 void
@@ -1702,8 +1790,7 @@
17021790 return WrapUnwrap(term,
17031791 [&](FormContextHandler& fch, ResolvedTermReferencePtr p_ref){
17041792 return fch.Wrapping == 0 ? fwrap(term, ctx, p_ref, fch, 1)
1705- : ThrowForWrappingFailure(
1706- ystdex::type_id<FormContextHandler>());
1793+ : ThrowForWrappingFailure(ystdex::type_id<FormContextHandler>());
17071794 }, [](const ContextHandler& h) YB_ATTR_LAMBDA(noreturn) -> ReductionStatus{
17081795 ThrowForWrappingFailure(h.target_type());
17091796 });
@@ -1921,7 +2008,9 @@
19212008
19222009 ConsItem(expr, NPL::Deref(++i));
19232010 term = std::move(expr);
1924- return RelayCombinedTail(ctx, term, std::move(p_env));
2011+ // NOTE: The precondition is same to the last call in %EvalImplUnchecked.
2012+ // See also the precondition for %RelayCombindEnvTail.
2013+ return RelayCombindEnvTail(ctx, term, std::move(p_env));
19252014 }
19262015
19272016 //! \since build 860
@@ -2046,8 +2135,7 @@
20462135 [&](TermNode& t, ContextNode& c){
20472136 return AccRImpl(t, c, sum);
20482137 }, A1::NameTypedReducerHandler([&, d]() YB_FLATTEN{
2049- SetupTailContext(ctx, term);
2050- return RelayCombinedTail(ctx, term, d);
2138+ return ReduceCombindEnvNonTail(term, ctx, d);
20512139 }, "eval-accr-sum"));
20522140 }, "eval-accr-accr"));
20532141 }, term, ctx);
@@ -2131,7 +2219,7 @@
21312219 const auto& d(ctx.GetRecordPtr());
21322220 auto lv_l(EvaluateLocalObject(l, d));
21332221
2134- // XXX: Keep %lv_l is a slightly more efficient.
2222+ // XXX: Keeping %lv_l is a slightly more efficient.
21352223 return NPL::ResolveTerm(
21362224 [&, d](TermNode& nd, ResolvedTermReferencePtr p_ref) -> ReductionStatus{
21372225 if(!IsEmpty(nd))
@@ -2147,8 +2235,7 @@
21472235 [&](TermNode& t, ContextNode& c){
21482236 return FoldR1Impl(t, c, kons);
21492237 }, A1::NameTypedReducerHandler([&, d]() YB_FLATTEN{
2150- SetupTailContext(ctx, term);
2151- return RelayCombinedTail(ctx, term, d);
2238+ return ReduceCombindEnvNonTail(term, ctx, d);
21522239 }, "eval-foldr1-kons"));
21532240 }
21542241 LiftOther(term, knil);
@@ -2395,9 +2482,9 @@
23952482 }
23962483
23972484 bool
2398-Not(TermNode& term)
2485+Not(TermNode& x)
23992486 {
2400- auto& tm(NPL::ReferenceTerm(term));
2487+ auto& tm(NPL::ReferenceTerm(x));
24012488
24022489 return IsLeaf(tm) && tm.Value == false;
24032490 }
@@ -2478,7 +2565,8 @@
24782565 #else
24792566 term.GetContainerRef().pop_front();
24802567 #endif
2481- return RelayCombinedTail(ctx, term, std::move(p_e0));
2568+ // NOTE: See the precondition for %ReduceCombindEnvTail.
2569+ return ReduceCombindEnvTail(term, ctx, std::move(p_e0));
24822570 }
24832571 else
24842572 ThrowInsufficientTermsError(nd, p_ref);
@@ -2553,17 +2641,42 @@
25532641 FirstVal(TermNode& term)
25542642 {
25552643 return FirstOrVal(term, [&](TermNode& tm, bool has_ref){
2556- // XXX: Simple 'ReduceToValue(term, tm)' is wrong because it may
2557- // move non-unqiue reference object away.
2558- if(const auto p = NPL::TryAccessLeaf<const TermReference>(tm))
2559- LiftMovedOther(term, *p, !has_ref && p->IsMovable());
2560- else
2561- // XXX: Term tags are currently not respected in prvalues.
2562- LiftOtherOrCopy(term, tm, !has_ref);
2644+ DoFirstRestVal(term, tm, has_ref);
25632645 return ReductionStatus::Retained;
25642646 });
25652647 }
25662648
2649+ReductionStatus
2650+Rest(TermNode& term)
2651+{
2652+ return RestOrVal(term, [](TermNode&) ynothrow{},
2653+ [&](TermNode& dst, TermNode& tm, ResolvedTermReferencePtr p_ref){
2654+ LiftOtherOrCopy(dst, tm, NPL::IsMovable(p_ref));
2655+ });
2656+}
2657+
2658+ReductionStatus
2659+RestRef(TermNode& term)
2660+{
2661+ return CallResolvedUnary([&](TermNode& nd, ResolvedTermReferencePtr p_ref){
2662+ CheckResolvedListReference(nd, p_ref);
2663+
2664+ TermNode::Container con(term.get_allocator());
2665+
2666+ for(auto i(std::next(nd.begin())); i != nd.end(); ++i)
2667+ // XXX: Same to %FirstRef.
2668+ ReduceToReference(con.emplace_back(), *i, p_ref);
2669+ con.swap(term.GetContainerRef());
2670+ return ReductionStatus::Retained;
2671+ }, term);
2672+}
2673+
2674+ReductionStatus
2675+RestVal(TermNode& term)
2676+{
2677+ return RestOrVal(term, LiftSubtermsToReturn, DoFirstRestVal);
2678+}
2679+
25672680 void
25682681 SetFirst(TermNode& term)
25692682 {
diff -r 2d5fea7ba0cd -r c91b75c1e8bc YFramework/source/NPL/NPLA1Internals.cpp
--- a/YFramework/source/NPL/NPLA1Internals.cpp Sat Jan 30 14:55:15 2021 +0800
+++ b/YFramework/source/NPL/NPLA1Internals.cpp Sat Feb 06 16:04:05 2021 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2018-2020 FrankHB.
2+ © 2018-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 NPLA1Internals.cpp
1212 \ingroup NPL
1313 \brief NPLA1 内部接口。
14-\version r20290
14+\version r20372
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 473
1717 \par 创建时间:
1818 2020-02-15 13:20:08 +0800
1919 \par 修改时间:
20- 2020-08-11 14:20 +0800
20+ 2021-01-31 21:26 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 非公开模块名称:
@@ -26,8 +26,8 @@
2626
2727
2828 #include "NPL/YModules.h"
29-#include "NPLA1Internals.h" // for NPL::Deref, Environment, shared_ptr,
30-// std::make_move_iterator, NPL::AsTermNode;
29+#include "NPLA1Internals.h" // for NPL::Deref, Environment, ystdex::dismiss,
30+// shared_ptr, std::make_move_iterator, NPL::AsTermNode;
3131
3232 using namespace YSLib;
3333
@@ -42,53 +42,6 @@
4242
4343 #if NPL_Impl_NPLA1_Enable_Thunked
4444 # if NPL_Impl_NPLA1_Enable_TCO
45-ReductionStatus
46-TCOAction::operator()(ContextNode& ctx) const
47-{
48- YAssert(ystdex::ref_eq<>()(EnvGuard.func.Context.get(), ctx),
49- "Invalid context found.");
50-
51- // NOTE: Lifting is optional, but is shall be performed before release
52- // of guards. See also $2018-02 @ %Documentation::Workflow.
53- const auto res(HandleResultRequests(TermRef, ctx));
54-
55- // NOTE: The order here is significant. The environment in the guards
56- // should be hold until lifting is completed.
57- {
58- const auto egd(std::move(EnvGuard));
59- }
60- while(!xgds.empty())
61- xgds.pop_back();
62- // NOTE: This should be after the guards to ensure there is no term
63- // references the environment.
64- while(!RecordList.empty())
65- {
66- // NOTE: The order is significant, as %FrameRecord destruction now
67- // is unspecified, and temporary objects have dependencies on
68- // environments.
69- auto& front(RecordList.front());
70-
71- get<ActiveCombiner>(front) = {};
72- RecordList.pop_front();
73- }
74- return res;
75-}
76-
77-
78-TCOAction&
79-EnsureTCOAction(ContextNode& ctx, TermNode& term)
80-{
81- auto p_act(AccessTCOAction(ctx));
82-
83- if(!p_act)
84- {
85- SetupTailTCOAction(ctx, term, {});
86- p_act = AccessTCOAction(ctx);
87- }
88- return NPL::Deref(p_act);
89-}
90-
91-
9245 void
9346 RecordCompressor::Compress()
9447 {
@@ -167,6 +120,82 @@
167120 return collected;
168121 });
169122 }
123+
124+
125+ReductionStatus
126+TCOAction::operator()(ContextNode& ctx) const
127+{
128+ YAssert(ystdex::ref_eq<>()(EnvGuard.func.Context.get(), ctx),
129+ "Invalid context found.");
130+
131+ // NOTE: Many orders are siginificant, see %Documentation::NPL. The comments
132+ // here only specify the implementation-dependent ones.
133+ // NOTE: Lifting is optional, but it shall be performed before release
134+ // of guards. See also $2018-02 @ %Documentation::Workflow.
135+ const auto res(HandleResultRequests(ctx));
136+
137+ ystdex::dismiss(term_guard);
138+ {
139+ const auto egd(std::move(EnvGuard));
140+ }
141+ while(!xgds.empty())
142+ xgds.pop_back();
143+ while(!RecordList.empty())
144+ {
145+ // NOTE: The order is significant, as the destruction order of
146+ // components of %FrameRecord is unspecified.
147+ auto& front(RecordList.front());
148+
149+ get<ActiveCombiner>(front) = {};
150+ RecordList.pop_front();
151+ }
152+ return res;
153+}
154+
155+void
156+TCOAction::CompressFrameList()
157+{
158+ auto i(RecordList.cbegin());
159+
160+ ystdex::retry_on_cond(ystdex::id<>(), [&]() -> bool{
161+ const auto orig_size(RecordList.size());
162+
163+ // NOTE: The following code searches the frames to be removed, in the
164+ // order from new to old. After merging, the guard slot %EnvGuard owns
165+ // the resources of the expression (and its enclosed subexpressions)
166+ // being TCO'd.
167+ i = RecordList.cbegin();
168+ while(i != RecordList.cend())
169+ {
170+ auto& p_frame_env_ref(NPL::get<ActiveEnvironmentPtr>(
171+ *ystdex::cast_mutable(RecordList, i)));
172+
173+ if(p_frame_env_ref.use_count() != 1
174+ || NPL::Deref(p_frame_env_ref).IsOrphan())
175+ // NOTE: The whole frame is to be removed. The function prvalue
176+ // is expected to live only in the subexpression evaluation.
177+ // This has equivalent effects of evlis tail recursion.
178+ i = RecordList.erase(i);
179+ else
180+ ++i;
181+ }
182+ return RecordList.size() != orig_size;
183+ });
184+
185+}
186+
187+TCOAction&
188+EnsureTCOAction(ContextNode& ctx, TermNode& term)
189+{
190+ auto p_act(AccessTCOAction(ctx));
191+
192+ if(!p_act)
193+ {
194+ SetupTailTCOAction(ctx, term, {});
195+ p_act = AccessTCOAction(ctx);
196+ }
197+ return NPL::Deref(p_act);
198+}
170199 # endif
171200 #endif
172201
diff -r 2d5fea7ba0cd -r c91b75c1e8bc YFramework/source/NPL/NPLA1Internals.h
--- a/YFramework/source/NPL/NPLA1Internals.h Sat Jan 30 14:55:15 2021 +0800
+++ b/YFramework/source/NPL/NPLA1Internals.h Sat Feb 06 16:04:05 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Internals.h
1212 \ingroup NPL
1313 \brief NPLA1 内部接口。
14-\version r20236
14+\version r20579
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 882
1717 \par 创建时间:
1818 2020-02-15 13:20:08 +0800
1919 \par 修改时间:
20- 2021-01-30 13:45 +0800
20+ 2021-02-04 00:46 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 非公开模块名称:
@@ -31,6 +31,7 @@
3131 #include "YModules.h"
3232 #include YFM_NPL_NPLA1 // for ContextNode, TermNode, ContextState,
3333 // ReductionStatus, Reducer, NPL::tuple, YSLib::get, list, EnvironmentGuard,
34+// std::declval, std::ref, make_observer, std::bind, ystdex::unique_guard,
3435 // RegularizeTerm, EnvironmentReference, ystdex::cast_mutable, TermReference,
3536 // ystdex::get_less, YSLib::map, set, ystdex::retry_on_cond, ystdex::id,
3637 // A1::NameTypedReducerHandler, A1::NameTypedContextHandler;
@@ -109,202 +110,6 @@
109110 }
110111
111112 # if NPL_Impl_NPLA1_Enable_TCO
112-/*!
113-\brief 帧记录索引。
114-\note 顺序保持和 FrameRecord 的元素对应一致。
115-\since build 842
116-*/
117-enum RecordFrameIndex : size_t
118-{
119- ActiveCombiner,
120- ActiveEnvironmentPtr
121-};
122-
123-/*!
124-\brief 帧记录。
125-\note 成员顺序和 RecordFrameIndex 中的项对应。
126-\since build 842
127-\sa RecordFrameIndex
128-*/
129-using FrameRecord = NPL::tuple<ContextHandler, shared_ptr<Environment>>;
130-
131-/*!
132-\brief 帧记录列表。
133-\sa FrameRecord
134-\since build 827
135-*/
136-using FrameRecordList = yimpl(list)<FrameRecord>;
137-
138-//! \since build 818
139-class TCOAction final
140-{
141-public:
142- //! \since build 893
143- lref<TermNode> TermRef;
144-
145-private:
146- //! \since build 854
147- //@{
148- bool req_combined = {};
149- bool req_lift_result = {};
150- //@}
151- //! \since build 827
152- mutable list<ContextHandler> xgds;
153-
154-public:
155- // See $2018-06 @ %Documentation::Workflow for details.
156- //! \since build 825
157- mutable observer_ptr<const ContextHandler> LastFunction{};
158- //! \since build 820
159- mutable EnvironmentGuard EnvGuard;
160- //! \since build 825
161- mutable FrameRecordList RecordList;
162- //! \since build 896
163- mutable ValueObject OperatorName;
164-
165- //! \since build 819
166- TCOAction(ContextNode& ctx, TermNode& term, bool lift)
167- : TermRef(term), req_lift_result(lift), xgds(ctx.get_allocator()),
168- EnvGuard(ctx), RecordList(ctx.get_allocator()),
169- OperatorName(ctx.get_allocator())
170- {
171- YAssert(term.Value.type() == ystdex::type_id<TokenValue>()
172- || !term.Value, "Invalid value for combining term found.");
173- OperatorName = std::move(term.Value);
174- // XXX: After the move, %term.Value is unspecified.
175- }
176- // XXX: Not used, but provided for well-formness.
177- //! \since build 819
178- TCOAction(const TCOAction& a)
179- // XXX: Some members are moved. This is only safe when newly constructed
180- // object always live longer than the older one.
181- : TermRef(a.TermRef), req_combined(a.req_combined),
182- req_lift_result(a.req_lift_result), xgds(std::move(a.xgds)),
183- EnvGuard(std::move(a.EnvGuard))
184- {}
185- DefDeMoveCtor(TCOAction)
186-
187- DefDeMoveAssignment(TCOAction)
188-
189- //! \since build 877
190- ReductionStatus
191- operator()(ContextNode&) const;
192-
193- //! \since build 857
194- void
195- AddRecord(shared_ptr<Environment>&& p_env)
196- {
197- // NOTE: The temporary function and the environment are saved in the
198- // frame record list as a new entry.
199- RecordList.emplace_front(MoveFunction(), std::move(p_env));
200- }
201-
202- //! \since build 825
203- YB_ATTR_nodiscard lref<const ContextHandler>
204- AttachFunction(ContextHandler&& h)
205- {
206- // NOTE: This scans guards to hold function prvalues, which are safe to
207- // be removed as per the equivalence (hopefully, of beta reduction)
208- // defined by %operator== of the handler. No new instance is to be
209- // added.
210- ystdex::erase_all(xgds, h);
211- xgds.emplace_back();
212- // NOTE: Strong exception guarantee is kept here.
213- swap(xgds.back(), h);
214- return ystdex::as_const(xgds.back());
215- }
216-
217- //! \since build 893
218- ReductionStatus
219- HandleResultRequests(TermNode& term, ContextNode& ctx) const
220- {
221- // NOTE: This implies the call of %RegularizeTerm before lifting. Since
222- // the call of %RegularizeTerm is idempotent without term modification
223- // before the next reduction of term, there is no need to call
224- // %RegularizeTerm if the lift is not needed.
225- if(req_lift_result)
226- {
227- // NOTE: The call of %RegularizeTerm is for the previous reduction.
228- // The order of the calls is significant.
229- RegularizeTerm(term, ctx.LastStatus);
230- return ReduceForLiftedResult(term);
231- }
232- // NOTE: This is only needed on a real call from the evaluation is
233- // reentered. Currently, other evaluations (e.g. for continuation are
234- // all administrative) and expected not reentered with unbound number
235- // of times when no lifting is required.
236- // TODO: Prepare for invocation of first-class continuations?
237- if(req_combined)
238- RegularizeTerm(term, ctx.LastStatus);
239- return ctx.LastStatus;
240- }
241-
242- //! \since build 825
243- YB_ATTR_nodiscard ContextHandler
244- MoveFunction()
245- {
246- ContextHandler res(std::allocator_arg, xgds.get_allocator());
247-
248- if(LastFunction)
249- {
250- const auto i(std::find_if(xgds.rbegin(), xgds.rend(),
251- [this](const ContextHandler& h) ynothrow{
252- return NPL::make_observer(&h) == LastFunction;
253- }));
254-
255- if(i != xgds.rend())
256- {
257- res = ContextHandler(std::allocator_arg, xgds.get_allocator(),
258- std::move(*i));
259- xgds.erase(std::next(i).base());
260- }
261- LastFunction = {};
262- }
263- return res;
264- }
265-
266- //! \since build 854
267- //@{
268- void
269- RequestCombined()
270- {
271- req_combined = true;
272- }
273-
274- void
275- RequestLiftResult()
276- {
277- req_lift_result = true;
278- }
279- //@}
280-};
281-
282-//! \since build 886
283-YB_ATTR_nodiscard YB_PURE inline
284- PDefH(TCOAction*, AccessTCOAction, ContextNode& ctx) ynothrow
285- ImplRet(ctx.AccessCurrentAs<TCOAction>())
286-// NOTE: There is no need to check term like 'if(&p->TermRef.get() == &term)'.
287-// It should be same to saved enclosing term unless a nested TCO action is
288-// needed explicitly (by following %SetupTailAction rather than
289-// %EnsureTCOAction).
290-
291-//! \since build 840
292-YB_ATTR_nodiscard YB_FLATTEN TCOAction&
293-EnsureTCOAction(ContextNode& ctx, TermNode& term);
294-
295-//! \since build 840
296-YB_ATTR_nodiscard inline PDefH(TCOAction&, RefTCOAction, ContextNode& ctx)
297- // NOTE: The TCO action should have been created by a previous call of
298- // %EnsureTCOAction, typically in the call of %CombinerReturnThunk in
299- // calling a combiner from %ReduceCombinedBranch.
300- ImplRet(NPL::Deref(AccessTCOAction(ctx)))
301-
302-//! \since build 886
303-inline
304- PDefH(void, SetupTailTCOAction, ContextNode& ctx, TermNode& term, bool lift)
305- ImplExpr(SetupTailAction(ctx, TCOAction(ctx, term, lift)))
306-
307-
308113 //! \since build 827
309114 struct RecordCompressor final
310115 {
@@ -410,69 +215,157 @@
410215 }
411216 };
412217
413-// NOTE: See $2018-06 @ %Documentation::Workflow and $2019-06 @
414-// %Documentation::Workflow for details.
415-//! \since build 861
416-inline void
417-CompressTCOFrames(ContextNode& ctx, TCOAction& act)
418-{
419- auto& record_list(act.RecordList);
420- auto i(record_list.cbegin());
421-
422- ystdex::retry_on_cond(ystdex::id<>(), [&]() -> bool{
423- const auto orig_size(record_list.size());
424-
425- // NOTE: The following code searches the frames to be removed, in the
426- // order from new to old. After merging, the guard slot %EnvGuard owns
427- // the resources of the expression (and its enclosed subexpressions)
428- // being TCO'd.
429- i = record_list.cbegin();
430- while(i != record_list.cend())
431- {
432- auto& p_frame_env_ref(NPL::get<ActiveEnvironmentPtr>(
433- *ystdex::cast_mutable(record_list, i)));
434218
435- if(p_frame_env_ref.use_count() != 1
436- || NPL::Deref(p_frame_env_ref).IsOrphan())
437- // NOTE: The whole frame is to be removed. The function prvalue
438- // is expected to live only in the subexpression evaluation.
439- // This has equivalent effects of evlis tail recursion.
440- i = record_list.erase(i);
441- else
442- ++i;
443- }
444- return record_list.size() != orig_size;
445- });
446- RecordCompressor(ctx.GetRecordPtr()).Compress();
447-}
219+/*!
220+\brief 帧记录索引。
221+\note 顺序保持和 FrameRecord 的元素对应一致。
222+\since build 842
223+*/
224+enum RecordFrameIndex : size_t
225+{
226+ ActiveCombiner,
227+ ActiveEnvironmentPtr
228+};
448229
449-//! \since build 878
450-//@{
451230 /*!
452-\brief 准备 TCO 求值。
453-\param ctx 规约上下文。
454-\param term 被规约的项。
455-\param act TCO 动作。
231+\brief 帧记录。
232+\note 成员顺序和 RecordFrameIndex 中的项对应。
233+\since build 842
234+\sa RecordFrameIndex
456235 */
457-inline TCOAction&
458-PrepareTCOEvaluation(ContextNode& ctx, TermNode& term, EnvironmentGuard&& gd)
236+using FrameRecord = NPL::tuple<ContextHandler, shared_ptr<Environment>>;
237+
238+/*!
239+\brief 帧记录列表。
240+\sa FrameRecord
241+\since build 827
242+*/
243+using FrameRecordList = yimpl(list)<FrameRecord>;
244+
245+//! \since build 818
246+class TCOAction final
459247 {
460- auto& act(RefTCOAction(ctx));
248+private:
249+ // NOTE: Specialized guard type (instead of using %ystdex::unique_guard) is
250+ // more efficient here.
251+ // XXX: More specialized guard type without %ystdex::unique_guard works, but
252+ // it is acually less efficient, at least on x86_64-pc-linux G++ 10.2.
253+ //! \since build 910
254+ struct GuardFunction final
255+ {
256+ //! \brief 当前项引用。
257+ lref<TermNode> TermRef;
461258
462- [&]{
259+ PDefHOp(void, (), ) const ynothrow
260+ ImplExpr(TermRef.get().Clear())
261+ };
262+
263+ //! \since build 909
264+ mutable decltype(ystdex::unique_guard(std::declval<GuardFunction>()))
265+ term_guard;
266+ //! \since build 910
267+ mutable size_t req_lift_result = 0;
268+ //! \since build 827
269+ mutable list<ContextHandler> xgds;
270+
271+public:
272+ // See $2018-06 and $2021-02 @ %Documentation::Workflow for details.
273+ //! \since build 825
274+ mutable observer_ptr<const ContextHandler> LastFunction{};
275+ //! \since build 820
276+ mutable EnvironmentGuard EnvGuard;
277+ //! \since build 825
278+ mutable FrameRecordList RecordList;
279+ //! \since build 896
280+ mutable ValueObject OperatorName;
281+
282+public:
283+ //! \since build 819
284+ TCOAction(ContextNode& ctx, TermNode& term, bool lift)
285+ : term_guard(ystdex::unique_guard(GuardFunction{term})),
286+ req_lift_result(lift ? 1 : 0), xgds(ctx.get_allocator()), EnvGuard(ctx),
287+ RecordList(ctx.get_allocator()), OperatorName(ctx.get_allocator())
288+ {
289+ YAssert(term.Value.type() == ystdex::type_id<TokenValue>()
290+ || !term.Value, "Invalid value for combining term found.");
291+ OperatorName = std::move(term.Value);
292+ // XXX: After the move, %term.Value is unspecified.
293+ }
294+ // XXX: Not used, but provided for well-formness.
295+ //! \since build 819
296+ TCOAction(const TCOAction& a)
297+ // XXX: Some members are moved. This is only safe when newly constructed
298+ // object always live longer than the older one.
299+ : term_guard(std::move(a.term_guard)),
300+ req_lift_result(a.req_lift_result), xgds(std::move(a.xgds)),
301+ EnvGuard(std::move(a.EnvGuard))
302+ {}
303+ DefDeMoveCtor(TCOAction)
304+ // XXX: Out of line destructor here is inefficient.
305+
306+ DefDeMoveAssignment(TCOAction)
307+
308+ //! \since build 877
309+ ReductionStatus
310+ operator()(ContextNode&) const;
311+
312+ //! \since build 909
313+ DefGetter(const ynothrowv, TermNode&, TermRef, term_guard.func.func.TermRef)
314+
315+ //! \since build 857
316+ void
317+ AddRecord(shared_ptr<Environment>&& p_env)
318+ {
319+ // NOTE: The temporary function and the environment are saved in the
320+ // frame record list as a new entry.
321+ RecordList.emplace_front(MoveFunction(), std::move(p_env));
322+ }
323+
324+ //! \since build 825
325+ YB_ATTR_nodiscard lref<const ContextHandler>
326+ AttachFunction(ContextHandler&& h)
327+ {
328+ // NOTE: This scans guards to hold function prvalues, which are safe to
329+ // be removed as per the equivalence (hopefully, of beta reduction)
330+ // defined by %operator== of the handler. No new instance is to be
331+ // added.
332+ ystdex::erase_all(xgds, h);
333+ xgds.emplace_back();
334+ // NOTE: Strong exception guarantee is kept here.
335+ swap(xgds.back(), h);
336+ return ystdex::as_const(xgds.back());
337+ }
338+
339+ //! \since build 910
340+ //@{
341+ void
342+ CompressFrameList();
343+
344+ // NOTE: See $2018-06 @ %Documentation::Workflow and $2019-06 @
345+ // %Documentation::Workflow for details.
346+ void
347+ CompressForContext(ContextNode& ctx)
348+ {
349+ CompressFrameList();
350+ RecordCompressor(ctx.GetRecordPtr()).Compress();
351+ }
352+
353+ void
354+ CompressForGuard(ContextNode& ctx, EnvironmentGuard&& gd)
355+ {
463356 // NOTE: If there is no environment set in %act.EnvGuard yet, there is
464357 // ideally no need to save the components to the frame record list
465358 // for recursive calls. In such case, each operation making
466359 // potentionally overwriting of %act.LastFunction will always get into
467360 // this call and that time %act.EnvGuard should be set.
468- if(act.EnvGuard.func.SavedPtr)
361+ if(EnvGuard.func.SavedPtr)
469362 {
470363 // NOTE: Operand saving is performed whether the frame compression
471364 // is needed, once there is a saved environment set.
472365 if(auto& p_saved = gd.func.SavedPtr)
473366 {
474- CompressTCOFrames(ctx, act);
475- act.AddRecord(std::move(p_saved));
367+ CompressForContext(ctx);
368+ AddRecord(std::move(p_saved));
476369 return;
477370 }
478371 // XXX: Normally this should not occur, but this is allowed by the
@@ -480,35 +373,152 @@
480373 // without an environment).
481374 }
482375 else
483- act.EnvGuard = std::move(gd);
376+ EnvGuard = std::move(gd);
484377 // XXX: Not with a guarded tail environment, setting the environment to
485378 // empty.
486- act.AddRecord({});
487- }();
488- // NOTE: The lift is handled according to the previous status of
489- // %act.LiftCallResult, rather than a seperated boolean value (e.g.
490- // the parameter %no_lift in %RelayForEval).
491- act.HandleResultRequests(term, ctx);
492- return act;
493-}
379+ AddRecord({});
380+ }
381+
382+ /*!
383+ \brief 处理操作压缩的结果请求。
384+ \pre 规约上下文的最后一次规约状态表示的是当前项上的状态。
385+ \sa ContextNode::LastStatus
386+ */
387+ ReductionStatus
388+ HandleResultRequests(ContextNode& ctx) const
389+ {
390+ // NOTE: If this is called properly, %ctx.LastStatus should be
391+ // maintained to refer to the reduction status of the right term by
392+ // %PushedAction::operator() in NPLA1.cpp.
393+ // NOTE: This implies the call of %RegularizeTerm before lifting. Since
394+ // the call of %RegularizeTerm is idempotent without term modification
395+ // before the next reduction of term, there is no need to call
396+ // %RegularizeTerm if the lift is not needed.
397+ if(req_lift_result != 0)
398+ {
399+ // NOTE: This is for the previous reduction. The order of the calls
400+ // is significant.
401+ RegularizeTerm(GetTermRef(), ctx.LastStatus);
402+ for(; req_lift_result != 0; --req_lift_result)
403+ LiftToReturn(GetTermRef());
404+ return ReductionStatus::Retained;
405+ }
406+ // NOTE: This is only needed on a real call from the evaluation is
407+ // reentered. Currently, other evaluations (e.g. for continuation are
408+ // all administrative) and expected not reentered with unbound number
409+ // of times when no lifting is required. However, since the TCO action
410+ // should have been initialized in a call whose resouces to be reused,
411+ // this should be normally unconditionally true.
412+ // TODO: Prepare for invocation of first-class continuations?
413+ RegularizeTerm(GetTermRef(), ctx.LastStatus);
414+ return ctx.LastStatus;
415+ }
416+ //@}
417+
418+ //! \since build 825
419+ YB_ATTR_nodiscard ContextHandler
420+ MoveFunction()
421+ {
422+ ContextHandler res(std::allocator_arg, xgds.get_allocator());
423+
424+ if(LastFunction)
425+ {
426+ const auto i(std::find_if(xgds.rbegin(), xgds.rend(),
427+ [this](const ContextHandler& h) ynothrow{
428+ return NPL::make_observer(&h) == LastFunction;
429+ }));
430+
431+ if(i != xgds.rend())
432+ {
433+ res = ContextHandler(std::allocator_arg, xgds.get_allocator(),
434+ std::move(*i));
435+ xgds.erase(std::next(i).base());
436+ }
437+ LastFunction = {};
438+ }
439+ return res;
440+ }
441+
442+ /*
443+ \brief 按参数设置 TCO 动作提升请求。
444+ \param act TCO 动作。
445+ \param no_lift 指定是否避免保证规约后提升结果。
446+ \since build 910
447+ */
448+ void
449+ SetupTCOLift(bool no_lift) const
450+ {
451+ // NOTE: The flag indicates a request for handling during next time (by
452+ // the %HandleResultRequests call above before the last one) before
453+ // %TCOAction is finished. The last request would be handled by
454+ // %operator(), which also calls %HandleResultRequests.
455+ if(!no_lift)
456+ {
457+ ++req_lift_result;
458+ if(YB_UNLIKELY(req_lift_result == 0))
459+ throw NPLException(
460+ "TCO action lift request count overflow detected.");
461+ }
462+ }
463+};
464+
465+//! \since build 886
466+YB_ATTR_nodiscard YB_PURE inline
467+ PDefH(TCOAction*, AccessTCOAction, ContextNode& ctx) ynothrow
468+ ImplRet(ctx.AccessCurrentAs<TCOAction>())
469+// NOTE: There is no need to check term like
470+// 'if(&p->GetTermPtr().get() == &term)'. It should be same to saved enclosing
471+// term unless a nested TCO action is needed explicitly (by following
472+// %SetupTailAction rather than %EnsureTCOAction).
473+
474+//! \since build 840
475+YB_ATTR_nodiscard YB_FLATTEN TCOAction&
476+EnsureTCOAction(ContextNode& ctx, TermNode& term);
494477
495478 /*!
496-\brief 按参数设置 TCO 动作提升请求。
479+\pre 当前动作是 TCO 动作。
480+\since build 840
481+*/
482+YB_ATTR_nodiscard inline PDefH(TCOAction&, RefTCOAction, ContextNode& ctx)
483+ // NOTE: The TCO action should have been created by a previous call of
484+ // %EnsureTCOAction, typically in the call of %CombinerReturnThunk in
485+ // calling a combiner from %ReduceCombinedBranch.
486+ ImplRet(NPL::Deref(AccessTCOAction(ctx)))
487+
488+//! \since build 886
489+inline
490+ PDefH(void, SetupTailTCOAction, ContextNode& ctx, TermNode& term, bool lift)
491+ ImplExpr(SetupTailAction(ctx, TCOAction(ctx, term, lift)))
492+
493+
494+/*!
495+\brief 准备 TCO 求值。
496+\param ctx 规约上下文。
497+\param term 被求值项。
497498 \param act TCO 动作。
498-\param no_lift 指定是否避免保证规约后提升结果。
499+\pre 当前动作是 TCO 动作,且其中的当前项和被规约的项相同。
500+\since build 878
501+
502+访问现有的 TCO 动作进行操作压缩,以复用其中已被分配的资源。
499503 */
500-inline void
501-SetupTCOLift(TCOAction& act, bool no_lift)
504+inline TCOAction&
505+PrepareTCOEvaluation(ContextNode& ctx, TermNode& term, EnvironmentGuard&& gd)
502506 {
503- // NOTE: The %act.LiftCallResult indicates a request for handling during
504- // next time (by %TCOAction::HandleResultRequests call above before the
505- // last one) before %TCOAction is finished. The last request would be
506- // handled by %TCOAction::operator(), which also calls
507- // %TCOAction::HandleResultRequests.
508- if(!no_lift)
509- act.RequestLiftResult();
507+ auto& act(RefTCOAction(ctx));
508+
509+ YAssert(&act.GetTermRef() == &term,
510+ "Invalid term for target TCO action found.");
511+ yunused(term);
512+ // NOTE: The lift is handled according to the previous status of
513+ // the lifting request in %act, rather than a seperated boolean value (e.g.
514+ // the parameter %no_lift in %RelayForEval).
515+ // NOTE: As %TCOAction::operator(), the order is significant. Otherwise,
516+ // wrong environments would be destroyed and there could be dangling
517+ // references.
518+ act.HandleResultRequests(ctx);
519+ act.CompressForGuard(ctx, std::move(gd));
520+ return act;
510521 }
511-//@}
512522 # endif
513523
514524 //! \since build 879
@@ -632,10 +642,10 @@
632642 # endif
633643 }
634644
635-//! \since build 898
645+//! \since build 910
636646 template<typename _fCurrent, typename _fNext>
637647 YB_FLATTEN inline ReductionStatus
638-RelayCurrentNext(TermNode& term, ContextNode& ctx, _fCurrent&& cur,
648+RelayCurrentNext(ContextNode& ctx, TermNode& term, _fCurrent&& cur,
639649 _fNext&& next)
640650 {
641651 #if NPL_Impl_NPLA1_Enable_Thunked
@@ -643,6 +653,7 @@
643653 RelaySwitched(ctx, yforward(next));
644654 return A1::RelayDirect(ctx, yforward(cur), term);
645655 # else
656+ yunused(term);
646657 return A1::RelayNextOrDirect(ctx, yforward(cur), yforward(next));
647658 # endif
648659 #else
@@ -656,18 +667,21 @@
656667 // XXX: This is a workaround for G++'s LTO bug.
657668 #if YB_IMPL_GNUCPP >= 100000 || !NPL_Impl_NPLA1_Enable_Thunked \
658669 || NPL_Impl_NPLA1_Enable_TCO
659-YB_FLATTEN
670+YB_FLATTEN
660671 #endif
661672 inline ReductionStatus
662673 ReduceCurrentNext(TermNode& term, ContextNode& ctx, _fCurrent&& cur,
663674 _fNext&& next)
664675 {
665676 SetupNextTerm(ctx, term);
666- return RelayCurrentNext(term, ctx, yforward(cur), yforward(next));
677+ return A1::RelayCurrentNext(ctx, term, yforward(cur), yforward(next));
667678 }
668679 //@}
669680
670-//! \since build 878
681+/*!
682+\pre TCO 实现:当前动作是 TCO 动作,且其中的当前项和被规约的项相同。
683+\since build 878
684+*/
671685 template<typename _fNext>
672686 ReductionStatus
673687 RelayForEvalOrDirect(ContextNode& ctx, TermNode& term, EnvironmentGuard&& gd,
@@ -680,7 +694,7 @@
680694 // in direct calls instead of the setup next term, while they shall be
681695 // equivalent.
682696 #if NPL_Impl_NPLA1_Enable_TCO
683- SetupTCOLift(PrepareTCOEvaluation(ctx, term, std::move(gd)), no_lift);
697+ PrepareTCOEvaluation(ctx, term, std::move(gd)).SetupTCOLift(no_lift);
684698 return A1::RelayCurrentOrDirect(ctx, yforward(next), term);
685699 #elif NPL_Impl_NPLA1_Enable_Thunked
686700 // TODO: Blocked. Use C++14 lambda initializers to simplify the
@@ -688,7 +702,7 @@
688702 auto act(MakeMoveGuard(gd));
689703
690704 if(no_lift)
691- return A1::RelayCurrentNext(term, ctx, yforward(next), std::move(act));
705+ return A1::RelayCurrentNext(ctx, term, yforward(next), std::move(act));
692706
693707 // XXX: Term reused. Call of %SetupNextTerm is not needed as the next
694708 // term is guaranteed not changed when %next is a continuation.
@@ -698,7 +712,7 @@
698712 }, "eval-lift-result"), ctx);
699713
700714 RelaySwitched(ctx, std::move(act));
701- return A1::RelayCurrentNext(term, ctx, yforward(next), std::move(cont));
715+ return A1::RelayCurrentNext(ctx, term, yforward(next), std::move(cont));
702716 #else
703717 yunused(gd);
704718
diff -r 2d5fea7ba0cd -r c91b75c1e8bc doc/ChangeLog.V0.9.txt
--- a/doc/ChangeLog.V0.9.txt Sat Jan 30 14:55:15 2021 +0800
+++ b/doc/ChangeLog.V0.9.txt Sat Feb 06 16:04:05 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file ChangeLog.V0.9.txt
1212 \ingroup Documentation
1313 \brief 版本更新历史记录 - V0.9 。
14-\version r2324
14+\version r2531
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 800
1717 \par 创建时间:
1818 2020-10-12 17:19:23 +0800
1919 \par 修改时间:
20- 2021-01-30 14:48 +0800
20+ 2021-02-06 15:35 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -32,6 +32,210 @@
3232
3333 $now
3434 (
35+ / DLDI 'friend YB_PURE' -> 'YB_PURE friend' $effective @ "%operator=="
36+ @ "class template %WrappedContextHandler" @ %YFramework.NPL.NPLA1,
37+ // More consistent to other code.
38+ / %YFramework.NPL $=
39+ (
40+ / %NPLA1 $=
41+ (
42+ * "missing saving %ContextNode::LastStatus before passes"
43+ @ "member function %ContextState::DefaultReduceOnce"
44+ @ 'NPL_Impl_NPLA1_Enable_Thunked'
45+ $orig (@ "function %ReduceOnce" $since b841),
46+ / DD "restricted precondition" @ 'NPL_Impl_NPLA1_Enable_TCO'
47+ @ "functions %(RelayForEval, RelayForCall)",
48+ / @ "functions %(ReduceCombinedBranch, ReduceCombinedReferent)" $=
49+ (
50+ / "removed combined call request" @ 'NPL_Impl_NPLA1_Enable_TCO'
51+ $dep_from "reset last status before applicative calls"
52+ $dep_to "removal of combined call request",
53+ / "support destroying temporary objects on exception"
54+ $dep_from ("temporary guard" @ "class %TCOAction"
55+ @ 'NPL_Impl_NPLA1_Enable_TCO' @ %NPLA1Interals)
56+ ),
57+ * "nonmodifying list subobjects wrongly moved on list \
58+ initialization" @ "function %BindParameter" $since b898
59+ ),
60+ / @ "namespace %Forms" @ %NPLA1Forms $=
61+ (
62+ * DD "comment" @ ("function %Not" $since b861,
63+ "functions %(First, FirstVal)" $since b859),
64+ * "missing reset %ContextNode::LastStatus before the sum call"
65+ @ ("function %AccR" $since b898, "function %FoldR1" $since
66+ b899) $dep_to "reset last status before applicative calls",
67+ // See $2021-02 @ %Documentation::Workflow.
68+ / DLDI "simplified resetting %ContextNode::LastStatus before \
69+ non-tail calls of applicative argument"
70+ @ "functions %(AccR, FoldR1)" $dep_to
71+ "reset last status before applicative calls",
72+ // See $2021-02 @ %Documentation::Workflow.
73+ / DLDI "parameter name" @ "function %Not" -> 'x' ~ 'term',
74+ // To indicate the parameter represents an operand in the \
75+ object language.
76+ * "missing setup the next term" @ "functions of native \
77+ implementations of applicatives with applicative arguments"
78+ @ '!NPL_Impl_NPLA1_Enable_InlineDirect \
79+ && NPL_Impl_NPLA1_Enable_Thunk' $since b898,
80+ // See $2021-02 @ %Documentation::Workflow.
81+ + "assertion to ensure the term same to the next term"
82+ @ "function %ReduceN",
83+ (
84+ + "function %(Rest, RestVal)";
85+ / DLDI "simplified function %(First, FirstVal)"
86+ ),
87+ + "function %RestRef"
88+ ),
89+ / @ 'NPL_Impl_NPLA1_Enable_TCO' @ %NPLA1Internals $=
90+ (
91+ / @ "function %PrepareTCOEvaluation" $=
92+ (
93+ + "assertion to ensure identities of term in the TCO \
94+ action and the parameter are same" $dep_from
95+ ("%(RelayForEval, RelayForCall)" @ %NPLA1)
96+ $dep_to "assertion of TCO preparation"
97+ ^ $dep_from "%TCOAction::GetTermRef",
98+ // See $2021-02 @ %Documentation::Workflow.
99+ / DLDI "simplified"
100+ ^ $dep_from "%TCOAction::CompressForGuard",
101+ * "result handling did not come before frame compression"
102+ $since b878 $dep_to "order of TCO result handling"
103+ // See $2021-02 @ %Documentation::Workflow.
104+ ),
105+ / DLDI "classes declaration order",
106+ // For easier in-class inlining definition in %TCOAction.
107+ / @ "class %TCOAction" $=
108+ (
109+ + "function %GetTermRef",
110+ / DLI @ "function %operator()" $=
111+ (
112+ / "simplified term" @ "result handling"
113+ $dep_from "assertion of TCO preparation",
114+ / $forced $design "simplified"
115+ $dep_from "simplified result request"
116+ ),
117+ / @ "function %HandleResultRequests" $=
118+ (
119+ - "term parameter" $dep_from "%operator()"
120+ $dep_to "simplified result request",
121+ - "combiner request check" $dep_all_from
122+ "reset last status before applicative calls"
123+ $dep_to "removed combiner request check",
124+ * "missing reset lifting result request" $orig (@ %NPLA1
125+ $since b854) $dep_from ("order of TCO result handling"
126+ @ 'list-concat' @ "%LoadGroundContext" @ %Dependency),
127+ // See $2021-02 @ %Documentation::Workflow.
128+ * "missing support of lifting more than once"
129+ $since b854
130+ // See $2021-02 @ %Documentation::Workflow.
131+ ),
132+ + "functions %(CompressFrameList; \
133+ CompressForContext; CompressForGuard)",
134+ + "function %HandleResultLift",
135+ + "function %SetupTCOLift",
136+ (
137+ - "function %RequestCombined"
138+ $dep_from "removal of combined call request";
139+ - DLI "combined call resquest data member"
140+ $dep_from "removed combiner request check"
141+ ),
142+ - "function %RequestLiftResult"
143+ $dep_from "%A1::SetupTCOLift",
144+ (
145+ + "temporary guard";
146+ - "data member %TermRef"
147+ )
148+ ),
149+ (
150+ / DLDI @ "function template %RelayForEvalOrDirect"
151+ ^ $dep_from "%TCOAction::SetupTCOLift"
152+ ~ "%SetupTCOLift";
153+ - "function %SetupTCOLift"
154+ ),
155+ / "parameter order" @ "function template %RelayCurrentNext"
156+ // To be consisten with other API prefixed with 'Relay'.
157+ ),
158+ / %Dependency $=
159+ (
160+ / @ "function %LoadGroundContext" $=
161+ (
162+ + "native derivation enabled by default" @ "applicatives \
163+ 'make-standard-environment', 'first-null?'",
164+ * "missing fowrading the 2nd argument"
165+ @ "applicative 'list-concat'" $since b859,
166+ // See $2021-02 @ %Documentation::Workflow.
167+ + "native derivation of applicatives enabled by default"
168+ @ ("%restv" ^ $dep_from ("%RestVal" @ %NPLA1Forms),
169+ 'rest&' ^ $dep_from ("%RestRef" @ %NPLA1Forms)),
170+ (
171+ + "native derivation enabled by default"
172+ @ "applicative 'rest%'"
173+ $dep_from (("%BindParameter", ^ "%Rest") @ %NPLA1Forms);
174+ * $comp "inconsistent internal derivation of 'rest%' used"
175+ $since b909
176+ // This should have 'move!' in the body.
177+ ),
178+ + "%forward call for the argument"
179+ @ "applicative 'list-rest%'",
180+ / DLDI "order of derivation" @ "operative $as-environment"
181+ // This does use '$let', so put it later.
182+ ),
183+ / DLDI "all parameter name for handler used by \
184+ 'RegisterUnary<>' call" -> 'x' ~ 'term',
185+ // Similar to %Forms::Not.
186+ / "applicative %load" @ "function %LoadModule_std_io" $=
187+ (
188+ / DLI "simplified" @ 'NPL_Impl_NPLA1_Enable_Thunked'
189+ ^ "%ReduceToLoadExternal" ~ "%RelayToLoadExternal"
190+ $dep_to "avoided use of RelayToLoadExternal",
191+ / DLDI "simplified by removing %RetainN call"
192+ $dep_from "refined arguments for loading"
193+ ),
194+ // %RelayToLoadExternal is preserved for external \
195+ implementations.
196+ / DLI @ "function %ReduceToLoadExternal" $=
197+ (
198+ + $dep_from "%Forms::RetainN call"
199+ $dep_to "refined arguments for loading",
200+ / "avoid redundant next term setup"
201+ ^ "%ContextState::ReduceOnce" ~ "%ReduceOnce"
202+ // The consistency is now guaranteed by the NPLA1 \
203+ reduction function requirements.
204+ )
205+ )
206+ ),
207+ / %YBase.YStandardEx $=
208+ (
209+ / @ "binary operator templates" @ %Operators $=
210+ (
211+ + "%std::move calls in pass-by-value arguments",
212+ / @ "left binary operator templates" $=
213+ (
214+ * "inconsistent parameter types for operator function templates"
215+ $since b576,
216+ // One parameter type should be '_type' without 'const&' \
217+ to be aligned with the non-left ones.
218+ * "missing specializing template with 2 same template arguments"
219+ $since b260
220+ // This would break left binary operator templates \
221+ combining with non-left ones due to ambiguity.
222+ )
223+ ),
224+ / @ "%operator(+=, -=)" @ "class template %transformed_iterator" $=
225+ (
226+ / "simplified exception specifications",
227+ (
228+ / "friend non-member overloads" -> "member overloads";
229+ // This is more idiomadic, also fixing the following bug.
230+ * $comp "inability to be accessed by modifiable rvalue objects"
231+ $since b600
232+ )
233+ )
234+ )
235+),
236+
237+b909
238+(
35239 / %YFramework.NPL $=
36240 (
37241 / %NPLA $=
@@ -145,10 +349,7 @@
145349 / DLI "simplified"
146350 @ 'NPL_Impl_NPLA1_Enable_Thunked && !NPL_Impl_NPLA1_Enable_TCO'
147351 @ "function %ReduceOrdered"
148- ^ "%A1::RelayCurrentNext" ~ "%A1::ReduceCurrentNext",
149- / "support destroying temporary objects on exception"
150- @ "functions %(ReduceCombinedBranch, ReduceCombinedReferent)"
151- $dep_from ("TCO term cleanup" @ %NPLA1Interals)
352+ ^ "%A1::RelayCurrentNext" ~ "%A1::ReduceCurrentNext"
152353 ),
153354 / %Dependency $=
154355 (
diff -r 2d5fea7ba0cd -r c91b75c1e8bc doc/NPL.txt
--- a/doc/NPL.txt Sat Jan 30 14:55:15 2021 +0800
+++ b/doc/NPL.txt Sat Feb 06 16:04:05 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPL.txt
1212 \ingroup Documentation
1313 \brief NPL 规范和实现规格说明。
14-\version r19454
14+\version r19650
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 304
1717 \par 创建时间:
1818 2012-04-25 10:34:20 +0800
1919 \par 修改时间:
20- 2021-01-30 14:24 +0800
20+ 2021-02-05 15:58 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -1575,6 +1575,8 @@
15751575 @5.2.4.2 求值和对象所有权:
15761576 被求值的表达式的内部表示即项(@5.5) 或环境(@5.4.3) 中的对象具有 NPLA 对象的所有权。
15771577 和宿主语言类似,NPLA 临时对象(@5.5.6) 的存储未指定,但部分临时对象被项所有。
1578+求值结束而不被使用的项的资源在求值终止时被释放,包括被项独占所有权的这些临时对象。
1579+类似宿主语言,求值终止包括可被实现确定的异常(@4.7.1) 退出。
15781580 对名义上被项所有的临时对象,必要时实现可分配内部存储转移项(包括在环境中分配),以满足附加要求(如 @5.2.5 )。
15791581 和宿主语言类似,对象的所有权随可随对象被转移,且被转移对象后的项具有有效但未指定(valid but unspecified) 的状态,参见 @5.5.2.3 和 @5.5.3 。
15801582 环境对绑定具有的所有权是独占的。绑定对其中的对象可具有独占或共享的所有权。因此,环境可对绑定中的对象具有独占或共享的所有权。
@@ -1997,7 +1999,7 @@
19971999 以 TermNode 对象的形式使其子项和值数据成员被转移(转移后的项具有有效但未指定的状态(@5.2.4.2) 但不可在对象语言中被访问);
19982000 以未指定的其它实现相关的方式使 TermNode 成为其它节点的子项(不存在转移后的项)。
19992001 项的转移引起对象的析构性转移(destructive move) :对象在转移后被销毁,其生存期结束。
2000-和对象的转移不同,因为被转移的对象的生存期结束,项的转移不保证被转移后的项中的对象的类型不改变。
2002+和对象的转移(@5.5.2.3) 不同,因为被转移的对象的生存期结束,项的转移不保证被转移后的项中的对象的类型不改变。
20012003 除了不初始化新的对象和使被转移项中对象的类型改变,项的转移在对象语言中效果可能类似宿主对象的转移。
20022004 项的转移保证不无效化(@5.4.2) 子项,也不转移或复制值数据成员(@5.4.2) 所有的宿主对象。
20032005
@@ -2528,7 +2530,7 @@
25282530
25292531 @5.10.1 异步规约动作(@5.4.4) :
25302532 NPLA 中,这通过以规约动作(@5.4.4) 作为 CPS(@5.4.4) 的被跳板(trampoline) 执行的中间值(@4.4.4.2) 例程的机制实现。此时,当前动作(@5.4.4) 是尾动作(@5.4.4) 。
2531-跳板例程设置的规约操作将被异步调用(@5.4.4) ,即当前动作设置为异步动作,在跳板中被循环调用。上下文的重写循环(@5.4.4) 提供一个跳板调用当前动作(@5.4.4) 。
2533+跳板例程设置的规约操作将被异步调用(@5.4.4) ,即当前动作(@5.4.4) 设置为异步动作,在跳板中被循环调用。上下文的重写循环(@5.4.4) 提供一个跳板调用当前动作(@5.4.4) 。
25322534 这种方式支持嵌套调用安全(@5.2.7) 。其它方式实现可能具有更好的性能,但存在限制而不被使用,如:
25332535 依赖不可移植的宿主语言扩展及实现假定,
25342536 对对象语言源代码中使用的语言构造(特别是影响控制流的合并子(@4.5.3.2) )进行限制,使用单独的阶段进行抽象求值(@4.1) 。(这允许使用静态 TCO (@5.2.6.4.2) 所需的变换。)
@@ -2952,6 +2954,7 @@
29522954 @6.9.4 上下文 API :
29532955 类型 ReducerFunctionType
29542956 类型 NPL::Reducer 是以上下文引用作为参数的规约器(@5.10.6) ,保存动作(@5.4.4) 。
2957+NPL::Reduer 能以兼容 ReducerFuntionType 的方式调用,可选提供一个 ContextNode& 参数。
29552958 类 ContextNode 提供其它一些上下文 API 。
29562959 成员类型 ExceptionHandler
29572960 成员类 ReducerSequence
@@ -2995,7 +2998,7 @@
29952998 @6.9.6 上下文和环境相关类型:
29962999 NPL::EnvironmentSwitcher 类型用于切换上下文(@6.9.4) 中的当前环境(@5.4.4) 。
29973000
2998-@6.9.7 组合动作和扩展当前动作的操作:
3001+@6.9.7 组合动作和扩展当前动作(@5.4.4) 的操作:
29993002 以下 API 集中实现基于当前动作的复合操作作为中继(relay) ,用于异步规约(@5.10.1) :
30003003 函数模板 NPL::RelaySwitched 规约当前动作作为后继动作(@5.4.4) ,并在后继动作起始设置当前动作。
30013004 NPL::RelaySwitched 以当前动作或特定派生实现定义的其它动作调用时候,可支持 TCO(@5.2.6.4) 。
@@ -3159,17 +3162,17 @@
31593162 这个特性允许在对象语言的对应确定被求值的子项的表达式外添加匹配的括号而不改变语义,且在列表遍中可简化子项求值(如 @7.6.4 直接使用 ReduceCombined(@7.6.4.2) 而不需检查节点数量是否大于 1 )。
31603163 一般列表遍不依赖以上假设,可能对叶节点或具有一个节点的平凡列表节点对应的表达式求值。因此列表遍不是枝节点(@5.4.2.1) 遍。
31613164 遍的调用不一定需要维护共享状态,也因此一般多个遍被调用时不保证求值的强异常安全;其它情形仍然遵守一般约定([Documentation::YFramework @@3.2]) 。
3162-除 A1::ReduceCombined(@7.6.4.2) ,NPLA1 规约实现不保证进行正规化操作(@5.9.2) :
3165+除合并子调用创建操作(@7.6.4.2) ,NPLA1 规约实现不保证进行正规化操作(@5.9.2) :
31633166 在正规化操作不保证存在时,实现列表遍的用户代码需注意清理(@5.8.2.1) 不需要的子项。
31643167
31653168 @7.4.1.5 规约函数实现约定:
31663169 关于规约函数(@5.8.5) 的基本约定,参见规约函数形式约定(@6.5.3) 。
3167-对 NPLA1 使用的规约函数(@7.4.6)(不含 NPLA 中引入的规约函数(@5.8.5) ,下同),约定以下可被实现假定的使用要求:
3170+对 NPLA1 使用的规约函数(@7.4.6)(不含 NPLA 中引入的规约函数,下同),约定以下可被实现假定的使用要求:
31683171 若具有上下文参数,则上下文的动态类型是 A1::ContextState(@7.4.3) 或其 public 派生类。
31693172 若规约函数包含异步求值规约且不排除可能调用续延,规约函数还应满足:
31703173 若可能通过调用 A1::ContextState::ReduceOnce 调用续延(@7.4.2) ,应确保调用时下一求值项(@7.4.3) 被正确设置。
31713174 以下情形要求规约函数具有指定 NPLA1 上下文的参数,以此和被规约项作为参数调用 ContextState::SetNextTermRef 更新 NPLA1 上下文中保存的下一求值项:
3172- 对主规约函数(@7.4.4) 及名称包含通配符模式 Reduce*Subsequent 或 RewriteTerm 起始的规约函数,首先以 TermNode& 参数指定的当前项(@5.8) 更新下一求值项。
3175+ 对主规约函数(@7.4.4) 及名称包含通配符模式 ReduceCombined* 、Reduce*Subsequent 或 RewriteTerm 起始的规约函数,在下一步规约前以 TermNode& 参数指定的当前项(@5.8) 更新下一求值项。
31733176 发生和当前项不同的被规约项(通常表示子表达式)的规约,首先以新的被规约项更新下一求值项。
31743177 除以上要求以当前项更新下一求值项的函数,调用函数的当前项应和下一求值项相同。
31753178 使用不符合以上要求的实现的程序行为未定义。
@@ -3212,7 +3215,7 @@
32123215 DefaultReduceOnce
32133216
32143217 @7.4.4 NPLA1 主规约函数:
3215-NPLA1 规约以函数 A1::Reduce 和 A1::ReduceOnce 作为主规约函数(@6.5.3.3) 的参考实现,返回类型为描述一次规约调用操作结束后的状态的 ReductionStatus(@6.5.1) 。
3218+函数 A1::Reduce 和 A1::ReduceOnce 是 NPLA1 规约使用的主规约函数(@6.5.3.3) ,返回类型为描述一次规约调用操作结束后的状态的 ReductionStatus(@6.5.1) 。
32163219 A1::ReduceOnce 转发操作到 ContextState::ReduceOnce 的处理器调用,后者提供默认实现为 ContextState::DefaultReduceOnce(@7.4.3) 。
32173220 A1::Reduce 是 A1::ReduceOnce 的包装,最终调用后者以支持异步规约(@7.9) 。
32183221 @7.4 及以下各节中对 A1::ReduceOnce 的行为描述默认由 A1::ContextState::DefaultReduceOnce(@7.4.3) 实现。替换的实现应保持的要求相同。
@@ -3365,6 +3368,7 @@
33653368 作为被规约项,规约合并项的第一个子项应能规约到 A1::FormContextHandler(@7.6.1.1) 的值或引用 A1::FormContextHandler 对象的引用值(@5.5.4) 。
33663369
33673370 @7.6.4.2 规约合并求值:
3371+A1 中包含 ReduceCombined 前缀的函数名的规约函数(@5.8.5) 是合并子调用创建操作,可蕴含合并子调用所需资源的初始化。
33683372 函数 A1::ReduceCombined 对规约合并项(@7.6.4.1) 以已规约的第一个子项为上下文处理器(@7.6.1) 并返回调用结果,以节约严格性分析(@4.4.4.2) 开销;并根据处理器返回的规约结果进行正规化操作。
33693373 通过节点的值数据成员(@5.4.2) 是否持有 A1::ContextHandler 类型的值或引用到 A1::ContextHandler 的值上的 NPL::TermReference 的值确定是否为上下文处理器。
33703374 若存在 A1::ContextHandler 对象的子对象引用(@5.6.3.5) ,先变换子对象引用。这些子对象引用应符合 @5.9.4 约定的表示,否则断言检查失败。
@@ -3500,6 +3504,8 @@
35003504 绑定的默认行为不使用析构性转移(@5.5.3) 的操作(类似 [Rust] 的设计),原因是考虑到绑定的副作用影响操作数(即便因为对象被销毁而不一定是修改操作)和破坏幂等性(@4.1) (特别是指定过程调用的形式参数时,参见 @8.4.5 )违反易预测性原则(@1.4.5.2) 。
35013505 当前暂时不支持修改被绑定操作数。使用标记字符而修改操作数的绑定可能在未来支持。
35023506 具有引用标记字符的形式参数支持引入引用值并支持绑定引入临时对象的实际参数(@5.5.6) 。
3507+为允许调用宿主对象的转移构造函数(@5.5.2.3) ,仅在绑定临时对象到引用时使用复制消除(@5.5.6.3) 。
3508+(实际上,初始化引用之外的参数创建也不是 ISO C++17 约定要求消除复制的上下文。)
35033509 绑定临时对象时可能设置被绑定对象上的 TermTags::Temporary 标签(@7.7.3.2) 。
35043510
35053511 @7.7.3.5 递归绑定:
@@ -3514,8 +3520,10 @@
35143520 若子项是引用项,则子项的操作数标签不包含 TermTags::Temporary ;
35153521 子项的操作数标签是否包含 TermTags::Unique 标签应和所在的项的操作数标签中是否包含 TermTags::Unique 一致;
35163522 若所在的项的操作数标签包含 TermTags::Nonmodifying ,子项的操作数标签应包含 TermTags::Nonmodifying 标签。
3517-对引用项的 TermTags::Nonmodifying 标签的绑定规则和宿主语言中的非 mutable 类数据成员访问操作符决定 const 限定符的规则类似。
3518-绑定 TermTags::Temporary 标签可影响参数转发(@7.7.3.2) 。若需按类似宿主的成员表达式的值类别而不是成员是否为非左值引用进行转发,需确保被转发的值不是带有 TermTags::Temporary 的引用值。
3523+引用项的 TermTags::Nonmodifying 标签标记不可修改项而避免非临时对象的转移。
3524+这和宿主语言中的非 mutable 类数据成员访问操作符决定 const 限定符的规则类似。
3525+绑定需转移子项(包括绑定子项的复制消除(@7.7.3.4) )时,使用项的转移(@5.5.3) 。
3526+绑定 TermTags::Temporary 标签可影响参数转发(@7.7.3.2) 。若需按类似宿主语言的成员表达式的值类别而不是成员是否为非左值引用进行转发,需确保被转发的值不是带有 TermTags::Temporary 的引用值。
35193527 操作数标签中的 TermTags::Unique 由所在的项单独决定,而 TermTags::Temporary 仅在递归绑定时所在的所有列表项都是非引用值时包含。
35203528 子项标签继承规则保证使用 & 或 % 标记字符(@7.7.3.4) 时,值类别的决定规则和宿主语言的成员操作符类似:
35213529 列表左值中的元素总是被绑定为左值;
@@ -3530,9 +3538,8 @@
35303538 实现引入绑定的绑定操作(@7.7.3) 的 API 包括:
35313539 函数 A1::MatchParameter 匹配操作数树(@7.7.3) 。
35323540 函数 A1::BindParameter 匹配并绑定操作数到参数上。
3533-为允许调用宿主对象的转移构造函数,绑定操作(@7.7.3) 仅在特定的上下文中使用复制消除(@5.5.6.3) 。(实际上,初始化引用之外的参数创建也不是 ISO C++17 约定要求消除复制的上下文。)
35343541 对实际参数,检查尾部参数是否为省略(@7.7.3.3) 后,匹配前判断是否为 TermNode 以支持引用(@5.7.6.3) 。
3535-绑定尾部参数列表时,若对应的操作数是右值,直接转移子项,使用复制消除。
3542+绑定尾部参数列表时,若对应的操作数是右值,直接转移子项(@7.7.3.5) 。
35363543 一般地,对操作数树的递归操作应在分配资源失败时,引起(可能派生)std::bad_alloc 的宿主异常而非宿主语言的未定义行为(@5.2.2) 。
35373544 为支持嵌套调用安全的约定(@6.1) ,实现应避免无法预测嵌套深度的递归调用,但实现内调用的重定向操作以尾调用形式进行递归调用,类似使用跳板的异步规约动作(@5.10.1) 。
35383545 引入绑定的规则参见绑定操作(@7.7.3) 。
@@ -3616,15 +3623,16 @@
36163623 @7.9 异步(@5.10.1) 规约支持:
36173624 基本概念参见异步规约(@5.10) 。
36183625 在 NPLA1 API 中同时提供对应的完全非异步规约的实现,不满足 TCO 要求(@5.2.6.4) ,仅供参考。
3626+NPLA1 API 的异步规约依赖上下文中的当前动作(@5.4.4) 。
36193627 以下讨论的由上下文(@5.4.4) 支持的 NPLA API 使用方式和 NPLA1 相关。其它非 NPLA1 的派生实现可使用其它方式或选择不支持。
36203628
36213629 @7.9.1 和同步规约实现兼容性:
36223630 和同步实现的对应关系参见 @5.10.3 。
36233631 同步规约时,在当前被求值项(@5.5) 之后添加的操作可包括调用 A1::ReduceCombined(@7.6.4.2) 的清理操作(@5.8.2.1) 。
3624-和 NPL::CheckedReduceWith(@6.5.2) 单独判断规约结果保持循环条件不同,清理操作对应的异步规约默认不清除当前动作。
3632+和 NPL::CheckedReduceWith(@6.5.2) 单独判断规约结果(@5.8.1) 保持循环条件不同,清理操作对应的异步规约默认不清除当前动作。
36253633 这种实现允许通常没有 TCO 且不访问当前动作或不暴露当前动作状态改变的同步实现兼容被调函数实现的子项规约为异步操作的实现,即异步调用安全(asynchronous call safety) 。
36263634 允许这种兼容性有利于迁移 API 的实现至异步操作的实现,也允许支持按不同运行时配置启用 TCO 以平衡时间和空间开销及优化性能(但当前不考虑支持)。
3627-注意支持异步规约的实现不保证能被同步调用而维持当前动作状态的正确。这允许异步规约的实现中最后的操作以直接内联(direct inline) 同步的实现代替异步规约动作组合(@6.9.7) ;其主要的应用参见 @7.10.7 。
3635+注意支持异步规约的实现不保证能被同步调用而维持当前动作状态的正确。这允许异步规约的实现中最后的操作以直接内联(direct inline) 同步的实现代替异步规约动作组合(@6.9.7) ;其主要的应用参见 @7.9.4 。
36283636 例如,异步实现的规约合并求值(@7.6.4.2) 应仅在最后一遍的最后的操作中被同步调用。
36293637
36303638 @7.9.2 异步规约 API 支持:
@@ -3648,12 +3656,14 @@
36483656 A1::ReduceCombined
36493657 A1::ReduceFirst
36503658 A1::ReduceTail
3659+NPLA1 主规约函数(@7.4.4) 隐含设置下一求值项(@7.4.3) 以维护约定(@7.4.1.5) 。
36513660 A1::Reduce 通过 ContextState::RewriteGuarded(@7.4.3) 在内部实现异步规约,自身可被作为同步规约函数调用。
36523661 规约函数通过 A1::ReduceOnce 的实现设置上下文中的当前动作(@5.4.4) 支持异步规约相关操作。
36533662 和同步规约不同,异步的重规约(@7.4.4) 不由求值遍(@7.4.1.2) 根据规约状态处理。调用异步重规约的代码需插入 A1::ReduceOnce(@7.4.4) 作为起始动作引导重规约。
36543663 规约状态 ReductionStatus::Retrying(@6.5.1) 在 A1::ReduceOnce 的实现中被用于判断是否跳过当前求值的子表达式支持重规约。
3664+ContextState::ReduceOnce(@7.4.4) 的默认实现(及要求的替代实现)支持异步分发求值遍。
36553665 在每个表达式求值时,每个求值遍(@7.4.1.2) 的中间结果保存在 ContextNode::LastStatus(@6.9.4) 中,其中非 ReductionStatus::Partial(@6.5.1) 的结果被通过调用 CombineSequenceReductionResult 合并。
3656-在表达式求值规约中,A1::ReduceOnce 对 ContextNode::LastStatus(@6.9.4) 置为 ReductionStatus::Retrying(@6.5.1) ;之后再在求值后判断是否需要重规约。因为后续的求值是尾调用,可支持 TCO(@7.10) ;另见 @7.10.6 。
3666+在表达式求值规约中,A1::ReduceOnce 对 ContextNode::LastStatus(@6.9.4) 置为 ReductionStatus::Retrying(@6.5.1) ;之后再在求值后判断是否需要重规约。因为后续的求值是尾调用,可支持 TCO(@7.10) ;另见 @7.10.8 。
36573667 之后在判断应跳过上下文当前处理的表达式的剩余求值或没有剩余的求值遍时,求值完毕,再次置空。
36583668 A1::ReduceOnce 在完整遍的异步调用前设置 ContextNode::LastStatus 为 ReductionStatus::Neutral 以标识完整的遍的开始。
36593669 A1::ReduceOnce 在遍的结束时确保后续异步动作返回 ReductionStatus::Retrying ,以跳过多余的下一个不完整遍而支持异步重规约。
@@ -3684,6 +3694,15 @@
36843694 当前不提供 API 辅助实现本机函数中的临时对象维护。
36853695 按 @5.7.8 ,本机实现可对表示临时对象的子项分别转移,但转移前不应有非内部对象(@4.2.4.1) 引用被转移的子项。
36863696
3697+@7.9.4 动作内联(inlining) :
3698+特定的异步规约实现可能要求引入的操作总是尾动作(@5.4.4),否则行为未定义。
3699+这允许直接使用同步动作代替异步动作调用,即动作内联。
3700+动作内联可减少操作 ContextNode 中当前动作序列(@6.9.4) 的开销。
3701+注意这项要求具有传递性,即在宿主语言中直接或间接同步调用的操作也应满足要求的条件。
3702+因此,除非能确保不限制接口语义,当前 NPLA1 不使用这样的实现方式。
3703+启用使用这种方式的实现定义为实现选项(@7.1) NPL_Impl_NPLA1_Enable_InlineDirect 。
3704+派生实现应明确指定影响接口语义的动作内联的使用。
3705+
36873706 @7.10 TCO 支持:
36883707 因为要求对宿主语言中立(@5.2.6.4.1) ,基于 CPS 调用(@5.4.4) 的上下文 API 实现异步规约(@7.9) ,带有上下文状态的前置条件(@6.9.4) 。
36893708 这样的实现方式性能相对直接使用体系结构相关的方式较低,但仍可被接受。
@@ -3701,9 +3720,9 @@
37013720 NPLA1 实现支持的 TCO 包括以下形式:
37023721 直接在实现中变换宿主语言的递归调用为循环可实现宿主 TCO(@5.2.6.4.2) ;
37033722 基于异步规约 API(@7.9.2) ,使用重写规则(直接通过现有 API 支持的即重规约(@7.4.4) )代替直接使用宿主语言的递归形式可实现目标 TCO(@5.2.6.4.2) ;
3704-动态 TCO(@5.2.6.4.2) ,具体机制详见操作压缩(@7.10.3.2) 。
3723+动态 TCO(@5.2.6.4.2) ,具体机制详见 TCO 操作复用(@7.10.4) 。
37053724 NPLA 除个别例外(@6.1) 均支持宿主 TCO ,不需要其它形式的静态 TCO(@5.2.6.4.2) 。
3706-在此基础上,NPLA1 通过允许和动态 TCO 互操作的异步重规约的方式支持目标 TCO(@7.10.6) ;但当前 NPLA1 部分 API 不保证支持宿主 TCO(@6.1) ,这可能在以后完善。
3725+在此基础上,NPLA1 通过允许和动态 TCO 互操作的异步重规约的方式支持目标 TCO(@7.10.8) ;但当前 NPLA1 部分 API 不保证支持宿主 TCO(@6.1) ,这可能在以后完善。
37073726 NPLA1 支持的动态 TCO 按实现策略即 TCM(@5.2.6.4.2) 。
37083727 综上,使用 NPLA1 的 PTC 有三类实现策略:
37093728 通过直接重写宿主语言的实现(使用循环代替部分递归)提供宿主 TCO ;
@@ -3713,41 +3732,76 @@
37133732 @7.10.2 整体设计约束:
37143733 TCO 要求尾上下文(@4.4.7) 的续延(@4.5.2.1) 保持等效。考虑到计算资源的限制,排除构造相同的外部的求值环境(@4.6.1.1.2) 的实现方式,这蕴含同一性。
37153734 NPLA 的 TCO 基于异步规约(@7.9) ,基本方式是尾调用合并(@7.10) 。TCO 动作(@7.10.3) 是支持了尾调用合并的动作。
3716-考虑到当前动作预期直接构成当前续延(@6.9.4) ,对 NPLA1 ,这需要限制尾动作(@5.4.4) 的下一动作(@5.10.2) 不是 TCO 动作。
3735+考虑到当前动作(@5.4.4) 预期直接构成当前续延(@6.9.4) ,对 NPLA1 ,这需要限制尾动作(@5.4.4) 的下一动作(@5.10.2) 不是 TCO 动作。
37173736
37183737 @7.10.3 TCO 动作:
37193738 TCO 动作是用于实现 TCO 的动作帧(@5.10.6) 。
37203739 TCO 动作支持实现符合 PTC 的尾调用(@5.2.6.2) 的尾动作(@5.4.4) 。
3721-TCO 动作支持动态 TCO (@7.10.1) 。
3740+TCO 动作支持动态 TCO(@7.10.1) 。
37223741 TCO 动作具有特定的非公开类型。为简化实现,减小判断开销,当前是单一的非公开的类型。
3723-TCO 动作作为 NPLA 续延和之后在 PLDI 2020 公开的论文 https://doi.org/10.1145/3385412.3385981 中的 extra continuation frame 类似。
3724-TCO 动作可扩展实现类似 SRFI-157 的续延标记(continuation mark) 的特性。
3725-TCO 动作应在依赖 TCO 动作实现 PTC 的尾上下文时已被创建。若能静态确定创建,可不检查是否已经存在。
3726-
3727-@7.10.3.1 TCO 操作序列:
3728-TCO 动作可包含多项操作,典型地用于支持函数应用替换(@4.5.3) 或求值函数(@7.10.10) 的实现,包括如下序列:
3729-保存当前的环境并更新环境(@7.10.3.3) ;
3742+
3743+@7.10.3.1 组成:
3744+TCO 动作包含当前项的引用,以指定一些操作(@7.10.4) 的默认目标。
3745+TCO 动作也包含以下各节中描述的机制使用的状态:
3746+当前项作为临时对象的守卫(@7.10.7.4) ;
3747+操作结果(@7.10.6.2) 请求状态;
3748+已知的函数对象(@7.10.7.1) ;
3749+当前处理的尾环境(@7.10.6.1) 的守卫;
3750+帧记录列表(@7.10.5.1) 。
3751+此外,TCO 动作还包含用于诊断的操作数名称。
3752+当前项的引用可在当前项的守卫中蕴含。
3753+
3754+@7.10.3.2 创建:
3755+TCO 动作应在依赖 TCO 动作实现 PTC 的尾上下文时已被创建。若能静态确定创建,可不在之后的操作中检查是否已经存在。
3756+一般地,TCO 动作应通过合并子调用创建操作(@7.6.4.2) 或 A1::SetupTailContext(@7.8) 创建,以确保复用 TCO 资源的调用的存在。
3757+TCO 动作创建时初始化当前项(@7.10.3.1) 的引用,不在生存期内改变。
3758+
3759+@7.10.3.3 TCO 操作序列:
3760+典型地,用于支持函数应用替换(@4.5.3) 或求值函数(@7.10.10) 的实现,包括如下序列:
3761+保存当前的环境并更新环境(@7.10.6) ;
37303762 分配活动调用关联(@5.2.4.2) 的临时对象(@5.2.5) ;
3763+可选地请求异步重规约(@7.10.8) ;
3764+异步规约被替换或求值的项;
37313765 可选地提升项(变换函数应用替换的结果为非引用;当前具体实现在 A1::RelayForEval 或 A1::RelayForCall(@7.7.2) 内部提供);
3732-可选地请求异步重规约(@7.10.6) ;
3733-异步规约被替换或求值的项。
3734-对应的不支持 TCO 的异步实现中,每个操作都可以是一个动作。
3735-为减少判断 TCO 动作的复杂性,这些操作被合并到 TCO 动作内,TCO 动作的类型保持唯一。
3736-
3737-@7.10.3.2 操作压缩:
3738-上述操作中的一些是幂等的(@4.1) 。这些操作一一对应 TCO 动作内部的某个状态,表示是否存在此操作。
3739-当检查到 TCO 动作而需要继续补充上述操作时,不创建新的动作或分配其它资源,而修改当前 TCO 动作的状态表示启用对应的操作,在规约调用动作时生效。
3740-因为不同幂等的操作分配的资源不冲突,操作可以按类别归类,满足同类幂等操作的相对顺序不变即可。
3741-连续出现的幂等操作不受递归嵌套深度的影响。若操作序列(@7.10.3.1) 最后一项以外的操作都是幂等的,则可支持不受限制递归尾调用,即实现了 PTC 。
3766+结果的正规化(@5.9.2) 。
3767+对应的不支持 TCO 的异步实现中,以上每个操作都可以是一个动作。
3768+为减少判断 TCO 动作的复杂性,这些操作中的一部分被合并到 TCO 动作内,TCO 动作的类型保持唯一。
3769+这些动作在规约调用动作(即 NPL::Reducer::operator() (@6.9.4) )时被调用而生效,包括:
3770+处理调用结果(@7.10.6.2) ;
3771+释放当前处理的尾环境(@7.10.6.1) ;
3772+顺序释放所有已知的函数对象(@7.10.7.1) ;
3773+顺序释放帧记录列表(@7.10.5.1) ;
3774+返回调用处理调用结果的规约结果(@7.10.6.2) 。
3775+TCO 操作序列的顺序不是任意的。特别地:
3776+处理调用结果应在环境释放前进行;
3777+环境守卫(@7.10.3.1) 先于帧记录释放以确保项引用在帧记录释放前不再依赖帧记录中的环境强引用(@7.10.5.1) ;
3778+每个帧记录中,临时对象先于环境释放,以首先确保临时对象不再依赖对应的环境。
3779+
3780+@7.10.4 TCO 操作复用:
3781+TCO 操作序列(@7.10.3.3) 中的一些操作是幂等的(@4.1) 。这些操作一一对应 TCO 动作内部的某个状态,表示是否存在此操作。
3782+需要支持 TCO 的上下文检查当前动作(@5.4.4) 是否是 TCO 动作。当检查到 TCO 动作而需要继续补充上述操作时,不创建新的动作或分配其它资源,而修改当前 TCO 动作的状态表示启用对应的操作。
3783+启用的对应操作可即时生效。剩余的操作在规约调用动作时生效。生效时操作被调用。这种机制支持一个 TCO 动作上实现的对象语言中的多个合并子调用。
3784+因为不同幂等的操作分配的资源不冲突,操作可以按类别归类。
3785+上述多次生效的操作满足同类幂等操作的相对顺序不变即可,而不需保证实际调用的次数和非 TCO 情形一致。
3786+这种机制称为操作压缩(operation compression) 。
3787+操作压缩允许释放预期不引起行为改变的一些资源而对之后的操作进行准备,使之被保留的已再次分配的资源总量不超过先前调用使用的资源,而可能满足 PTC 的要求。
3788+调用处请求操作压缩前,需确保当前动作是 TCO 动作。
3789+一般地,操作压缩依序包含:
3790+处理调用结果(@7.10.6.2) ;
3791+扫描和回收资源(@7.10.5.2) 的帧压缩(frame compression) ;
3792+按需转移资源(@7.10.5.1) 。
3793+处理调用结果在其它操作之前。这允许首先移除调用结果中依赖可能在之后被释放的关联环境(@5.6.3.1) 的项引用。这和调用 TCO 的操作序列(@7.10.3.3) 中的顺序类似。
3794+当前设计中的操作压缩是同步操作。
3795+连续出现的幂等操作不受递归嵌套深度的影响。若操作序列(@7.10.3.3) 最后一项以外的操作都是幂等的,则可支持不受限制递归尾调用,即实现了 PTC 。
37423796 由于语义规则限制,不在任意情形下避免非幂等的操作而保证 PTC(这些操作随着嵌套深度的增加随之需要分配存储):
37433797 捕获动态环境(通过 vau 抽象(@4.5.2.3) 的创建(@8.4.5) )时,所在的环境记录(@5.4.4) 作为帧(@4.5.3.4) 占据的存储不保证有上界(unbounded) ;
37443798 包含非平凡析构(trivially destructible) 对象的帧的存储是否支持 PTC 未指定(另见 TCO 相关的生存期的实现注记(@7.11.5) )。
37453799
3746-@7.10.3.2.1 非幂等操作:
3800+@7.10.4.1 非幂等操作:
37473801 非幂等的操作和生存期相关,包括:
37483802 设置环境时,确保被设置环境的父环境(@5.4.3) 的生存期;
37493803 分配临时对象(@5.5.6) 时,确保临时对象最终能被及时释放;
3750-每次调用的调用结果都适用的附加的状态维护操作(@7.10.3.3) ,包括对调用结果的正规化操作(@5.9.2) 和提升项(@6.6) 。
3804+每次调用的调用结果都适用的附加的状态维护操作(@7.10.6) ,包括对调用结果的正规化操作(@5.9.2) 和提升项(@6.6) 。
37513805 这些操作可能有多个实例,因为以下几个原因而无法合并:
37523806 存储模型和对象模型(@5.2.4) 要求生存期开始和结束的顺序;
37533807 对象模型相关的语义规则没有允许合并不同的对象;
@@ -3757,71 +3811,92 @@
37573811 若设置前的环境是被设置的环境的直接或间接父环境,其中可能影响可观察行为的变量都被新的环境隐藏,可确定父环境不再使用且不影响可观察行为,则仍然合并操作,并释放该父环境的资源。
37583812 NPLA 规则 @5.2.6 允许可合并临时对象分配的非幂等操作实现 PTC ,即若存在已在 TCO 动作中分配的相等(以宿主环境(@2.7.1) 约定,参见 @7.11.5)的临时对象,则不再分配。
37593813
3760-@7.10.3.2.2 非幂等操作合并约束:
3814+@7.10.4.2 非幂等操作合并约束:
37613815 同一个 TCO 动作内,以下尾调用合并的效果仍然有效:
37623816 相同类别的不同实例的操作仍然被尾调用合并集中;
37633817 非幂等操作和幂等操作之间的顺序被调整;
37643818 两类非幂等操作之间的顺序被调整,不再交错。
37653819 最后一项变换是被允许的,因为尾上下文内环境的生存期被子表达式内的调用(如 @8.4.5 的实现)约束;而临时对象生存期在尾上下文中可以延长到整个 TCO 操作结束(@5.2.6) 。
37663820
3767-@7.10.3.3 状态维护:
3768-和操作序列(@7.10.3.1) 对应,规约调用动作时影响状态。
3769-在最后一项操作前,状态需要被维护,保证被语义和非 TCO 的实现语义的差异不违反语义规则。
3770-求值退出尾上下文时,需恢复原始的求值环境(正常退出或异常退出),或显式设置被保存的环境(通过修改当前动作返回,可实现续延返回)。
3771-退出尾上下文时恢复原始的环境由尾环境(tail environment) ,即在 TCO 操作前设置的环境提供。
3772-因需支持本机实现的异常退出和显式设置当前动作,使用守卫(guard) 的形式实现。
3773-若退出时尾环境不是保存的环境,表明并非尾上下文求值,则直接设置尾环境为空(为实现 PTC 的 TCO 动作不应有这种情形)。
3774-设置的非空的尾环境前调用操作压缩(@7.10.3.2) 。这可导致先前被保存的环境被释放。
3775-同时,在保持尾环境时,基于环境的引用值的内存安全检查(@5.6.3.1) 失效,因为在抽象机语义中应释放的环境的生存期被延长而改变可观察行为(@4.1.3) ;但检查失败表明已存在未定义行为,所以被允许。
3776-NPLA 的 TCO 实现较一般实现(直接设置及恢复下一环境)更复杂。除为支持有界续延公开当前动作为单一对象外,原因主要来自尾动作支持的操作需要适应 API 的设计和语言特性的要求:
3777-不在规约函数中传递环境参数,避免在不切换环境的操作中频繁传递环境的开销,同时需维护作为一等对象的环境自身的生存期,因此需要在切换环境时进行附加的操作;
3778-存储模型和对象模型支持临时对象(@5.2.4.2) ,需要非幂等的操作(@7.10.3.2) 维护临时对象的生存期;
3779-不要求支持 GC(@5.2.4.4) ,而一等函数的结果默认需要维护内存安全(@5.2.4.3) ,因此需要提升项(@6.6) ;
3780-规约函数支持返回规约结果(@6.5.1) ,需要附加的操作进行维护(为减小开销仅表达式求值状态)。
3781-注意其它一般实现若要保证内存安全,仍然可能需 GC 对环境的维护提供支持(如设置 GC 根对象,避免环境记录(@5.4.4) 被回收)。
3782-当前 TCO 设计的资源管理机制参见 @7.10.4 。
3783-
3784-@7.10.4 TCO 相关的资源管理:
3821+@7.10.5 TCO 相关的资源管理:
37853822 TCO 动作(@7.10.3) 持有尾上下文的活动记录帧(@4.5.3.4) ,包括环境和被规约表达式中的右值对应的临时对象。
3786-操作压缩(@7.10.3.2) 和以下资源管理的操作在 A1::RelayForCall 和 A1::RelayForEval(@7.7.2) 的调用中实现。
3787-另见临时对象管理(@7.10.5) 。
3788-
3789-@7.10.4.1 帧记录(frame record) :
3790-帧记录是保存无法被操作压缩安全释放的活动记录保存的对象和中间状态。
3791-操作压缩结束后的数据结构被保存在帧记录构成的列表中,称为帧记录列表(frame record list) 。
3792-帧记录列表添加的操作在操作压缩以及资源回收后(@7.10.4.2) 进行。
3793-因为互动记录帧的资源以环境引用(@5.4.3) 的形式管理,帧之间的引用影响引用计数。
3794-帧记录中的环境总是具有环境强引用(@5.4.3) 以确保直接可用且避免资源回收(@7.10.4.2) 发生时机的非确定性。
3795-
3796-@7.10.4.2 资源回收:
3797-除非证明移除活动记录帧不影响对象语言的语义,活动记录帧不是操作压缩的可合并的目标。
3798-因为帧记录具有环境强引用,不通过其它操作引起的引用计数改变而释放,回收帧记录引用的资源是显式的操作。这需要同时避免循环引用。
3823+复用 TCO 操作(@7.10.4) 和以下资源管理的操作在 A1::RelayForCall 和 A1::RelayForEval(@7.7.2) 的调用中被蕴含。
3824+另见临时对象管理(@7.10.7) 。
3825+
3826+@7.10.5.1 帧记录(frame record) :
3827+帧记录是保存无法被帧压缩(@7.10.4) 安全释放以完全复用资源的活动记录保存的对象和中间状态。
3828+帧压缩结束后的数据结构被保存在帧记录构成的列表中,称为帧记录列表(frame record list) 。
3829+帧记录列表添加的操作在帧压缩以及资源回收后(@7.10.5.2) 进行。
3830+添加的帧记录项来自被转移的维护当前状态的资源,包括:
3831+尾环境(@7.10.6.1) ;
3832+TCO 动作保存的非本机函数临时对象(@7.10.7.1) 。
3833+因为活动记录帧的资源以环境引用(@5.4.3) 的形式管理,帧之间的引用影响引用计数。
3834+帧记录中的环境总是具有环境强引用(@5.4.3) 以确保直接可用且避免资源回收(@7.10.5.2) 发生时机的非确定性。
3835+在调用 TCO 动作(@7.10.3.3) 时,活动记录帧最终被释放以恢复状态。
3836+
3837+@7.10.5.2 资源回收:
3838+除非证明移除活动记录帧不影响对象语言的语义,活动记录帧不是帧压缩(@7.10.4) 的可合并的目标。
3839+因为帧记录(@7.10.5.1) 具有环境强引用,不通过其它操作引起的引用计数改变而释放,回收帧记录引用的资源是显式的操作。这需要同时避免循环引用。
37993840 在切换环境的操作(如过程调用(@8.4.5.4) )时,无法合并的帧在当前 TCO 动作中分配并储存。
38003841 紧接储存帧的操作之前,对现有的活动记录进行压缩,以确保新的帧后不再形成冗余。PTC 要求存储的帧不无限增长,依赖此处活动记录被成功压缩。
38013842 压缩活动记录会对现有储存的帧中的环境和父环境进行遍历,以环境为单位回收不再被引用的环境对象和对应帧中的其它资源。
38023843 环境对象及其关联的锚对象(@6.9.4) 的引用计数为是否引用提供依据:
3803-环境自身以 shared_ptr 确定强引用计数;
3844+环境自身以 shared_ptr 的实例确定强引用计数;
38043845 锚对象确定其弱引用计数。
38053846 被从 TCO 对象中移除的环境对象是没有 NPL::TermReference 引用其锚对象的对象。当强引用计数为 1 时,压缩后最终会被清除而释放,持有的对应资源被回收。
38063847 这一过程在局部上类似基于引用计数的 GC(@5.2.4.4) 机制。
38073848
3808-@7.10.5 TCO 临时对象(@5.5.6) 管理:
3809-一般机制参见临时对象管理(@7.9.3) 和 TCO 资源管理(@7.10.4) 。
3810-TCO 异步规约对临时对象的管理基本策略同 @7.9.3 ,但因为操作压缩(@7.10.3.2) 具有更多的变化和限制,所以实现较复杂。
3849+@7.10.6 状态维护:
3850+和 TCO 操作序列(@7.10.3.3) 对应,规约调用动作时影响状态。
3851+在最后一项操作前,状态需要被维护,保证被语义和非 TCO 的实现语义的差异不违反语义规则。
3852+求值退出尾上下文时,需恢复原始的求值环境(正常退出或异常退出),或显式设置被保存的环境(通过修改当前动作返回,可实现续延返回)。
3853+NPLA 的 TCO 实现较一般实现(直接设置及恢复下一环境)更复杂。除为支持有界续延公开当前动作为单一对象外,原因主要来自尾动作支持的操作需要适应 API 的设计和语言特性的要求:
3854+不在规约函数中传递环境参数,避免在不切换环境的操作中频繁传递环境的开销,同时需维护作为一等对象的环境自身的生存期,因此需要在切换环境时进行附加的操作;
3855+存储模型和对象模型支持临时对象(@5.2.4.2) ,需要非幂等的操作(@7.10.4) 维护临时对象的生存期;
3856+不要求支持 GC(@5.2.4.4) ,而一等函数的结果默认需要维护内存安全(@5.2.4.3) ,因此需要提升项(@6.6) ;
3857+规约函数支持返回规约结果(@6.5.1) ,需要附加的操作进行维护(为减小开销仅表达式求值状态)。
3858+注意其它一般实现若要保证内存安全,仍然可能需 GC 对环境的维护提供支持(如设置 GC 根对象,避免环境记录(@5.4.4) 被回收)。
3859+另见 TCO 资源管理机制(@7.10.5) 。
3860+
3861+@7.10.6.1 尾环境(tail environment) :
3862+退出尾上下文时恢复原始的环境由尾环境 ,即在 TCO 操作前设置的环境提供。
3863+因需支持本机实现的异常退出和显式设置当前动作,使用单独的守卫( A1::EnvironmentGuard(@7.7.2) )实现。
3864+TCO 构造时的守卫(@7.10.3.2) 不包含尾环境的守卫。
3865+帧记录(@7.10.5.1) 保存环境的守卫(@7.10.6.1) 。
3866+若退出时尾环境不是保存的环境,表明并非尾上下文求值,则直接设置尾环境为空(为实现 PTC 的 TCO 动作不应有这种情形)。
3867+设置的非空的尾环境前复用 TCO 操作(@7.10.4) 。这可导致先前被保存的环境被释放。
3868+同时,在保持尾环境时,基于环境的引用值的内存安全检查(@5.6.3.1) 失效,因为在抽象机语义中应释放的环境的生存期被延长而改变可观察行为(@4.1.3) ;但检查失败表明已存在未定义行为,所以被允许。
3869+
3870+@7.10.6.2 操作结果:
3871+为支持 TCO 操作上的不同调用,调用的结果需被反馈到 TCO 动作中。
3872+操作压缩(@7.10.4) 的和在最后一项操作都包含这样的处理调用结果的请求。
3873+请求包括当前项(@7.10.3.1) 上的以下非幂等操作(@7.10.4.1) :
3874+指定项表示合并子调用的结果而需正规化(@5.9.2) ;
3875+指定项表示需被提升的调用结果(@6.6) 。
3876+处理操作结果返回规约结果(@6.5.1) :
3877+若提升结果,同 NPL::ReduceForLiftedResult(@6.7.1) ;
3878+否则,是上下文中保存的规约结果(@6.9.4) 。
3879+
3880+@7.10.7 TCO 临时对象(@5.5.6) 管理:
3881+一般机制参见临时对象管理(@7.9.3) 和 TCO 资源管理(@7.10.5) 。
3882+TCO 异步规约对临时对象的管理基本策略同一般的临时对象管理(@7.9.3) ,但因为操作压缩(@7.10.4) 具有更多的变化和限制,所以实现较复杂。
38113883 和非 TCO 异步规约的实现相比,临时对象不仅因为引用绑定子项需保证子项的生存期足够长(@7.7.2) 的原因,还可因为操作压缩的需要,而可被转移。后者的情形应符合 @5.7.8 中取得求值结果后不被引用的项修改限制条件。
3812-
3813-@7.10.5.1 非本机函数临时对象:
3814-在 @7.9.3 的基础上,操作压缩保存函数临时对象时,尝试进行去重(deduplication) :比较函数是否相同,以在资源回收(@7.10.4.2) 前即尽可能接近 PTC 的要求。资源回收不处理函数临时对象。
3815-未能被操作压缩去重的函数临时对象应被保存到帧记录列表(@7.10.4.1) 中,以确保满足和 @7.9.3 中相同的要求。
3816-
3817-@7.10.5.2 非本机操作数临时对象:
3818-资源回收支持处理操作数临时对象。资源回收以环境为单位,因此操作数临时对象应绑定到由 TCO 操作对象持有的环境对象上以便回收(@7.10.4.2) 时引用。
3884+构成操作符(@4.4.3.1) 而被调用的函数临时对象在 TCO 操作中保存,并在添加新的帧时,被转移到帧记录(@7.10.5.1) 。
3885+
3886+@7.10.7.1 非本机函数临时对象:
3887+在 @7.9.3 的基础上,在保存函数临时对象前,尝试对其与已知的函数对象(@7.10.3.1) 比较而进行去重(deduplication) ,以在资源回收(@7.10.5.2) 前即尽可能接近 PTC 的要求:
3888+若函数相等,则不添加到已知的函数对象中。
3889+这不同于帧压缩(@7.10.4) 的资源回收(@7.10.5.2) ,后者不处理函数临时对象。
3890+未能被操作压缩去重的函数临时对象应被保存到帧记录列表(@7.10.5.1) 中,以确保满足和 @7.9.3 中相同的要求。
3891+
3892+@7.10.7.2 非本机操作数临时对象:
3893+资源回收支持处理操作数临时对象。资源回收以环境为单位,因此操作数临时对象应绑定到由 TCO 操作对象持有的环境对象上以便回收(@7.10.5.2) 时引用。
38193894 操作数临时对象作为实际参数在环境中绑定后是固定(@5.2.4.1) 的,这要求保存操作数临时对象提前到 A1::RelayForCall(@7.7.2) 的调用之前。
38203895 虽然 @5.2.4.2 和 @7.9.3 未指定临时对象的存储,当前实现在当前环境中使用 TermTags::Temporary 标签(@5.4.2.2) 标记被绑定的临时对象,不需在被求值项或其它位置存储临时对象。
38213896 即当前实现中,临时对象分配由过程抽象(@8.4.5) 引入的合并子的处理器在参数绑定(@8.4.5.3) 时进行。
3822-未能被资源回收释放的环境应被保存到帧记录列表(@7.10.4.1) 中,以确保保存操作数临时对象的环境不会过早地在过程抽象(@8.4.5) 恢复环境时被销毁。
3823-
3824-@7.10.5.3 本机临时对象:
3897+未能被资源回收释放的环境应被保存到帧记录列表(@7.10.5.1) 中,以确保保存操作数临时对象的环境不会过早地在过程抽象(@8.4.5) 恢复环境时被销毁。
3898+
3899+@7.10.7.3 本机临时对象:
38253900 因为没有公开 API ,当前不支持外部的本机函数(@7.9.3) 实现访问 TCO 动作(@7.10.3) 实现以上的类似操作。
38263901 不使用 A1::RelayForCall 和 A1::RelayForEval 调用的本机函数若需支持 TCO ,应使用 TCM(@5.2.6.4.2) 以外的替代 TCO 实现方式。
38273902 本机函数的实现和以上的普遍机制略有不同,和需要被保存的临时对象相关。
@@ -3832,29 +3907,24 @@
38323907 转移临时对象,使之被确保足够涵盖预期生存期的动作(另见 @7.9.3 )或当前合并的某个不作为被规约项(@5.4.2) 的子项所有;
38333908 在表示当前函数合并的项整体作为求值结果前,移除被规约项。
38343909 向 TCO 动作添加临时对象使之被 TCO 动作所有。此时,转移临时对象对应的子项(@7.9.3) 。
3835-尾重写中释放和临时对象相关的资源及对象销毁的顺序要求同 @5.4.3 。这和 @7.10.5.3 中释放环境时遵循 @5.4.3 一致。
3836-
3837-@7.10.6 异步重规约(@7.9.2.1) 的 TCO 互操作:
3910+尾重写中释放和临时对象相关的资源及对象销毁的顺序要求同 @5.4.3 。这和 @7.10.7.3 中释放环境时遵循 @5.4.3 一致。
3911+
3912+@7.10.7.4 临时对象守卫:
3913+临时对象守卫在清理非正常退出(未调用动作时析构)调用 TermNode::Clear 清理当前项的临时对象资源。
3914+这确保当前项作为临时对象时的生存期在求值以异常终止时(@5.2.4.2) 符合所有权隐含的生存期要求(@5.2.5) 。
3915+
3916+@7.10.8 异步重规约(@7.9.2.1) 的 TCO 互操作:
38383917 主规约函数(通过调用跳板(@5.10.1) )保证调用的异步的重规约可以支持 PTC(@5.2.6) 。
38393918 异步重规约不是 TCO 。当前的实现直接操作上下文(@5.4.4) 中的状态(参见 @7.9.2.1 ),不需要依赖 TCO 动作(@7.10.3) 。
3840-当前异步重规约的 TCO 互操作由 A1::ReduceOnce(@7.4.4) 内部遍的调用实现。另见 @7.10.7 。
3841-若使用 TCO 动作中保存状态,则不需要依赖上下文中的状态,但这样的异步重规约和 TCO 不直接兼容。此时,不维护 TCO 动作的异步重规约可导致之后紧随的 TCO 动作无法进行操作压缩(@7.10.3.2) 而无法支持 PTC 。
3842-使特定异步重规约操作感知 TCO 动作,允许和 TCO 操作序列 @7.10.3.1 中的操作兼容且非决定性有序(@4.4.1) 地组合以支持 PTC 。
3919+当前异步重规约的 TCO 互操作由 A1::ReduceOnce(@7.4.4) 内部遍的调用实现。另见动作内联(@7.9.4) 。
3920+若使用 TCO 动作中保存状态,则不需要依赖上下文中的状态,但这样的异步重规约和 TCO 不直接兼容。此时,不维护 TCO 动作的异步重规约可导致之后紧随的 TCO 动作无法进行操作压缩(@7.10.4) 而无法支持 PTC 。
3921+使特定异步重规约操作感知 TCO 动作,允许和 TCO 操作序列 @7.10.3.3 中的操作兼容且非决定性有序(@4.4.1) 地组合以支持 PTC 。
38433922 通过静态地决定使用异步重规约和 TCO 互操作,这种组合实现目标 TCO(@9.1) 。
38443923
3845-@7.10.7 动作内联(inlining) :
3846-特定的异步规约实现可能要求引入的操作总是尾动作(@5.4.4),否则行为未定义。
3847-这允许直接使用同步动作代替异步动作调用,即动作内联。
3848-动作内联可减少操作 ContextNode 中当前动作序列(@6.9.4) 的开销。
3849-注意这项要求具有传递性,即在宿主语言中直接或间接同步调用的操作也应满足要求的条件。
3850-因此,除非能确保不限制接口语义,当前 NPLA1 不使用这样的实现方式。
3851-启用使用这种方式的实现定义为实现选项(@7.1) NPL_Impl_NPLA1_Enable_InlineDirect 。
3852-派生实现应明确指定影响接口语义的动作内联的使用。
3853-
3854-@7.10.8 TCO 动作消除:
3924+@7.10.9 TCO 动作消除:
38553925 部分上下文的实现静态地已知不需要切换当前环境(@5.4.4) 而直接规约。
38563926 这些上下文可省略创建环境并切换当前环境。
3857-因为不需要创建环境,在尾上下文也不需要操作压缩(@7.10.3.2) ;实际不依赖 TCO 动作,可省略 TCO 动作创建(@7.10.3) 。
3927+因为不需要创建环境,在尾上下文也不需要操作压缩(@7.10.4) ;实际不依赖 TCO 动作,可省略 TCO 动作创建(@7.10.3) 。
38583928 这种实现实际不区分尾上下文。之后不能消除 TCO 动作的上下文不能省略检查 TCO 动作是否存在并按需创建。
38593929 若能进一步确保退出上下文时没有切换当前环境为和之前不同的环境(这一般仅在使用本机实现的情形出现),之前不需要保存当前环境。
38603930
@@ -3888,19 +3958,21 @@
38883958
38893959 @7.11.4 PTC 和尾调用本机实现:
38903960 符合 PTC 的尾调用通过 TCO 动作(@7.10.3) 实现。
3891-在规范求值算法(@7.8.2) 的 PTC 通过显式构造 TCO 动作以及在此基础上的操作压缩(@7.10.3.2) 实现。
3892-这对应 klisp 的 ktail_call 实现 Kernel 的默认求值算法(包括 [RnRK] 的 eval 应用子的底层的操作子);但和后者依赖 GC 不同,TCO 动作可对环境有所有权(@7.10.3.3) 。
3961+TCO 动作(@7.10.3) 作为 NPLA 续延和之后在 PLDI 2020 公开的论文 https://doi.org/10.1145/3385412.3385981 中的 extra continuation frame 类似。
3962+TCO 动作可扩展实现类似 SRFI-157 的续延标记(continuation mark) 的特性。
3963+在规范求值算法(@7.8.2) 的 PTC 通过显式构造 TCO 动作以及在此基础上的操作压缩(@7.10.4) 实现。
3964+这对应 klisp 的 ktail_call 实现 Kernel 的默认求值算法(包括 [RnRK] 的 eval 应用子的底层的操作子);但和后者依赖 GC 不同,TCO 动作可对环境有所有权(@7.10.6) 。
38933965 klisp 还使用 ktail_call 提供本机实现。NPLA1 也可使用类似的实现。
38943966 其它更一般的情况下,在求值上下文(@4.4.7) 中隐含已构造 TCO 动作为前提,通过直接或间接调用 A1::ReduceOnce(@7.4.4) 提供 PTC 保证。
38953967 这对应 klisp 的 ktail_eval ;但和后者不同,A1::ReduceOnce 调用确切地只对应不显式引入新的环境的操作。
38963968 可能引入新的环境的操作,如 [RnRK] 的 eval(对应 NPLA1 对象语言函数 eval 和 eval% 等,参见 @11.3.7 ),需要另行调用比 A1::ReduceOnce 更复杂的操作压缩。
3897-和 Kernel 要求以外,因为维护资源(@7.10.4) 的需要,TCO 动作在函数合并的底层操作子合并前被构造,因此进入参数求值时在资源管理的意义上也在所在函数合并的同一个尾上下文(@4.4.7) 。
3969+和 Kernel 要求以外,因为维护资源(@7.10.5) 的需要,TCO 动作在函数合并的底层操作子合并前被构造,因此进入参数求值时在资源管理的意义上也在所在函数合并的同一个尾上下文(@4.4.7) 。
38983970 另见 TCO 实现(@7.11.5) 。
38993971
39003972 @7.11.5 TCO 动作(@7.10.3) 实现和限制:
3901-因为维护资源(@7.10.4) 的需要,规范求值算法(@7.8.2) 不处理 WHNF 以外的操作数且允许宿主类型的调用(@7.6.1.2) 处理 WHNF 的第一项。
3973+因为维护资源(@7.10.5) 的需要,规范求值算法(@7.8.2) 不处理 WHNF 以外的操作数且允许宿主类型的调用(@7.6.1.2) 处理 WHNF 的第一项。
39023974 当前在 TCO 动作中被分配的临时对象(@5.5.6) 的操作都针对作为 WHNF 的第一项的合并子,其中合并子相等性由 @8.4.5.5 或其它宿主实现提供的 == 操作确定。
3903-由 @8.4.5.5 ,捕获不同环境的 vau 合并子不相等;不论是否具有相等的外部表示(@2.3.4) 和来源,其右值不会被直接操作压缩(@7.10.3.2) 避免多次添加而被 TCO ;若其嵌套调用因为捕获不同的存在引用关系的环境,不支持其它形式的 TCO ;因此,不支持 PTC 。
3975+由 @8.4.5.5 ,捕获不同环境的 vau 合并子不相等;不论是否具有相等的外部表示(@2.3.4) 和来源,其右值不会被直接操作压缩(@7.10.4) 避免多次添加而被 TCO ;若其嵌套调用因为捕获不同的存在引用关系的环境,不支持其它形式的 TCO ;因此,不支持 PTC 。
39043976 注意上述情形在其它语言中因为可达性分析等可能具有实现缺陷也不会释放存储,如 Kernel 程序 ($sequence ($define! f ($lambda (n) ((($vau (x y) e (eval ($sequence x y) e)) n (f n))))) (f 1)) 在 klisp 中实测不支持 PTC 。
39053977 当前 TCO 操作压缩不支持回收非 #ignore 的动态环境。这实际上较 [RnRK] 的保证弱。但 klisp 同样没有很好地支持此特性,如 Kernel 程序 ($sequence ($define! $f ($vau () #ignore (($f)))) ($f)) 在 klisp 中实测不支持 PTC 。
39063978 (关于以上 klisp 的问题,参见 https://bitbucket.org/AndresNavarro/klisp/issues/12 。)
@@ -3970,7 +4042,7 @@
39704042 eval-map1-appv :Forms::Map1(@8.4.9) 应用子调用。
39714043 eval-map1-cons :Forms::Map1 列表构造调用。
39724044 eval-sequence-return :非 TCO 的 A1::ReduceOrdered(@7.4.6) 实现中设置规约合并最后的返回结果。
3973-eval-tail :TCO 动作尾上下文求值(和 klisp 不同,不依赖 GC(@5.2.4.4) 而在 TCO 动作单独保存资源(@7.10.4.1) )。
4045+eval-tail :TCO 动作尾上下文求值(和 klisp 不同,不依赖 GC(@5.2.4.4) 而在 TCO 动作单独保存资源(@7.10.5.1) )。
39744046 eval-vau :求值过程抽象(@8.4.5) 在带环境的函数构造器中作为续延。
39754047 注意 klisp 的 $vau 不具有应保证被求值的实际参数,不需要异步调用,没有对应的续延。
39764048 load-external :A1::RelayToLoadExternal(@8.5.2) 加载外部翻译单元。
@@ -4092,6 +4164,8 @@
40924164 Forms::FirstAt
40934165 Forms::FirstRef
40944166 Forms::FirstVal
4167+Forms::Rest
4168+Forms::RestRef
40954169 Forms::RestVal
40964170 Forms::SetFirst
40974171 Forms::SetFirstAt
@@ -4208,7 +4282,7 @@
42084282
42094283 @8.4.9 通用基本派生操作:
42104284 Forms 提供以下可通过对象语言派生的操作的对应本机实现的函数:
4211-Forms::Sequence 序列有序参数规约:移除第一项后顺序规约子项,结果为最后一个子项的规约结果。
4285+Forms::Sequence 序列有序参数规约:移除第一项后顺序规约子项,结果为最后一个子项的规约结果(@6.5.1) 。
42124286 此操作对列表形式的操作数的子项有序求值,其顺序约定同 A1::ReduceOrdered 。
42134287 当操作数非空时,保证最后一项的规约是尾上下文(@4.4.7) ,允许 PTC(@5.2.6) 。
42144288 Forms::Apply 函数应用:应用参数指定的函数和作为函数参数的列表。
@@ -4603,7 +4677,7 @@
46034677 满足实现行为(@4.1.3) 要求的前提下,实现可能实际支持更多的 TCO 和 PTC 尾上下文。
46044678
46054679 @9.7.4.1 支持 PTC 的尾调用尾上下文:
4606-当允许忽略非平凡析构(@7.10.3.2) 时,可以在尾上下文求值中进行 TCO 并支持 PTC 。
4680+当允许忽略非平凡析构(@7.10.4) 时,可以在尾上下文求值中进行 TCO 并支持 PTC 。
46074681 注意尾上下文中被求值的子表达式中的对象生存期可被调整(@5.2.6) ,是否调整和具体调整的方式未指定,不同的实现可能不同。
46084682 提供 PTC 保证的尾上下文和 Kernel 类似,当前仅包括 PTC 不改变可观察行为(@4.1.3) 时的:
46094683 函数调用(@4.5.3.1) 中,(使用 $vau 和 $lambda 等合并子抽象引入的)合并子(@9.9.5) 的调用(注意不包括其它途径引入的外部函数的调用);
@@ -5359,9 +5433,9 @@
53595433 first(@11.4.1)
53605434 first&(@11.4.1)
53615435 first@(@11.4.1)
5362-restv(@11.4.3)
5363-rest&(@11.4.3)
5364-rest%(@11.4.3)
5436+rest%(@11.4.1)
5437+rest&(@11.4.1)
5438+restv(@11.4.1)
53655439 因为正规表示(@5.9.4) 的限制,restv 、rest& 和 rest% 重新构造列表,并不直接返回子对象(@9.8.2) ;其它访问器若带有引用标记字符,可直接返回引用值。
53665440 此外,@11.2.2.4 中部分函数也符合容器元素访问器的要求,但当前不提供带有后缀标记字符的变体。这些函数包括:
53675441 unwrap(@11.3.8)
@@ -5489,7 +5563,7 @@
54895563 若右值之间 eqv? 比较结果为 #t ,eq? 比较结果未指定,以允许实现复用存储右值的驻留(@9.8.4) 对象。
54905564 因为对应等价的不变性(@2.3.1.1) 关系不具有唯一性(@4.1.4.3) ,且可能允许不唯一的方式产生副作用(如缓存),和 Kernel 不同,不以基本操作提供 equal? 对任意对象提供一般的相等操作。
54915565 不等价(@4.5.3.2) 的函数的 eqv? 比较结果为 #f 。
5492-除以上规则确定的结果外,eqv? 对函数或列表的比较结果未指定。在互操作(@2.3.3) 的意义上,当前其 eqv? 定义的函数的相等性由 @8.4.5.5 或其它宿主实现提供的 == 操作确定(关于合并子相等,另见 TCO 实现(@7.11.5) 中关于操作压缩判断的说明)。
5566+除以上规则确定的结果外,eqv? 对函数或列表的比较结果未指定。在互操作(@2.3.3) 的意义上,当前其 eqv? 定义的函数的相等性由 @8.4.5.5 或其它宿主实现提供的 == 操作确定(关于合并子相等,另见 TCO 实现(@7.11.5) 中关于函数右值去重(@7.10.7.1) 的说明)。
54935567 通常,等价谓词比较的求值应保证能终止且对非列表项和 n 个子项的列表分别具有 O(1) 和 O(n) 平摊复杂度。但语言不需要约束这个性质。
54945568
54955569 @11.3.3 控制:
@@ -5670,7 +5744,7 @@
56705744 不直接使用本机实现的替代实现仅供参考,可能引入和本机实现不同的未指定行为,且可能有和核心特性实现相关的限制:
56715745 由合并子调用的参数绑定(@8.4.5.3) 引起(@9.5.1) 的静态语法错误(@9.5.1) 可能具有不同的诊断(@9.5) ;
56725746 类型检查(@9.5.4.1) 可具有不同的顺序,多次失败的类型检查引起的类型错误可能具有不同的诊断;
5673-使用 TCO 实现 PTC(@9.7.4) ,可能具有不同的支持(@9.7.4.1) 和实现(如 TCO 动作消除(@7.10.8) );
5747+使用 TCO 实现 PTC(@9.7.4) ,可能具有不同的支持(@9.7.4.1) 和实现(如 TCO 动作消除(@7.10.9) );
56745748 @10.9 约定的关于一般操作实现的一些未指定行为,特别地,包括续延名称(@7.11.6) 的差异。
56755749 注意和 [RnRK] 合并子的派生完全省略错误处理而允许不同的诊断不同,NPLA1 中的操作的替代实现的没有被以上例外或操作自身的语义指定的其它行为(如诊断(@9.5) )仍应和本机实现保持一致。
56765750
@@ -5747,8 +5821,13 @@
57475821 类似传统 Lisp 及 Kernel 的 car 。命名和 SRFI-1 及 Clojure 等现代变体一致。
57485822 当 <list> 是左值时结果是折叠的引用值,否则结果是返回值转换(@5.7.6.4) 后的值。
57495823 first@ <list> :同 first ,但结果总是未折叠的引用值。
5750-first& <list> :同 first ,但结果总是折叠的引用值,且首先调用 check-list-reference(@11.3.9) 检查参数是列表引用,对右值的抛出异常。
5824+first& <list> :同 first ,但结果总是折叠的引用值。
5825+首先调用 check-list-reference(@11.3.9) 检查参数是列表引用,对右值的抛出异常。
57515826 firstv <list> :同 first ,但结果总是返回值转换后的值。
5827+rest% <list> :取列表第一个元素以外的元素值经过转发的值构成的列表。
5828+rest& <list> :取列表第一个元素以外的元素值的引用值构成的列表。
5829+首先调用 check-list-reference 检查参数是列表引用,对右值的抛出异常。
5830+restv <list> :取列表第一个元素以外的元素值构成的列表。
57525831 equal? <object> <object> :一般相等关系。类似 eqv?(@11.3.2) ,但同时支持表示中具有子项作为子对象(@9.8.2) 的对象。
57535832 相等定义为:
57545833 对叶节点,同 eqv? ;
@@ -5821,9 +5900,6 @@
58215900 $lambda/e% <parent> <formals> <body> :同 $lambda/e ,但保留引用值。
58225901 $defl/e! <variable> <parent> <formals> <body> :绑定指定静态环境的 λ 抽象,等价 $def! <variable> $lambda/e <parent> <formals> <body> 。
58235902 $defl/e%! <variable> <parent> <formals> <body> :绑定指定静态环境的 λ 抽象,等价 $def! <variable> $lambda/e% <parent> <formals> <body> 。
5824-restv <list> :取列表第一个元素以外的元素值构成的列表。
5825-rest& <list> :同 rest ,但在返回的列表值中的元素是引用值,且首先调用 check-list-reference 检查参数是列表引用,对右值的抛出异常。
5826-rest% <list> :同 rest ,但在返回的列表值中的元素可能是传递的引用值。
58275903 box <object> 装箱:构造箱(类型为 <box> 的对象)。
58285904 box% <object> 同 box ,但参数不隐含左值到右值转换,在结果保留引用值。
58295905 box? <object> 判断是否为 <box> 类型的对象。
@@ -5834,7 +5910,10 @@
58345910 和 http://community.schemewiki.org/?scheme-faq-language 关于装箱的描述不同,这样的代替不一定保证有更好的性能。
58355911 以上这些函数可使用 make-encapsulation-type(@11.3.10) 实现。
58365912 和 Scheme 等不同,箱具有被装箱对象的所有权,因此使用 box% 和 unbox 时,需注意保存被构造的箱或被箱中引用值引用的对象。
5837-list-rest% <list> :复合 list% 和 rest% 操作。可用于解析 <bindings> ,去除其中每个元素的符号项构成新的列表。
5913+list-rest% <list> :复合 list% 和 rest% 操作。
5914+结果为以下调用的结果:
5915+参数被转发作为 rest% 的实际参数,结果作为 list% 的实际参数。
5916+可用于解析 <bindings> ,去除其中每个元素的符号项构成新的列表。
58385917 list-concat <list1> <list2> :取顺序连接的列表。
58395918 append <list>... :顺序拼接列表。
58405919 assv <object> <lists> :访问关联元素。
@@ -5849,9 +5928,6 @@
58495928 创建参数指定的环境和基础环境作为父环境的空环境。
58505929 父环境顺序同参数顺序。基础环境是最后一个父环境。
58515930 类似 make-standard-environment(@11.4.1) ,但具有参数指定的环境作为其它的父环境。
5852-$as-environment <body> :求值表达式构造环境。
5853-创建以动态环境为父环境的空环境,并在其中求值参数指定的表达式。
5854-结果是创建的环境强引用。
58555931 $let <bindings> <body> :局部绑定求值:创建以当前环境为父环境的空环境,在其中添加 <bindings> 指定的变量绑定,再求值 <body> 。
58565932 类似 Kernel 的同名操作,但返回非引用值(@10.4.2) 。
58575933 创建的环境同合并子的局部环境(@8.4.5) 。
@@ -5874,6 +5950,9 @@
58745950 $bindings/p->environment (<environment>...) <binding>... :转换绑定列表为以指定的环境列表中的环境为父环境的具有这些绑定的环境。
58755951 类似 Kernel 的 $binding->environment ,但指定父环境,且具有适当的所有权。
58765952 使用 make-environment 而不是 $let/e 等绑定构造实现。
5953+$as-environment <body> :求值表达式构造环境。
5954+创建以动态环境为父环境的空环境,并在其中求值参数指定的表达式。
5955+结果是创建的环境强引用。
58775956 $bindings->environment <binding>... :转换绑定列表为没有父环境的具有这些绑定的环境。
58785957 类似 Kernel 的同名操作,但因为要求对内部父环境环境所有权,使用 $binding/p->environment 而不是 $let/e 等绑定构造实现。
58795958 symbols->imports! <symbol>... :转换符号列表为未求值的适合初始化符号导入列表的初值符列表。
diff -r 2d5fea7ba0cd -r c91b75c1e8bc doc/Workflow.txt
--- a/doc/Workflow.txt Sat Jan 30 14:55:15 2021 +0800
+++ b/doc/Workflow.txt Sat Feb 06 16:04:05 2021 +0800
@@ -11,13 +11,13 @@
1111 /*! \file Workflow.txt
1212 \ingroup Documentation
1313 \brief 工作流汇总报告。
14-\version r4157
14+\version r4224
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 433
1717 \par 创建时间:
1818 2013-07-31 01:27:41 +0800
1919 \par 修改时间:
20- 2021-01-30 14:36 +0800
20+ 2021-02-04 08:20 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -4123,5 +4123,74 @@
41234123 'A1::ReduceCombined' and its friends are simplified.
41244124 The term setup is now in the internal combiner thunk instead of the entries of each reduction functions.
41254125
4126+$2021-02:
4127+
4128+report.impl:
4129+NPLA1 implementation is continuously imporved.
4130+ Temporary destroying on exception exit is implemented.
4131+ This is more consistent to C++.
4132+ The implementation clears the currently evaluated term in the end of the combiner reduction.
4133+ This assumes temporary objects are created only in the branch actions.
4134+ This is implemented by several different ways.
4135+ Basically, in the combiner call, it uses a guard to clear the term of the prvalues potentionally holding the temporary objects.
4136+ The guard is required to be usable in the combiner call thunk, so calls to initiate combiner calls in the object language (e.g. 'A1::ReduceCombiner') can assume the removal of temporary objects in abnormal exits.
4137+ In the TCO implementation, the TCO action owns the guard.
4138+ Now the term reference in the TCO action is also inline into the guard.
4139+ The TCO action is also allowed to be extended before the call.
4140+ No TCO action is supported to be out of an existing call which provides the resources to be reused.
4141+ And here, it works even when the TCO action is created elsewhere, before entering the combiner call thunk.
4142+ In other implementations, the guard is put together with the term regularization in the combiner call thunk.
4143+ The guard is dismissed on normal exits.
4144+ The synchronous implementation uses C++ exception handling directly.
4145+ For asynchronous implementations, the guard is actually put into a continuation, either the TCO action or the continuation named 'eval-combine-return'.
4146+ Operations of cleanup are ordered since the operations in the guards are ordered.
4147+ However, it is unspecified that destroying of the temporary objects would be sequenced before continuation traverse or not.
4148+ It is unspecified to allow synchronous and asynchronous implementations having different behavior.
4149+ For example, the effects of temporary object destruction can have different order relative to the output in the call of 'A1::TraceBacktrace'.
4150+ They are even allowed interleaving, although as an implementation detail, the current 'A1::TraceBacktrace' does not work like this.
4151+ The single cleanup operations still do not interleave with other operations.
4152+ For consistency and intuition, 'A1::RelayForEval' and 'A1::RelayForCall' now requires TCO action having the term same to the argument of the call in the TCO implementation.
4153+ A bug of reduction status handling of native implementation of applicatives 'accr', 'foldr1' and 'map1' (i.e. functions 'Forms::AccR', 'Forms::FoldR1' and 'Forms::Map1') has been fixed.
4154+ In the TCO implementation, it is significant that the last status for the non-tail call should not be misused for the later caller sites in the tail context.
4155+ Otherwise, it might cause wrong result due to the term regularization performed on wrong term.
4156+ This bug is not shown previously due to a bug of result handling in the TCO implementation, see below.
4157+ Non-tail calls in the TCO implementation of functions 'Forms::AccR' and 'Forms::FoldR1' are optimized.
4158+ The creation of TCO action is deterministic for the sum calls, so no check needs to be performed (by 'A1::SetupTailContext').
4159+ In 'Forms::Map1', no such optimization is needed because 'cons' operation is already native.
4160+ A bug in the derivation of the applicative 'list-concat' has been fixed.
4161+ The bug would cause invalid reference to prvalue transferred to the 'foldr1' call implementing the function.
4162+ The reference to prvalue would have the reference of the environment created in the body of 'list-concat' implementation.
4163+ The reference would be invalid after the frame compression in the TCO implementation or the call of 'list-concat' had been returned.
4164+ This bug was hidden by a TCO implementation detail, see below.
4165+ Implementation of the TCO action is revised.
4166+ Some implementations are refactored and now it is a slightly more efficient.
4167+ The order of the result handling relative to frame compression in the operation compression (TCO action prepration) is fixed.
4168+ The bug would case some reference retained unexpectedly.
4169+ Some bugs in the result handling has been fixed.
4170+ When the combining request is missing, the term regularization is just ignored, which is wrong.
4171+ This would make impossible to preserve references in the middle of the tail call sequence if it is finally lifted.
4172+ Previously, the bug of those native implementation is hidden by the missing of the request.
4173+ As an optimization, the combining request now further removed after the above fix and the fix in 'Forms::AccR', 'Forms::FoldR1' and 'Forms::Map1'.
4174+ When the lifting request is handled, it is not consumed.
4175+ This would hide the bug of wrong reference handling when the enclosing call has required the lifting of the call result.
4176+ For example, the applcative 'list-concat' (defined by '$defl!') implementation was buggy but the bug was not shown, for subtle implementation-specific reasons.
4177+ Note the impelementation of 'list-concat' currently uses '$defl!' for consistency of the sytle rule.
4178+ Since the result is already a list in the body, defining by '$def%l!' is also correct despite the style rule.
4179+ The bug would not be hidden if 'foldr1' used in the implementation is derivative, or '$defl%!' (without lifting) is used instead of '$defl!'.
4180+ In the case '$defl!' is used, the lift request for 'list-concat' is performed by '$lambda' implied in '$defl!'.
4181+ The 'foldr1' implementation would then recursively calls itself on necessity, taking the buggy reference in the 'list-concat' derivation (as the argument of 'knil' in derivation of 'foldr1') all the way.
4182+ Note that all the recursive 'foldr1' calls are not the tail calls.
4183+ Only the last sum call ('kons' call in the 'foldr1' derivation) in the innermost 'foldr1' call is a tail call.
4184+ In a native 'foldr1' implementation, the sum call would wrongly lift the result agian, and the offending reference is then lifted.
4185+ For a derivative 'foldr1', there is no such lift (and 'forward! knil' before the sum call is not a tail call), so the bug is not hidden.
4186+ The lifting request is now recorded as a count in the TCO action.
4187+ 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.
4188+ Like adding the combiner wrapping count ('A1::FormContextHandler::Wrapping'), if the count overflow, an exception is thrown.
4189+ A bug in the native implementation of applicatives with applicative arguments (i.e. callbacks) has been fixed.
4190+ The bug missed the setup of the next term in certain contexts, effective only when configured '!NPL_Impl_NPLA1_Enable_InlineDirect' and 'NPL_Impl_NPLA1_Enable_Thunk', since build 898.
4191+ The bug would cause an assertion failure in 'A1::ReduceCombinedBranch' debug configurations due wrong term used.
4192+ Currently, this would affect functions 'AccL', 'AccR', 'FoldR1', 'Map1' and 'ForwardListFirst' in namespace 'A1::Forms'.
4193+ The fix is as the change in functions 'A1::RelayForEval' and 'A1::RelayForCall' since build 909.
4194+
41264195 ////
41274196
Show on old repository browser