• R/O
  • SSH

YSLib: Commit

The YSLib project - main repository


Commit MetaInfo

Revisione1756f2eba1f20856ce3f7b484ad7662a0d37d38 (tree)
Time2022-07-12 19:45:26
AuthorFrankHB <frankhb1989@gmai...>
CommiterFrankHB

Log Message

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

Change Summary

Incremental Difference

diff -r eae6ca1f576c -r e1756f2eba1f Tools/SHBuild/Main.cpp
--- a/Tools/SHBuild/Main.cpp Thu Jun 30 07:43:19 2022 +0800
+++ b/Tools/SHBuild/Main.cpp Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file Main.cpp
1212 \ingroup MaintenanceTools
1313 \brief 宿主构建工具:递归查找源文件并编译和静态链接。
14-\version r4507
14+\version r4509
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 473
1717 \par 创建时间:
1818 2014-02-06 14:33:55 +0800
1919 \par 修改时间:
20- 2022-02-14 07:29 +0800
20+ 2022-07-11 18:05 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -481,7 +481,7 @@
481481 // intended at least in the stage 1.
482482 context.Root.Trace.FilterLevel = Logger::Level::Informative;
483483 LoadStandardContext(context);
484- context.OutputStreamPtr = NPL::make_observer(&std::cout);
484+ context.OutputStreamPtr = make_observer(&std::cout);
485485
486486 auto& rctx(context.Root);
487487
@@ -971,6 +971,7 @@
971971 PrintInfo("Deleted file " + Quote(target) + '.', Debug);
972972 }
973973 CallWithException(str, 1);
974+ print("Built file: " + Quote(target) + '.', Informative);
974975 }
975976 }
976977 else
diff -r eae6ca1f576c -r e1756f2eba1f YBase/include/ystdex/memory_resource.h
--- a/YBase/include/ystdex/memory_resource.h Thu Jun 30 07:43:19 2022 +0800
+++ b/YBase/include/ystdex/memory_resource.h Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file memory_resource.h
1212 \ingroup YStandardEx
1313 \brief 存储资源。
14-\version r1564
14+\version r1570
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 842
1717 \par 创建时间:
1818 2018-10-27 19:30:12 +0800
1919 \par 修改时间:
20- 2022-03-16 21:58 +0800
20+ 2022-07-10 17:57 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -57,7 +57,7 @@
5757 [wide.stream.objects] 通过 [basic.ios.cons] 和 [ios.init]
5858 允许程序创建 std::basic_ios::Init 对象而隐含指定相对其它静态对象销毁的顺序要求。
5959 其它静态对象包括 pmr 资源没有相应的保证。
60-和 libstdc++ 、libc++ 和 Microsoft VC++ 的实现(另见 WG21 P01247 )不同,在
60+和 libstdc++ 、libc++ 和 Microsoft VC++ 的实现(另见 WG21 P1247 )不同,在
6161 ISO C++17 中指定为静态对象的资源仍保证析构,且不保证常量初始化,
6262 而无法保证和其它静态对象之间的析构顺序。
6363 使用默认资源初始化的静态对象可能因销毁时资源对象的生存期已终止而引起未定义行为;
@@ -232,10 +232,9 @@
232232 return &a == &b || a.is_equal(b);
233233 }
234234
235- /*!
236- \pre 断言:对齐值是 2 的整数次幂。
237- \post 断言:返回值符合参数指定的对齐值要求。
238- */
235+ //! \pre 断言:对齐值是 2 的整数次幂。
236+ //@{
237+ //! \post 断言:返回值符合参数指定的对齐值要求。
239238 YB_ALLOCATOR YB_ATTR(alloc_align(3), alloc_size(2)) YB_ATTR_returns_nonnull
240239 void*
241240 allocate(size_t bytes, size_t alignment = max_align)
@@ -260,6 +259,7 @@
260259 yconstraint(is_power_of_2_positive(alignment));
261260 return do_deallocate(p, bytes, alignment);
262261 }
262+ //@}
263263
264264 YB_ATTR_nodiscard bool
265265 is_equal(const memory_resource& other) const ynothrow
diff -r eae6ca1f576c -r e1756f2eba1f YFramework/include/NPL/Exception.h
--- a/YFramework/include/NPL/Exception.h Thu Jun 30 07:43:19 2022 +0800
+++ b/YFramework/include/NPL/Exception.h Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file Exception.h
1212 \ingroup NPL
1313 \brief NPL 异常。
14-\version r11489
14+\version r11508
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 663
1717 \par 创建时间:
1818 2022-01-21 01:59:22 +0800
1919 \par 修改时间:
20- 2022-06-26 04:06 +0800
20+ 2022-07-05 03:54 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -289,58 +289,57 @@
289289
290290
291291 /*!
292-\note 后两个参数传递给 TermToStringWithReferenceMark ,预期用法通常相同。
292+\note 非字符串的后三个参数传递给 TermToStringWithReferenceMark ,预期用法通常相同。
293293 \sa TermToStringWithReferenceMark
294+\since build 949
294295 */
295296 //@{
296297 /*!
297298 \brief 抛出缺少项的异常。
298299 \throw ParameterMismatch 缺少项的错误。
299-\since build 904
300300 */
301301 YB_NORETURN YF_API void
302-ThrowInsufficientTermsError(const TermNode&, bool);
302+ThrowInsufficientTermsError(const TermNode&, bool, size_t = 0);
303303
304304 /*!
305305 \brief 抛出对非有序对值预期列表类型的异常。
306306 \throw ListTypeError 值不是有序对。
307-\since build 948
308307 */
309308 YB_NORETURN YF_API void
310-ThrowListTypeErrorForAtom(const TermNode&, bool);
309+ThrowListTypeErrorForAtom(const TermNode&, bool, size_t = 0);
311310
312311 /*!
313312 \brief 对列表项抛出指定预期访问值的类型的异常。
314313 \throw ListTypeError 消息中包含由参数指定的预期访问值的类型的异常。
315314 */
316315 //@{
317-//! \since build 928
318316 YB_NORETURN YF_API YB_NONNULL(1) void
319-ThrowListTypeErrorForInvalidType(const char*, const TermNode&, bool);
320-//! \since build 855
317+ThrowListTypeErrorForInvalidType(const char*, const TermNode&, bool,
318+ size_t = 0);
321319 YB_NORETURN YF_API void
322-ThrowListTypeErrorForInvalidType(const type_info&, const TermNode&, bool);
320+ThrowListTypeErrorForInvalidType(const type_info&, const TermNode&, bool,
321+ size_t = 0);
323322 //@}
324323
325324 /*!
326325 \brief 抛出对非列表值预期列表类型的异常。
327326 \throw ListTypeError 值不是列表。
328-\since build 948
329327 */
330328 YB_NORETURN YF_API void
331-ThrowListTypeErrorForNonList(const TermNode&, bool);
329+ThrowListTypeErrorForNonList(const TermNode&, bool, size_t = 0);
332330
333331 /*!
334332 \brief 对项抛出指定预期访问值的类型的异常。
335333 \throw TypeError 消息中包含由参数指定的预期访问值的类型的异常。
336334 */
337335 //@{
338-//! \since build 928
336+YB_NORETURN YF_API YB_NONNULL(1,2) void
337+ThrowTypeErrorForInvalidType(const char*, const char*);
339338 YB_NORETURN YF_API YB_NONNULL(1) void
340-ThrowTypeErrorForInvalidType(const char*, const TermNode&, bool);
341-//! \since build 917
339+ThrowTypeErrorForInvalidType(const char*, const TermNode&, bool, size_t = 0);
342340 YB_NORETURN YF_API void
343-ThrowTypeErrorForInvalidType(const type_info&, const TermNode&, bool);
341+ThrowTypeErrorForInvalidType(const type_info&, const TermNode&, bool,
342+ size_t = 0);
344343 //@}
345344 //@}
346345
diff -r eae6ca1f576c -r e1756f2eba1f YFramework/include/NPL/NPLA.h
--- a/YFramework/include/NPL/NPLA.h Thu Jun 30 07:43:19 2022 +0800
+++ b/YFramework/include/NPL/NPLA.h Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA.h
1212 \ingroup NPL
1313 \brief NPLA 公共接口。
14-\version r9592
14+\version r9695
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 663
1717 \par 创建时间:
1818 2016-01-07 10:32:34 +0800
1919 \par 修改时间:
20- 2022-06-20 23:02 +0800
20+ 2022-07-06 08:28 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -43,12 +43,12 @@
4343 // TNIter, AccessFirstSubterm, AssertBranch, NPL::Deref,
4444 // YSLib::EmplaceCallResult, ystdex::less, YSLib::map, pmr,
4545 // ystdex::copy_and_swap, NoContainer, ystdex::try_emplace,
46-// ystdex::try_emplace_hint, ystdex::insert_or_assign, type_info,
46+// ystdex::try_emplace_hint, ystdex::insert_or_assign, type_info,
4747 // ystdex::expanded_function, ystdex::enable_if_same_param_t,
4848 // ystdex::exclude_self_t, ystdex::make_obj_using_allocator,
4949 // YSLib::forward_list, ystdex::swap_dependent, make_observer,
5050 // YSLib::allocate_shared, YSLib::Logger, trivial_swap, ystdex::exchange,
51-// NPL::AsTermNode, ystdex::is_bitwise_swappable;
51+// NPL::AssertMatchedAllocators, NPL::AsTermNode, ystdex::is_bitwise_swappable;
5252 #include <ystdex/base.h> // for ystdex::derived_entity;
5353 #include <libdefect/exception.h> // for std::exception_ptr;
5454
@@ -593,27 +593,27 @@
593593
594594 /*!
595595 \return 转换得到的字符串。
596+\pre 断言:跳过的子项数不超过构成有序对的前缀项数。
597+\note 最后一个参数表示跳过的子项数。
598+\sa CountPrefix
596599 \sa TermToString
600+\since build 949
597601
598602 访问项的值作为名称转换为字符串,若失败则提取值的类型和子项数作为构成值的表示。
603+项的值的表示首先忽略其中的若干个子项前缀,其数量由最后一个参数指定。
599604 除名称外的外部表示方法未指定;结果可能随实现变化。
600605 */
601606 //@{
602-/*!
603-\brief 访问项的值并转换为字符串形式的外部表示。
604-\since build 801
605-*/
607+//! \brief 访问项的值并转换为字符串形式的外部表示。
606608 YB_ATTR_nodiscard YF_API YB_PURE string
607-TermToString(const TermNode&);
609+TermToString(const TermNode&, size_t = 0);
608610
609611 /*!
610612 \brief 访问项的值并转换为可选带有引用标记的字符串形式。
611613 \note 当前使用前缀 [*] 和空格表示引用项。直接附加字符串,因此通常表示已解析的引用。
612-\sa TermToString
613-\since build 840
614614 */
615615 YB_ATTR_nodiscard YF_API YB_PURE string
616-TermToStringWithReferenceMark(const TermNode&, bool);
616+TermToStringWithReferenceMark(const TermNode&, bool, size_t = 0);
617617 //@}
618618
619619 /*!
@@ -1224,8 +1224,9 @@
12241224 \sa TermReference
12251225 \sa TryAccessLeafAtom
12261226
1227-接受指定解析实现的函数和被解析的项作为参数,尝试访问其中是否具有引用值。
1228-若确定是引用值,则被引用的项是被引用值引用的项;否则,被引用的项是第二参数指定的项。
1227+接受指定解析实现的函数和被解析的项作为参数。
1228+若确定可通过引用值访问,
1229+ 则被引用的项是被引用值引用的项;否则,被引用的项是第二参数指定的项。
12291230 确定被引用的项后,调用解析函数继续处理。
12301231 参数指定解析函数和被解析的项。
12311232 解析函数应具有第一参数取被解析的项的引用,还可以具有第二可选参数。后者是
@@ -1236,9 +1237,14 @@
12361237 和 observer_ptr<const TermReference> 不同,因为这个类型允许隐式转换为 bool ,
12371238 所以解析函数的实现中可直接以 bool 作为第二形式参数。
12381239 */
1240+//@{
1241+/*!
1242+\note 第三参数指定引用项的指针,当且仅当非空表示可通过引用值访问。
1243+\since build 949
1244+*/
12391245 template<typename _func, class _tTerm>
1240-auto
1241-ResolveTerm(_func do_resolve, _tTerm&& term)
1246+inline auto
1247+ResolveBy(_func do_resolve, _tTerm&& term, observer_ptr<const TermReference> p)
12421248 -> yimpl(decltype(ystdex::expand_proxy<yimpl(void)(_tTerm&&,
12431249 ResolvedTermReferencePtr)>::call(do_resolve, yforward(term),
12441250 ResolvedTermReferencePtr())))
@@ -1246,16 +1252,44 @@
12461252 using handler_t = yimpl(void)(_tTerm&&, ResolvedTermReferencePtr);
12471253
12481254 // XXX: Assume value representation of %term is not trivially regular.
1249- if(const auto p = TryAccessLeafAtom<const TermReference>(term))
1250- {
1255+ if(p)
12511256 return ystdex::expand_proxy<handler_t>::call(do_resolve, p->get(),
12521257 NPL::ResolveToTermReferencePtr(p));
1253- }
12541258 return ystdex::expand_proxy<handler_t>::call(do_resolve,
12551259 yforward(term), ResolvedTermReferencePtr());
12561260 }
12571261
12581262 /*!
1263+\note 引用值在视为非有序对的节点或有序对后缀的被解析的项中确定。
1264+\sa TryAccessLeaf
1265+\since build 949
1266+*/
1267+template<typename _func, class _tTerm>
1268+inline auto
1269+ResolveSuffix(_func do_resolve, _tTerm&& term)
1270+ -> yimpl(decltype(NPL::ResolveBy(std::move(do_resolve), yforward(term),
1271+ TryAccessLeaf<const TermReference>(term))))
1272+{
1273+ return NPL::ResolveBy(std::move(do_resolve), yforward(term),
1274+ TryAccessLeaf<const TermReference>(term));
1275+}
1276+
1277+/*
1278+\note 引用值在被解析的项中确定。
1279+\sa TryAccessLeafAtom
1280+*/
1281+template<typename _func, class _tTerm>
1282+inline auto
1283+ResolveTerm(_func do_resolve, _tTerm&& term)
1284+ -> yimpl(decltype(NPL::ResolveBy(std::move(do_resolve), yforward(term),
1285+ TryAccessLeafAtom<const TermReference>(term))))
1286+{
1287+ return NPL::ResolveBy(std::move(do_resolve), yforward(term),
1288+ TryAccessLeafAtom<const TermReference>(term));
1289+}
1290+//@}
1291+
1292+/*!
12591293 \brief 访问一次解析引用值后的项的指定类型正规值。
12601294 \exception ListTypeError 异常中立:项为列表项。
12611295 \exception bad_any_cast 异常中立:非列表项类型检查失败。
@@ -1630,6 +1664,11 @@
16301664 //@}
16311665
16321666 /*!
1667+\note 真列表的最后一个元素为值数据成员的默认值构成的空列表。
1668+\note 不修改参数指定的项的标签。
1669+*/
1670+//@{
1671+/*!
16331672 \brief 提升(可能非真)列表的每个元素项的值数据成员可能包含的引用值。
16341673 \note 不修改参数指定的项的标签。
16351674 \note 蕴含 LiftPrefixToReturn 。
@@ -1637,10 +1676,30 @@
16371676 \sa LiftToReturn
16381677 \since build 948
16391678 */
1640-YF_API void
1679+inline void
16411680 LiftElementsToReturn(TermNode&);
16421681
16431682 /*!
1683+\brief 提升(可能非真)列表的最后一个前的元素项的值数据成员可能包含的引用值。
1684+\note 不修改参数指定的项的标签。
1685+\since build 948
1686+*/
1687+inline TNIter
1688+LiftPrefixToReturn(TermNode&);
1689+/*!
1690+\brief 提升(可能非真)列表指定子项起最后一个前的元素项的值数据成员可能包含的引用值。
1691+\pre 第二参数是第一参数子项的迭代器。
1692+\pre 断言:第二参数是第一参数的前缀范围中或是其结尾迭代器。
1693+\note 第二参数指定起始子项。
1694+\since build 949
1695+*/
1696+YF_API TNIter
1697+LiftPrefixToReturn(TermNode&, TNCIter);
1698+
1699+inline PDefH(TNIter, LiftPrefixToReturn, TermNode& term)
1700+ ImplRet(LiftPrefixToReturn(term, term.begin()))
1701+
1702+/*!
16441703 \brief 提升每个子项项的值数据成员可能包含的引用值。
16451704 \note 对表示真列表的项,作用同 LiftElementsToReturn 。
16461705 \sa LiftToReturn
@@ -1660,13 +1719,17 @@
16601719 //@}
16611720
16621721 /*!
1663-\brief 提升(可能非真)列表的最后一个前的每个元素项的值数据成员可能包含的引用值。
1664-\note 真列表的最后一个元素为值数据成员的默认值构成的空列表。
1665-\note 不修改参数指定的项的标签。
1666-\since build 948
1722+\brief 提升(可能非真)列表的指定子项开始的每个元素项的值数据成员可能包含的引用值。
1723+\pre 第二参数是第一参数子项的迭代器。
1724+\pre 断言:第二参数是第一参数的前缀的结尾迭代器。
1725+\since build 949
16671726 */
1668-YF_API TNIter
1669-LiftPrefixToReturn(TermNode&);
1727+YF_API void
1728+LiftSuffixToReturn(TermNode&, TNCIter);
1729+
1730+inline PDefH(void, LiftElementsToReturn, TermNode& term)
1731+ ImplExpr(LiftSuffixToReturn(term, LiftPrefixToReturn(term)))
1732+//@}
16701733
16711734 //! \pre 断言:参数指定的项是枝节点。
16721735 //@{
@@ -2004,7 +2067,6 @@
20042067 class YF_API Environment : private ystdex::equality_comparable<Environment>
20052068 {
20062069 public:
2007- // TODO: Wait for %unordered_set to support transparent keys.
20082070 //! \since build 788
20092071 using BindingMap = YSLib::map<string, TermNode, ystdex::less<>>;
20102072 /*!
@@ -2475,8 +2537,9 @@
24752537 //! \since build 893
24762538 friend
24772539 PDefH(void, swap, ReducerSequence& x, ReducerSequence& y) ynothrowv
2540+ // XXX: As %NPL::AssertMatchedAllocators.
24782541 ImplExpr(YAssert(x.get_allocator() == y.get_allocator(),
2479- "Invalid allocator found."), x.swap(y),
2542+ "Mismatched allocators found."), x.swap(y),
24802543 ystdex::swap_dependent(x.Parent, y.Parent))
24812544
24822545 //! \since build 893
@@ -2943,30 +3006,21 @@
29433006 swap(ContextNode&, ContextNode&) ynothrow;
29443007 };
29453008
2946-
29473009 /*!
2948-\brief 断言分配器和项节点容器的分配器匹配。
3010+\brief 断言分配器和项节容器的分配器匹配。
29493011 \pre 断言:参数指定的分配器相等。
29503012 \since build 941
3013+\relates ContextNode
29513014 */
29523015 //@{
29533016 YB_NONNULL(3) inline PDefH(void, AssertMatchedAllocators,
2954- const TermNode::allocator_type& a, const TermNode::Container& con,
2955- const char* msg = "Allocators mismatch to the term container.") ynothrowv
2956- ImplExpr(yunused(a), yunused(con), yunused(msg),
2957- YAssert(a == con.get_allocator(), msg))
2958-YB_NONNULL(3) inline PDefH(void, AssertMatchedAllocators,
2959- const TermNode::allocator_type& a, const TermNode& nd, const char* msg
2960- = "Allocators mismatch to the term node.") ynothrowv
2961- ImplExpr(AssertMatchedAllocators(a, nd.GetContainer(), msg))
2962-YB_NONNULL(3) inline PDefH(void, AssertMatchedAllocators,
29633017 const ContextNode& ctx, const TermNode::Container& con, const char* msg
2964- = "Allocators mismatch between the term container and the context.")
3018+ = "Allocators for the context and the term node container mismatch.")
29653019 ynothrowv
29663020 ImplExpr(NPL::AssertMatchedAllocators(ctx.get_allocator(), con, msg))
29673021 YB_NONNULL(3) inline PDefH(void, AssertMatchedAllocators,
29683022 const ContextNode& ctx, const TermNode& nd, const char* msg
2969- = "Allocators mismatch between the term node and the context.") ynothrowv
3023+ = "Allocators for the context and the term node mismatch.") ynothrowv
29703024 ImplExpr(NPL::AssertMatchedAllocators(ctx.get_allocator(), nd, msg))
29713025 //@}
29723026
diff -r eae6ca1f576c -r e1756f2eba1f YFramework/include/NPL/SContext.h
--- a/YFramework/include/NPL/SContext.h Thu Jun 30 07:43:19 2022 +0800
+++ b/YFramework/include/NPL/SContext.h Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file SContext.h
1212 \ingroup NPL
1313 \brief S 表达式上下文。
14-\version r4472
14+\version r4516
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 304
1717 \par 创建时间:
1818 2012-08-03 19:55:41 +0800
1919 \par 修改时间:
20- 2022-06-30 04:09 +0800
20+ 2022-07-06 08:26 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -267,7 +267,6 @@
267267 class YF_API TermNode : private ystdex::equality_comparable<TermNode>
268268 {
269269 private:
270- // TODO: Deduplicate within %ValueNode?
271270 template<typename... _tParams>
272271 using enable_value_constructible_t = ystdex::enable_if_t<
273272 std::is_constructible<ValueObject, _tParams...>::value>;
@@ -484,17 +483,6 @@
484483 {}
485484 //@}
486485
487- /*!
488- \brief 复制赋值:使用参数副本和交换操作。
489- \since build 879
490- */
491- PDefHOp(TermNode&, =, const TermNode& nd)
492- ImplRet(ystdex::copy_and_swap(*this, nd))
493- /*!
494- \pre 被转移的参数不是被子节点容器直接或间接所有的其它节点。
495- \warning 违反前置条件的转移可能引起循环引用。
496- */
497- DefDeMoveAssignment(TermNode)
498486 /*
499487 \brief 析构:类定义外默认实现。
500488 \note 除非 Value 的析构非嵌套调用安全,支持移除任意子节点时的嵌套调用安全。
@@ -513,6 +501,19 @@
513501 }
514502
515503 /*!
504+ \brief 复制赋值:使用参数副本和交换操作。
505+ \since build 879
506+ */
507+ PDefHOp(TermNode&, =, const TermNode& nd)
508+ ImplRet(ystdex::copy_and_swap(*this, nd))
509+ /*!
510+ \pre 被转移的参数不是被子节点容器直接或间接所有的其它节点。
511+ \warning 违反前置条件的转移可能引起循环引用。
512+ */
513+ DefDeMoveAssignment(TermNode)
514+
515+
516+ /*!
516517 \brief 比较相等。
517518 \warning 不保证嵌套调用安全。
518519 \since build 944
@@ -993,6 +994,36 @@
993994 ImplExpr(yunused(nd), yunused(msg), YAssert(IsBranchedList(nd), msg))
994995
995996 /*!
997+\brief 断言参数或其分配器匹配。
998+\pre 断言:参数指定的分配器相等。
999+\since build 941
1000+*/
1001+//@{
1002+YB_NONNULL(3) inline PDefH(void, AssertMatchedAllocators,
1003+ const TermNode::allocator_type& a, const TermNode::Container& con,
1004+ const char* msg = "Allocators mismatch to the term container.") ynothrowv
1005+ ImplExpr(yunused(a), yunused(con), yunused(msg),
1006+ YAssert(a == con.get_allocator(), msg))
1007+//! \since build 949
1008+YB_NONNULL(3) inline PDefH(void, AssertMatchedAllocators,
1009+ const TermNode::Container& x, const TermNode::Container& y,
1010+ const char* msg = "Allocators for term containers mismatch.")
1011+ ynothrowv
1012+ ImplExpr(yunused(x), yunused(y), yunused(msg),
1013+ NPL::AssertMatchedAllocators(x.get_allocator(), y))
1014+YB_NONNULL(3) inline PDefH(void, AssertMatchedAllocators,
1015+ const TermNode::allocator_type& a, const TermNode& nd, const char* msg
1016+ = "Allocators mismatch to the term node.") ynothrowv
1017+ ImplExpr(AssertMatchedAllocators(a, nd.GetContainer(), msg))
1018+//! \since build 949
1019+YB_NONNULL(3) inline PDefH(void, AssertMatchedAllocators, const TermNode& x,
1020+ const TermNode& y, const char* msg = "Allocators for terms mismatch.")
1021+ ynothrowv
1022+ ImplExpr(yunused(x), yunused(y), yunused(msg),
1023+ NPL::AssertMatchedAllocators(x.GetContainer(), y.GetContainer()))
1024+//@}
1025+
1026+/*!
9961027 \brief 断言具有可表示被引用对象的标签节点。
9971028 \pre 断言:参数的标签可表示被引用对象的值。
9981029 \sa IsReferentTags
@@ -1142,7 +1173,6 @@
11421173 void
11431174 TraverseSubnodes(_fCallable f, const _tNode& nd)
11441175 {
1145- // TODO: Null coalescing or variant value?
11461176 if(const auto p = AccessPtr<YSLib::NodeSequence>(nd))
11471177 for(const auto& sub : *p)
11481178 ystdex::invoke(f, _tNode(sub));
diff -r eae6ca1f576c -r e1756f2eba1f YFramework/source/NPL/Exception.cpp
--- a/YFramework/source/NPL/Exception.cpp Thu Jun 30 07:43:19 2022 +0800
+++ b/YFramework/source/NPL/Exception.cpp Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file Exception.cpp
1212 \ingroup NPL
1313 \brief NPL 异常。
14-\version r4819
14+\version r4840
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 936
1717 \par 创建时间:
1818 2022-01-21 01:59:50 +0800
1919 \par 修改时间:
20- 2022-06-26 04:07 +0800
20+ 2022-07-05 04:50 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -95,53 +95,59 @@
9595
9696
9797 void
98-ThrowInsufficientTermsError(const TermNode& term, bool has_ref)
98+ThrowInsufficientTermsError(const TermNode& term, bool has_ref, size_t n_skip)
9999 {
100100 throw ParameterMismatch(ystdex::sfmt(
101101 "Insufficient subterms found in '%s' for the list parameter.",
102- TermToStringWithReferenceMark(term, has_ref).c_str()));
102+ TermToStringWithReferenceMark(term, has_ref, n_skip).c_str()));
103103 }
104104
105105 void
106-ThrowListTypeErrorForAtom(const TermNode& term, bool has_ref)
106+ThrowListTypeErrorForAtom(const TermNode& term, bool has_ref, size_t n_skip)
107107 {
108108 throw ListTypeError(ystdex::sfmt("Expected a pair, got '%s'.",
109- TermToStringWithReferenceMark(term, has_ref).c_str()));
109+ TermToStringWithReferenceMark(term, has_ref, n_skip).c_str()));
110110 }
111111
112112 void
113113 ThrowListTypeErrorForInvalidType(const char* name, const TermNode& term,
114- bool has_ref)
114+ bool has_ref, size_t n_skip)
115115 {
116116 throw ListTypeError(ystdex::sfmt("Expected a value of type '%s', got a list"
117- " '%s'.", name, TermToStringWithReferenceMark(term, has_ref).c_str()));
117+ " '%s'.", name, TermToStringWithReferenceMark(term, has_ref, n_skip).c_str()));
118118 }
119119 void
120120 ThrowListTypeErrorForInvalidType(const type_info& ti,
121- const TermNode& term, bool has_ref)
121+ const TermNode& term, bool has_ref, size_t n_skip)
122122 {
123- ThrowListTypeErrorForInvalidType(ti.name(), term, has_ref);
123+ ThrowListTypeErrorForInvalidType(ti.name(), term, has_ref, n_skip);
124124 }
125125
126126 void
127-ThrowListTypeErrorForNonList(const TermNode& term, bool has_ref)
127+ThrowListTypeErrorForNonList(const TermNode& term, bool has_ref, size_t n_skip)
128128 {
129129 throw ListTypeError(ystdex::sfmt("Expected a list, got '%s'.",
130- TermToStringWithReferenceMark(term, has_ref).c_str()));
130+ TermToStringWithReferenceMark(term, has_ref, n_skip).c_str()));
131131 }
132132
133133 void
134-ThrowTypeErrorForInvalidType(const char* name, const TermNode& term,
135- bool has_ref)
134+ThrowTypeErrorForInvalidType(const char* name, const char* rep)
136135 {
137136 throw TypeError(ystdex::sfmt("Expected a value of type '%s', got '%s'.",
138- name, TermToStringWithReferenceMark(term, has_ref).c_str()));
137+ name, rep));
138+}
139+void
140+ThrowTypeErrorForInvalidType(const char* name, const TermNode& term,
141+ bool has_ref, size_t n_skip)
142+{
143+ ThrowTypeErrorForInvalidType(name,
144+ TermToStringWithReferenceMark(term, has_ref, n_skip).c_str());
139145 }
140146 void
141147 ThrowTypeErrorForInvalidType(const type_info& ti, const TermNode& term,
142- bool has_ref)
148+ bool has_ref, size_t n_skip)
143149 {
144- ThrowTypeErrorForInvalidType(ti.name(), term, has_ref);
150+ ThrowTypeErrorForInvalidType(ti.name(), term, has_ref, n_skip);
145151 }
146152
147153 } // namespace NPL;
diff -r eae6ca1f576c -r e1756f2eba1f YFramework/source/NPL/NPLA.cpp
--- a/YFramework/source/NPL/NPLA.cpp Thu Jun 30 07:43:19 2022 +0800
+++ b/YFramework/source/NPL/NPLA.cpp Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA.cpp
1212 \ingroup NPL
1313 \brief NPLA 公共接口。
14-\version r4137
14+\version r4188
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 663
1717 \par 创建时间:
1818 2016-01-07 10:32:45 +0800
1919 \par 修改时间:
20- 2022-06-25 18:21 +0800
20+ 2022-07-06 20:50 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -32,14 +32,14 @@
3232 // AccessPtr, ystdex::value_or, ystdex::write, std::bind, TraverseSubnodes,
3333 // bad_any_cast, std::allocator_arg, YSLib::NodeSequence, ystdex::begins_with,
3434 // ystdex::sfmt, observer_ptr, ystdex::make_obj_using_allocator, trivial_swap,
35-// make_observer, TermTags, TryAccessLeafAtom, NPL::Deref, YSLib::sfmt,
36-// AssertReferentTags, ystdex::call_value_or, ystdex::compose, GetLValueTagsOf,
37-// std::mem_fn, IsTyped, ystdex::invoke_value_or, ystdex::ref, PropagateTo,
38-// NPL::IsMovable, IsSticky, TryAccessLeaf, AccessFirstSubterm,
39-// YSLib::FilterExceptions, type_id, ystdex::addrof, ystdex::second_of,
40-// type_info, std::current_exception, std::rethrow_exception,
41-// std::throw_with_nested, ystdex::retry_on_cond, ystdex::id, pair,
42-// YSLib::ExtractException;
35+// CountPrefix, make_observer, TermTags, TryAccessLeafAtom, NPL::Deref,
36+// YSLib::sfmt, AssertReferentTags, ystdex::call_value_or, ystdex::compose,
37+// GetLValueTagsOf, std::mem_fn, IsTyped, ystdex::invoke_value_or, ystdex::ref,
38+// PropagateTo, IsSticky, FindSticky, std::distance, ystdex::as_const,
39+// TryAccessLeaf, AccessFirstSubterm, YSLib::FilterExceptions, type_id,
40+// ystdex::addrof, ystdex::second_of, type_info, std::current_exception,
41+// std::rethrow_exception, std::throw_with_nested, ystdex::retry_on_cond,
42+// ystdex::id, pair, NPL::IsMovable, YSLib::ExtractException;
4343 #include <ystdex/function.hpp> // for ystdex::unchecked_function;
4444
4545 //! \since build 903
@@ -357,25 +357,42 @@
357357
358358
359359 string
360-TermToString(const TermNode& term)
360+TermToString(const TermNode& term, size_t n_skip)
361361 {
362362 if(const auto p = TermToNamePtr(term))
363363 return *p;
364364
365- const bool is_pair(IsPair(term));
366365 const bool non_list(!IsList(term));
367366
368367 // TODO: Use allocator?
369- return IsEmpty(term) ? string("()") : YSLib::sfmt<string>("#<%s{%zu}%s%s>",
370- is_pair ? (non_list ? "improper-list" : "list") : "unknown",
371- term.size(), is_pair ? " . " : (non_list ? ":" : ""),
368+ // NOTE: The case for no skip is shown below.
369+#if false
370+ if(n_skip == 0)
371+ {
372+ const bool is_pair(IsPair(term));
373+
374+ return IsEmpty(term) ? string("()") : YSLib::sfmt<string>(
375+ "#<%s{%zu}%s%s>", is_pair ? (non_list ? "improper-list" : "list")
376+ : "unknown", term.size(), is_pair ? " . " : (non_list ? ":" : ""),
377+ non_list ? term.Value.type().name() : "");
378+ }
379+#endif
380+ YAssert(n_skip <= CountPrefix(term), "Invalid skip number found.");
381+
382+ const bool s_is_pair(n_skip < term.size()
383+ && IsSticky(std::next(term.begin(), ptrdiff_t(n_skip))->Tags));
384+
385+ return !non_list && n_skip == term.size() ? string("()")
386+ : YSLib::sfmt<string>("#<%s{%zu}%s%s>",
387+ s_is_pair ? (non_list ? "improper-list" : "list") : "unknown",
388+ term.size() - n_skip, s_is_pair ? " . " : (non_list ? ":" : ""),
372389 non_list ? term.Value.type().name() : "");
373390 }
374391
375392 string
376-TermToStringWithReferenceMark(const TermNode& term, bool has_ref)
393+TermToStringWithReferenceMark(const TermNode& term, bool has_ref, size_t n_skip)
377394 {
378- auto term_str(TermToString(term));
395+ auto term_str(TermToString(term, n_skip));
379396
380397 return has_ref ? "[*] " + std::move(term_str) : std::move(term_str);
381398 }
@@ -630,11 +647,28 @@
630647 #endif
631648 }
632649
650+TNIter
651+LiftPrefixToReturn(TermNode& term, TNCIter it)
652+{
653+ YAssert(size_t(std::distance(ystdex::as_const(term).begin(), it))
654+ <= CountPrefix(term), "Invalid arguments found.");
655+
656+ auto i(ystdex::cast_mutable(term.GetContainerRef(), it));
657+
658+ while(i != term.end() && !IsSticky(i->Tags))
659+ {
660+ LiftToReturn(*i);
661+ ++i;
662+ }
663+ YAssert(term.Value || i == term.end(), "Invalid representation found.");
664+ return i;
665+}
666+
633667 void
634-LiftElementsToReturn(TermNode& term)
668+LiftSuffixToReturn(TermNode& term, TNCIter i)
635669 {
636- const auto i(LiftPrefixToReturn(term));
637-
670+ YAssert(FindSticky(ystdex::as_const(term).begin(),
671+ ystdex::as_const(term).end()) == i, "Invalid arguments found.");
638672 // NOTE: As %LiftToReturn without overriding the previous transformed terms.
639673 if(const auto p = TryAccessLeaf<const TermReference>(term))
640674 {
@@ -689,20 +723,6 @@
689723 // later in %RegularizeTerm).
690724 }
691725
692-TNIter
693-LiftPrefixToReturn(TermNode& term)
694-{
695- auto i(term.begin());
696-
697- while(i != term.end() && !IsSticky(i->Tags))
698- {
699- LiftToReturn(*i);
700- ++i;
701- }
702- YAssert(term.Value || i == term.end(), "Invalid representation found.");
703- return i;
704-}
705-
706726
707727 bool
708728 CheckReducible(ReductionStatus status)
@@ -848,7 +868,7 @@
848868 {
849869 const auto& ti(vo.type());
850870
851- if(ti == type_id<EnvironmentList>())
871+ if(IsTyped<EnvironmentList>(ti))
852872 {
853873 for(const auto& env : vo.GetObject<EnvironmentList>())
854874 CheckParent(env);
diff -r eae6ca1f576c -r e1756f2eba1f YFramework/source/NPL/NPLA1.cpp
--- a/YFramework/source/NPL/NPLA1.cpp Thu Jun 30 07:43:19 2022 +0800
+++ b/YFramework/source/NPL/NPLA1.cpp Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1.cpp
1212 \ingroup NPL
1313 \brief NPLA1 公共接口。
14-\version r22575
14+\version r23050
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 472
1717 \par 创建时间:
1818 2014-02-02 18:02:47 +0800
1919 \par 修改时间:
20- 2022-06-30 06:54 +0800
20+ 2022-07-12 18:03 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -28,25 +28,25 @@
2828 #include "NPL/YModules.h"
2929 #include YFM_NPL_NPLA1Forms // for NPL, EvaluationPasses, lref, ContextHandler,
3030 // RelaySwitched, trivial_swap, type_index, string_view, std::hash,
31-// ystdex::equal_to, YSLib::unordered_map, std::piecewise_construct,
32-// YSLib::lock_guard, YSLib::mutex, type_id, make_observer, IsBranch,
33-// CheckReducible, IsNPLAExtendedLiteralNonDigitPrefix, IsAllSignLexeme,
34-// AllocatorHolder, ystdex::ref, YSLib::IValueHolder,
35-// YSLib::AllocatedHolderOperations, any, ystdex::as_const,
36-// NPL::forward_as_tuple, uintmax_t, ystdex::bind1, TokenValue,
37-// Forms::Sequence, std::allocator_arg, ReduceBranchToList, YSLib::stack,
38-// YSLib::vector, TermTags, function, TermReference, GetLValueTagsOf,
39-// TryAccessLeaf, ListReductionFailure, ystdex::sfmt, TryAccessLeafAtom,
40-// PropagateTo, NPL::IsMovable, in_place_type, InvalidReference, NPL::Deref,
41-// IsLeaf, ResolveTerm, ThrowInsufficientTermsError,
42-// ThrowListTypeErrorForNonList, ystdex::update_thunk, Environment, shared_ptr,
43-// NPL::AsTermNode, IsTyped, ystdex::retry_on_cond, AccessFirstSubterm,
44-// ystdex::make_transform, IsBranchedList, std::placeholders, NoContainer,
45-// ystdex::try_emplace, Access, YSLib::Informative, ystdex::unique_guard,
46-// CategorizeBasicLexeme, DeliteralizeUnchecked, Deliteralize, ystdex::isdigit,
47-// INT_MAX, ystdex::ref_eq, TryAccessTerm, YSLib::share_move,
48-// ystdex::call_value_or, std::to_string, YSLib::Notice,
49-// YSLib::FilterException, Session;
31+// ystdex::equal_to, YSLib::unordered_map, YSLib::lock_guard, YSLib::mutex,
32+// std::ref, ListReductionFailure, ystdex::sfmt, FetchArgumentN, std::next,
33+// IsBranch, TermReference, std::allocator_arg, in_place_type, CheckReducible,
34+// IsNPLAExtendedLiteralNonDigitPrefix, IsAllSignLexeme, AllocatorHolder,
35+// YSLib::IValueHolder, ystdex::ref, YSLib::AllocatedHolderOperations, any,
36+// ystdex::as_const, NPL::forward_as_tuple, uintmax_t, ystdex::bind1,
37+// TokenValue, Forms::Sequence, ReduceBranchToList, YSLib::stack, vector,
38+// GetLValueTagsOf, TermTags, function, IsList, IsIgnore, TryAccessLeafAtom,
39+// PropagateTo, InvalidReference, tuple, TNCIter, NPL::get, NPL::Deref,
40+// ResolveTerm, TryAccessLeaf, std::prev, ThrowInsufficientTermsError,
41+// ThrowListTypeErrorForNonList, IsTyped, ReferenceTerm, Environment,
42+// ystdex::begins_with, IsMovable, NPL::AsTermNodeTagged, TermTags::Sticky,
43+// shared_ptr, ystdex::retry_on_cond, AccessFirstSubterm, ystdex::ref_eq,
44+// ystdex::make_transform, IsBranchedList, NPL::IsMovable, std::placeholders,
45+// NoContainer, ystdex::try_emplace, Access, YSLib::Informative,
46+// ystdex::unique_guard, CategorizeBasicLexeme, DeliteralizeUnchecked,
47+// Deliteralize, IsLeaf, TryAccessTerm, YSLib::share_move,
48+// ystdex::call_value_or, std::piecewise_construct, type_id, make_observer,
49+// YSLib::Notice, YSLib::FilterException, Session;
5050 #include "NPLA1Internals.h" // for A1::Internals API;
5151 #include YFM_NPL_NPLAMath // for ReadDecimal;
5252 #include <limits> // for std::numeric_limits;
@@ -77,30 +77,37 @@
7777 // single instance of %GParameterMatcher (%BindParameterWellFormed only) from
7878 // 360s to less than 90s, at the cost of a bit worse generated code. See also
7979 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101279.
80-// NOTE: Optimization value for call expansions in %Forms::(LetAsterisk,
81-// LetAsteriskRef), in range [0, 3]. The meanings of the value are:
82-// 0: Flatten no %BindParameterImpl instances
83-// (defualt to debug or size-optimized releases).
84-// 1: Flatten only %BindParameterWellFormed.
85-// 2: Flatten all %BindParameterImpl instances (default for other releases).
86-// This may even a bit better than 3 below for some compilation
87-// configurations.
88-// 3: Flatten all %BindParameterImpl instances, (much) slower build (~7x).
80+// NOTE: Optimization value for call expansions in %(MatchParameter,
81+// BindParameter, BindParameterWellFormed, in range [0, 3]. The meanings of the
82+// value are (performance based on x86_64-pc-linux G++ 12.1):
83+// 0: Flatten no %BindParameterImpl instances, likely not optimal for release
84+// configurations. This is defualt for debug or size-optimized releases.
85+// 1: Flatten only %BindParameterWellFormed, reasonably fast both at
86+// compilation and runtime, by preventing slow compilation of flattening
87+// %BindParameter for some configurations. This is default for other
88+// release since b949.
89+// 2: Flatten all %BindParameterImpl instances (default for other releases in
90+// b948). This might even a bit better than 3 below for some compilation
91+// configurations when using %Action and a bit better than expansion level
92+// 1 in b948, so it was the default for other releases then.
93+// 3: Flatten all %BindParameterImpl instances, significantly slower build
94+// compared to expansion level 2 (~7x when using %Action or ~1.5x when
95+// using %YSLib::stack and %vector).
8996 #ifndef NPL_Impl_NPLA1_BindParameter_ExpandLevel
9097 # if !defined(NDEBUG) || __OPTIMIZE_SIZE__
9198 # define NPL_Impl_NPLA1_BindParameter_ExpandLevel 0
9299 # else
93-# define NPL_Impl_NPLA1_BindParameter_ExpandLevel 2
100+# define NPL_Impl_NPLA1_BindParameter_ExpandLevel 1
94101 # endif
95102 #endif
103+// NOTE: This enables the hinted inlining to accelerate flattened inline
104+// heuristics. It is less significant in b949 (using %YSLib::stack and %vector)
105+// for level 1 than in b948 (%Action).
96106 #if NPL_Impl_NPLA1_BindParameter_ExpandLevel == 1 \
97- || NPL_Impl_NPLA1_BindParameter_ExpandLevel == 2
107+ || NPL_Impl_NPLA1_BindParameter_ExpandLevel == 2
98108 # define NPL_Impl_NPLA1_BindParameter_Inline YB_ATTR_always_inline
99-# define NPL_Impl_NPLA1_BindParameter_InlineAnnotate(_arglist) \
100- YB_LAMBDA_ANNOTATE(_arglist, , always_inline)
101109 #else
102110 # define NPL_Impl_NPLA1_BindParameter_Inline
103-# define NPL_Impl_NPLA1_BindParameter_InlineAnnotate(_arglist) _arglist
104111 #endif
105112 //@}
106113
@@ -328,7 +335,6 @@
328335 // XXX: All other value is ignored. As %CombinerReturnThunk, this is
329336 // necessary to the precondition for calling %FetchArgumentN.
330337 term.Value.Clear();
331- // TODO: Extract general form by some information extractor function.
332338 throw ListReductionFailure(ystdex::sfmt("No matching combiner '%s'"
333339 " for operand with %zu argument(s) found.", name.c_str(),
334340 FetchArgumentN(term)));
@@ -372,7 +378,6 @@
372378 ReduceSequenceOrderedAsync(TermNode& term, ContextNode& ctx, TNIter i)
373379 {
374380 YAssert(i != term.end(), "Invalid iterator found for sequence reduction.");
375- // TODO: Allow capture next sequenced evaluations as a single continuation?
376381 return std::next(i) == term.end() ? ReduceOnceLifted(term, ctx, *i)
377382 : ReduceSubsequent(*i, ctx,
378383 NameTypedReducerHandler(EvalSequence{term, i}, "eval-sequence"));
@@ -504,8 +509,7 @@
504509 #if NPL_Impl_NPLA1_Enable_ThunkedSeparatorPass
505510
506511 //! \since build 882
507- using TermStack
508- = YSLib::stack<lref<TermNode>, YSLib::vector<lref<TermNode>>>;
512+ using TermStack = YSLib::stack<lref<TermNode>, vector<lref<TermNode>>>;
509513
510514 //! \since build 882
511515 mutable TermStack remained{alloc};
@@ -625,6 +629,8 @@
625629 if(sigil != '@')
626630 {
627631 const bool can_modify(!bool(o_tags & TermTags::Nonmodifying));
632+ // XXX: Put this declaration here instead of the outer block is
633+ // better for both compilation performance and generated code.
628634 const auto a(o.get_allocator());
629635
630636 // NOTE: Subterms in arguments retained are also transferred for
@@ -682,9 +688,6 @@
682688 // The anchor here (if any) is not accurate because it refers
683689 // to the anchor saved by the reference (if any), not
684690 // necessarily the original environment owning the referent.
685- // TODO: Remove anchors without TCO (as it always interferes
686- // %NPL_NPLA_CheckEnvironmentReferenceCount for no
687- // benefits)?
688691 mv(TermNode::Container(o.get_allocator()),
689692 // XXX: Term tags on prvalues are reserved and should be
690693 // ignored normally except for future internal use. Note
@@ -717,22 +720,23 @@
717720 {
718721 using HasReferenceArg = ystdex::true_;
719722
723+ //! \since build 949
720724 NPL_Impl_NPLA1_BindParameter_Inline static void
721- CheckBack(const TermNode& t, bool t_has_ref)
725+ CheckBack(const TermNode& t, bool has_ref, bool indirect)
722726 {
723727 if(YB_UNLIKELY(!IsList(t)))
724- ThrowFormalParameterTypeError(t, t_has_ref);
728+ ThrowFormalParameterTypeError(t, has_ref || indirect);
725729 }
726730
727731 template<typename _func>
728732 NPL_Impl_NPLA1_BindParameter_Inline static inline void
729- HandleLeaf(_func f, const TermNode& t, bool t_has_ref)
733+ HandleLeaf(_func f, const TermNode& t, bool has_ref)
730734 {
731- HandleOrIgnore(std::ref(f), t, t_has_ref);
735+ HandleOrIgnore(std::ref(f), t, has_ref);
732736 }
733737
734738 template<typename _func>
735- NPL_Impl_NPLA1_BindParameter_Inline static inline void
739+ static inline void
736740 WrapCall(_func f)
737741 {
738742 try
@@ -750,15 +754,16 @@
750754 {
751755 using HasReferenceArg = ystdex::false_;
752756
757+ //! \since build 949
753758 static void
754- CheckBack(const TermNode& t)
759+ CheckBack(const TermNode& t, bool)
755760 {
756761 yunused(t);
757762 YAssert(IsList(t), "Invalid parameter tree found.");
758763 }
759764
760765 template<typename _func>
761- NPL_Impl_NPLA1_BindParameter_Inline static inline void
766+ static inline void
762767 HandleLeaf(_func f, const TermNode& t)
763768 {
764769 if(!IsIgnore(t))
@@ -793,27 +798,76 @@
793798 #endif
794799 //@}
795800
796-//! \since build 917
797-//@{
798-template<class _tTraits, typename _fBindTrailing, typename _fBindValue>
799-class GParameterMatcher final
801+//! \since build 949
802+template<typename _fBindTrailing, typename _fBindValue>
803+struct GBinder final
800804 {
801- //! \since build 863
802- //@{
803-public:
804805 _fBindTrailing BindTrailing;
805806 _fBindValue BindValue;
806807
808+ template<class _type, class _type2>
809+ inline
810+ GBinder(_type&& arg, _type2&& arg2)
811+ : BindTrailing(yforward(arg)), BindValue(yforward(arg2))
812+ {}
813+
814+ YB_ATTR_always_inline void
815+ operator()(TermNode& o, TNIter first, const TokenValue& n,
816+ TermTags o_tags, const EnvironmentReference& r_env) const
817+ {
818+ BindTrailing(o, first, n, o_tags, r_env);
819+ }
820+ YB_ATTR_always_inline void
821+ operator()(const TokenValue& n, TermNode& o, TermTags o_tags,
822+ const EnvironmentReference& r_env) const
823+ {
824+ BindValue(n, o, o_tags, r_env);
825+ }
826+};
827+
828+
829+//! \since build 949
830+//@{
831+// NOTE: Entry components are: ptree subterms iterator, ptree boundary iterator,
832+// reference to the operand, operand subterms iterator, operand term tags,
833+// reference to the weak environment, ellipsis mark,
834+// (if %NPL_Impl_NPLA1_AssertParameterMatch) ptree end iterator;
835+enum : size_t
836+{
837+ PTreeFirst,
838+ PTreeMid,
839+ OperandRef,
840+ OperandFirst,
841+ OperandTags,
842+ EnvironmentRef,
843+ Ellipsis
844+#if NPL_Impl_NPLA1_AssertParameterMatch
845+ , PTreeEnd
846+#endif
847+};
848+
849+using MatchEntry = tuple<TNCIter, TNCIter, lref<TermNode>, TNIter, TermTags,
850+ lref<const EnvironmentReference>, bool
851+#if NPL_Impl_NPLA1_AssertParameterMatch
852+ , TNCIter
853+#endif
854+>;
855+
856+
857+template<class _tTraits, typename _fBind>
858+class GParameterMatcher final
859+{
860+public:
861+ _fBind Bind;
862+
807863 private:
808- mutable Action act{};
809- //@}
864+ mutable YSLib::stack<MatchEntry, vector<MatchEntry>> remained;
810865
811866 public:
812- //! \since build 882
813- template<class _type, class _type2>
867+ template<typename... _tParams>
814868 inline
815- GParameterMatcher(_type&& arg, _type2&& arg2)
816- : BindTrailing(yforward(arg)), BindValue(yforward(arg2))
869+ GParameterMatcher(TermNode::allocator_type a, _tParams&&... args)
870+ : Bind(yforward(args)...), remained(a)
817871 {}
818872
819873 // NOTE: The environment argument is for rvalues in the caller. All prvalues
@@ -829,200 +883,231 @@
829883 operator()(const TermNode& t, TermNode& o, TermTags o_tags,
830884 const EnvironmentReference& r_env) const
831885 {
832- _tTraits::WrapCall([&] NPL_Impl_NPLA1_BindParameter_InlineAnnotate(()){
886+ _tTraits::WrapCall([&]{
887+ Dispatch(ystdex::true_(), typename _tTraits::HasReferenceArg(),
888+ ystdex::false_(), t, o, o_tags, r_env);
833889 // NOTE: This is a trampoline to eliminate the call depth
834890 // limitation.
835- DispatchMatch(typename _tTraits::HasReferenceArg(), t, o, o_tags,
836- r_env, ystdex::false_());
837- while(act)
891+ while(!remained.empty())
838892 {
839- const auto a(std::move(act));
893+ auto& e(remained.top());
894+ auto& i(NPL::get<PTreeFirst>(e));
840895
841- a();
896+ if(i == NPL::get<PTreeMid>(e))
897+ {
898+ if(NPL::get<Ellipsis>(e))
899+ {
900+ MatchTrailing(e);
901+#if NPL_Impl_NPLA1_AssertParameterMatch
902+ YAssert(std::next(NPL::get<PTreeMid>(e))
903+ == NPL::get<PTreeEnd>(e), "Invalid state found.");
904+#endif
905+ }
906+ remained.pop();
907+ }
908+ else
909+ {
910+ auto& j(NPL::get<OperandFirst>(e));
911+
912+ YAssert(j != NPL::get<OperandRef>(e).get().end(),
913+ "Invalid state of operand found.");
914+ // XXX: The iterators are incremented here to allow
915+ // invalidation of entries of %remained in the call.
916+ Dispatch(ystdex::true_(),
917+ typename _tTraits::HasReferenceArg(), ystdex::false_(),
918+ NPL::Deref(i++), NPL::Deref(j++),
919+ NPL::get<OperandTags>(e), NPL::get<EnvironmentRef>(e));
920+ }
842921 }
843922 });
844923 }
845924
846925 private:
847- template<class _tArg>
848- NPL_Impl_NPLA1_BindParameter_Inline inline void
849- DispatchMatch(ystdex::true_, const TermNode& t, TermNode& o,
850- TermTags o_tags, const EnvironmentReference& r_env, _tArg) const
926+ template<class _tEnableIndirect, class _tArg, typename... _tParams>
927+ inline void
928+ Dispatch(_tEnableIndirect, ystdex::true_, _tArg, _tParams&&... args) const
851929 {
852- Match(t, o, o_tags, r_env, _tArg::value);
930+ Match(_tEnableIndirect(), yforward(args)..., _tArg::value);
853931 }
854- template<class _tArg>
855- NPL_Impl_NPLA1_BindParameter_Inline inline void
856- DispatchMatch(ystdex::false_, const TermNode& t, TermNode& o,
857- TermTags o_tags, const EnvironmentReference& r_env, _tArg) const
932+ template<class _tEnableIndirect, class _tArg, typename... _tParams>
933+ inline void
934+ Dispatch(_tEnableIndirect, ystdex::false_, _tArg, _tParams&&... args) const
858935 {
859- Match(t, o, o_tags, r_env);
936+ Match(_tEnableIndirect(), yforward(args)...);
860937 }
861938
862939 // XXX: The initial %o_tags shall have %TermTags::Temporary unless it is
863940 // known to bound to some non-temporary objects not stored in the term tree
864941 // to be reduced.
865942 template<typename... _tParams>
866- NPL_Impl_NPLA1_BindParameter_Inline YB_FLATTEN inline void
867- Match(const TermNode& t, TermNode& o, TermTags o_tags,
868- const EnvironmentReference& r_env, _tParams&&... args) const
943+ inline void
944+ Match(ystdex::true_, const TermNode& t, _tParams&&... args) const
869945 {
870946 if(IsList(t))
871- {
872- if(IsBranch(t))
873- {
874- const auto n_p(t.size());
875- auto last(t.end());
947+ MatchList(t, yforward(args)...);
948+ else if(const auto p_t = TryAccessLeafAtom<const TermReference>(t))
949+ MatchIndirect(p_t->get(), yforward(args)...);
950+ else
951+ MatchNonList(t, yforward(args)...);
952+ }
953+ template<typename... _tParams>
954+ inline void
955+ Match(ystdex::false_, const TermNode& t, _tParams&&... args) const
956+ {
957+ if(IsList(t))
958+ MatchList(t, yforward(args)...);
959+ else
960+ MatchNonList(t, yforward(args)...);
961+ }
876962
877- if(n_p > 0)
878- {
879- const auto&
880- back(ReferenceTerm(NPL::Deref(std::prev(last))));
963+ template<typename... _tParams>
964+ inline void
965+ MatchIndirect(const TermNode& t, TermNode& o, TermTags o_tags,
966+ const EnvironmentReference& r_env, _tParams&&...) const
967+ {
968+ Dispatch(ystdex::false_(), typename _tTraits::HasReferenceArg(),
969+ ystdex::true_(), t, o, o_tags, r_env);
970+ }
881971
882- // NOTE: Empty list lvalue arguments shall not be matched
883- // here.
884- if(IsLeaf(back))
972+ template<typename... _tParams>
973+ inline void
974+ MatchList(const TermNode& t, TermNode& o, TermTags o_tags,
975+ const EnvironmentReference& r_env, _tParams&&... args) const
976+ {
977+ YAssert(IsList(t), "Invalid term found.");
978+ if(IsBranch(t))
979+ {
980+ const auto n_p(t.size());
981+ auto mid(t.end());
982+
983+ if(n_p > 0)
984+ ResolveTerm(
985+ [&](const TermNode& nd, ResolvedTermReferencePtr p_ref){
986+ if(IsAtom(nd))
885987 {
886- if(const auto p
887- = TryAccessLeafAtom<TokenValue>(back))
988+ if(const auto p = TryAccessLeaf<TokenValue>(nd))
888989 {
889990 if(!p->empty() && p->front() == '.')
890- --last;
991+ --mid;
891992 }
892- else if(!IsIgnore(back))
893- _tTraits::CheckBack(back, yforward(args)...);
993+ else if(!IsIgnore(nd))
994+ _tTraits::CheckBack(nd, p_ref, yforward(args)...);
894995 }
895- }
896- // XXX: There is only one level of indirection. It should work
897- // with the correct implementation of the reference collapse.
898- ResolveTerm([&, o_tags]
899- NPL_Impl_NPLA1_BindParameter_InlineAnnotate(
900- (TermNode& nd, ResolvedTermReferencePtr p_ref)){
901- if(IsList(nd))
902- {
903- const bool ellipsis(last != t.end());
904- const auto n_o(nd.size());
905-
906- if(n_p == n_o || (ellipsis && n_o >= n_p - 1))
907- {
908- auto tags(o_tags);
909-
910- // NOTE: All tags as type qualifiers should be
911- // checked here. Currently only glvalues can be
912- // qualified.
913- // XXX: Term tags are currently not respected in
914- // prvalues.
915- if(p_ref)
916- {
917- const auto ref_tags(p_ref->GetTags());
996+ }, NPL::Deref(std::prev(mid)));
997+ // XXX: There is only one level of indirection. It should work
998+ // with the correct implementation of the reference collapse.
999+ ResolveTerm(
1000+ [&, o_tags](TermNode& nd, ResolvedTermReferencePtr p_ref){
1001+ if(IsList(nd))
1002+ {
1003+ const bool ellipsis(mid != t.end());
1004+ const auto n_o(nd.size());
9181005
919- // NOTE: Drop the temporary tag unconditionally.
920- // Even the referent is a list temporary
921- // object, its elements are not prvalues to be
922- // matched (whatever the types thereof).
923- tags = (tags
924- & ~(TermTags::Unique | TermTags::Temporary))
925- | (ref_tags & TermTags::Unique);
926- // NOTE: Propagate %TermTags::Nonmodifying to
927- // the referent.
928- tags = PropagateTo(tags, ref_tags);
929- }
930- MatchSubterms(t.begin(), last, nd, nd.begin(), tags,
931- p_ref ? p_ref->GetEnvironmentReference()
932- : r_env, ellipsis
933-#if NPL_Impl_NPLA1_AssertParameterMatch
934- , t.end()
935-#endif
936- );
1006+ if(n_p == n_o || (ellipsis && n_o >= n_p - 1))
1007+ {
1008+ auto tags(o_tags);
1009+
1010+ // NOTE: All tags as type qualifiers should be
1011+ // checked here. Currently only glvalues can be
1012+ // qualified.
1013+ // XXX: Term tags are currently not respected in
1014+ // prvalues.
1015+ if(p_ref)
1016+ {
1017+ const auto ref_tags(p_ref->GetTags());
1018+
1019+ // NOTE: Drop the temporary tag unconditionally.
1020+ // Even the referent is a list temporary
1021+ // object, its elements are not prvalues to be
1022+ // matched (whatever the types thereof).
1023+ tags = (tags
1024+ & ~(TermTags::Unique | TermTags::Temporary))
1025+ | (ref_tags & TermTags::Unique);
1026+ // NOTE: Propagate %TermTags::Nonmodifying to
1027+ // the referent.
1028+ tags = PropagateTo(tags, ref_tags);
9371029 }
938- else if(!ellipsis)
939- throw ArityMismatch(n_p, n_o);
940- else
941- ThrowInsufficientTermsError(nd, p_ref);
1030+ // XXX: When it is known to only have one trailing
1031+ // subterm, calling to the binding routine can bypass
1032+ // the several operations on %remained. However,
1033+ // although this saves accesses to %remained, it occurs
1034+ // rarely and actually perform worse by more
1035+ // inefficient branching and inlining for typical
1036+ // cases. Avoiding this branch (which would be reused
1037+ // in the trampoline body in %operator()) also improves
1038+ // the compilation performance significantly.
1039+ remained.emplace(t.begin(), mid, nd, nd.begin(),
1040+ tags, p_ref ? p_ref->GetEnvironmentReference()
1041+ : r_env, ellipsis
1042+#if NPL_Impl_NPLA1_AssertParameterMatch
1043+ , t.end()
1044+#endif
1045+ );
9421046 }
1047+ else if(!ellipsis)
1048+ throw ArityMismatch(n_p, n_o);
9431049 else
944- ThrowListTypeErrorForNonList(nd, p_ref);
945- }, o);
946- }
947- else
948- ResolveTerm([&](const TermNode& nd, bool has_ref){
949- if(nd)
950- throw ParameterMismatch(ystdex::sfmt("Invalid nonempty"
951- " operand value '%s' found for empty list"
952- " parameter.", TermToStringWithReferenceMark(nd,
953- has_ref).c_str()));
954- }, o);
955- }
956- else if(const auto p_t = TryAccessLeafAtom<const TermReference>(t))
957- {
958- auto& nd(p_t->get());
959-
960- ystdex::update_thunk(act, o.get_allocator(), trivial_swap,
961- [&, o_tags] NPL_Impl_NPLA1_BindParameter_InlineAnnotate(()){
962- DispatchMatch(typename _tTraits::HasReferenceArg(), nd, o,
963- o_tags, r_env, ystdex::true_());
964- });
1050+ ThrowInsufficientTermsError(nd, p_ref);
1051+ }
1052+ else
1053+ ThrowListTypeErrorForNonList(nd, p_ref);
1054+ }, o);
9651055 }
9661056 else
967- _tTraits::HandleLeaf([&] NPL_Impl_NPLA1_BindParameter_InlineAnnotate
968- ((const TokenValue& n)){
969- BindValue(n, o, o_tags, r_env);
970- }, t, yforward(args)...);
1057+ ResolveTerm([&](const TermNode& nd, bool has_ref){
1058+ if(nd)
1059+ throw ParameterMismatch(ystdex::sfmt("Invalid nonempty"
1060+ " operand value '%s' found for empty list"
1061+ " parameter.", TermToStringWithReferenceMark(nd,
1062+ has_ref).c_str()));
1063+ }, o);
9711064 }
9721065
973- //! \since build 898
974- NPL_Impl_NPLA1_BindParameter_Inline void
975- MatchSubterms(TNCIter i, TNCIter last, TermNode& o_tm, TNIter j,
976- TermTags tags, const EnvironmentReference& r_env, bool ellipsis
977-#if NPL_Impl_NPLA1_AssertParameterMatch
978- , TNCIter t_end
979-#endif
980- ) const
1066+ template<typename... _tParams>
1067+ inline void
1068+ MatchNonList(const TermNode& t, TermNode& o, TermTags o_tags,
1069+ const EnvironmentReference& r_env, _tParams&&... args) const
9811070 {
982- if(i != last)
983- {
984- // XXX: Use explicit captures here to ensure ISO C++20
985- // compatibility.
986- ystdex::update_thunk(act, o_tm.get_allocator(), trivial_swap,
987- [this, i, j, last, tags, ellipsis,
988-#if NPL_Impl_NPLA1_AssertParameterMatch
989- t_end,
990-#endif
991- &o_tm, &r_env]{
992- return MatchSubterms(std::next(i), last, o_tm, std::next(j),
993-#if NPL_Impl_NPLA1_AssertParameterMatch
994- tags, r_env, ellipsis, t_end);
995-#else
996- tags, r_env, ellipsis);
997-#endif
998- });
999- YAssert(j != o_tm.end(), "Invalid state of operand found.");
1000- DispatchMatch(typename _tTraits::HasReferenceArg(), NPL::Deref(i),
1001- NPL::Deref(j), tags, r_env, ystdex::false_());
1002- }
1003- else if(ellipsis)
1004- {
1005- const auto& lastv(NPL::Deref(last).Value);
1071+ YAssert(!IsList(t), "Invalid term found.");
1072+ _tTraits::HandleLeaf([&](const TokenValue& n){
1073+ Bind(n, o, o_tags, r_env);
1074+ }, t, yforward(args)...);
1075+ }
10061076
1007- YAssert(lastv.type() == type_id<TokenValue>(),
1008- "Invalid ellipsis sequence token found.");
1009- BindTrailing(o_tm, j, lastv.GetObject<TokenValue>(), tags, r_env);
1010-#if NPL_Impl_NPLA1_AssertParameterMatch
1011- YAssert(std::next(last) == t_end, "Invalid state found.");
1012-#endif
1013- }
1077+ // XXX: Make this a separated function is both a bit better in the generated
1078+ // code and compilation performance.
1079+ void
1080+ MatchTrailing(MatchEntry& e) const
1081+ {
1082+ const auto& mid(NPL::get<PTreeMid>(e));
1083+ const auto& trailing(NPL::Deref(mid));
1084+
1085+ YAssert(IsTyped<TokenValue>(ReferenceTerm(trailing)),
1086+ "Invalid ellipsis sequence token found.");
1087+ Bind(NPL::get<OperandRef>(e), NPL::get<OperandFirst>(e),
1088+ trailing.Value.template GetObject<TokenValue>(),
1089+ NPL::get<OperandTags>(e), NPL::get<EnvironmentRef>(e));
10141090 }
10151091 };
10161092
10171093 //! \relates GParameterMatcher
1094+template<class _tTraits, typename _fBind>
1095+YB_ATTR_nodiscard inline GParameterMatcher<_tTraits, _fBind>
1096+MakeParameterMatcher(TermNode::allocator_type a, _fBind bind)
1097+{
1098+ return GParameterMatcher<_tTraits, _fBind>(a, std::move(bind));
1099+}
1100+//! \relates GParameterMatcher
10181101 template<class _tTraits, typename _fBindTrailing, typename _fBindValue>
1019-YB_ATTR_nodiscard NPL_Impl_NPLA1_BindParameter_Inline inline
1020- GParameterMatcher<_tTraits, _fBindTrailing, _fBindValue>
1021-MakeParameterMatcher(_fBindTrailing bind_trailing_seq, _fBindValue bind_value)
1102+YB_ATTR_nodiscard inline
1103+ GParameterMatcher<_tTraits, GBinder<_fBindTrailing, _fBindValue>>
1104+MakeParameterMatcher(TermNode::allocator_type a,
1105+ _fBindTrailing bind_trailing_seq, _fBindValue bind_value)
10221106 {
1023- return GParameterMatcher<_tTraits, _fBindTrailing, _fBindValue>(
1024- std::move(bind_trailing_seq), std::move(bind_value));
1107+ return GParameterMatcher<_tTraits, GBinder<_fBindTrailing, _fBindValue>>(
1108+ a, std::move(bind_trailing_seq), std::move(bind_value));
10251109 }
1110+//@}
10261111
10271112 //! \since build 948
10281113 // XXX: 'YB_FLATTEN' cannot used with G++ 12.1.0 when %BindParameterImpl is also
@@ -1030,9 +1115,9 @@
10301115 // 'tree-cfg.c:6420'. This is no more efficient otherwise.
10311116 void
10321117 BindRawSymbol(const EnvironmentReference& r_env, string_view id,
1033- TermNode& b, TermTags o_tags, Environment& env, char sigil)
1118+ TermNode& o, TermTags o_tags, Environment& env, char sigil)
10341119 {
1035- BindParameterObject{r_env}(sigil, sigil == '&', o_tags, b,
1120+ BindParameterObject{r_env}(sigil, sigil == '&', o_tags, o,
10361121 [&](const TermNode& tm){
10371122 CopyTermTags(env.Bind(id, tm), tm);
10381123 }, [&](TermNode::Container&& c, ValueObject&& vo) -> TermNode&{
@@ -1041,18 +1126,101 @@
10411126 });
10421127 }
10431128
1044-//! \since build 920
1045-// XXX: 'YB_FLATTEN' does not work better than 'inline'.
1046-NPL_Impl_NPLA1_BindParameter_Inline inline void
1047-BindSymbolImpl(const EnvironmentReference& r_env, const TokenValue& n,
1048- TermNode& b, TermTags o_tags, Environment& env)
1129+
1130+//! \since build 949
1131+struct DefaultBinder final
10491132 {
1050- string_view id(n);
1051- const char sigil(ExtractSigil(id));
1133+ lref<Environment> EnvRef;
10521134
1053- BindRawSymbol(r_env, id, b, o_tags, env, sigil);
1054-}
1135+ inline
1136+ DefaultBinder(Environment& env)
1137+ : EnvRef(env)
1138+ {}
10551139
1140+ void
1141+ operator()(TermNode& o_nd, TNIter first, string_view id,
1142+ TermTags o_tags, const EnvironmentReference& r_env) const
1143+ {
1144+ YAssert(ystdex::begins_with(id, "."), "Invalid symbol found.");
1145+ id.remove_prefix(1);
1146+ // NOTE: Ignore the name of single '.'.
1147+ if(!id.empty())
1148+ {
1149+ const char sigil(ExtractSigil(id));
1150+
1151+ // NOTE: Ignore the name of single '.' followed by a sigil.
1152+ if(!id.empty())
1153+ {
1154+ const auto a(o_nd.get_allocator());
1155+ const auto last(o_nd.end());
1156+ TermNode::Container con(a);
1157+
1158+ // XXX: As %TermReference::IsMovable for non-temporary objects.
1159+ if(IsMovable(o_tags) || bool(o_tags & TermTags::Temporary))
1160+ {
1161+ if(sigil == char())
1162+ LiftSubtermsToReturn(o_nd);
1163+ // NOTE: This implements the copy elision of list members.
1164+ con.splice(con.end(), o_nd.GetContainerRef(), first, last);
1165+ MarkTemporaryTerm(EnvRef.get().Bind(id,
1166+ TermNode(std::move(con))), sigil);
1167+ }
1168+ else
1169+ {
1170+ // NOTE: Make a list as a copy of the sublist or as a list
1171+ // of references to the elements of the sublist, depending
1172+ // on the sigil.
1173+ for(; first != last; ++first)
1174+ BindParameterObject{r_env}(sigil, {}, o_tags,
1175+ NPL::Deref(first), [&](const TermNode& tm){
1176+ CopyTermTags(con.emplace_back(tm.GetContainer(),
1177+ tm.Value), tm);
1178+ }, [&](TermNode::Container&& c, ValueObject&& vo)
1179+ -> TermNode&{
1180+ con.emplace_back(std::move(c), std::move(vo));
1181+ return con.back();
1182+ });
1183+ if(sigil == '&')
1184+ {
1185+ // NOTE: Make a sublist reference whose referent is the
1186+ // copy constructed above. Irregular representation is
1187+ // constructed for the sublist reference.
1188+ // XXX: As %ReduceAsSubobjectReference in
1189+ // NPLA1Internals.
1190+ auto p_sub(YSLib::allocate_shared<TermNode>(a,
1191+ std::move(con)));
1192+ auto& sub(NPL::Deref(p_sub));
1193+
1194+ EnvRef.get().Bind(id,
1195+ TermNode(std::allocator_arg, a, [&]{
1196+ TermNode::Container tcon(a);
1197+
1198+ tcon.push_back(NPL::AsTermNodeTagged(a,
1199+ TermTags::Sticky, std::move(p_sub)));
1200+ return tcon;
1201+ }(), std::allocator_arg, a, TermReference(sub, r_env)));
1202+ }
1203+ else
1204+ MarkTemporaryTerm(EnvRef.get().Bind(id,
1205+ TermNode(std::move(con))), sigil);
1206+ }
1207+ }
1208+ }
1209+ }
1210+ void
1211+ operator()(string_view id, TermNode& o, TermTags o_tags,
1212+ const EnvironmentReference& r_env) const
1213+ {
1214+ // XXX: This shall be sequenced before the following call since %id can
1215+ // be modified.
1216+ const char sigil(ExtractSigil(id));
1217+
1218+ BindRawSymbol(r_env, id, o, o_tags, EnvRef, sigil);
1219+ }
1220+};
1221+
1222+
1223+//! \since build 917
10561224 template<class _tTraits>
10571225 // XXX: 'YB_FLATTEN' here may make the generated code a bit more efficient, but
10581226 // significantly slow in compiling without manually annotated 'always_inline',
@@ -1071,79 +1239,14 @@
10711239 {
10721240 auto& env(NPL::Deref(p_env));
10731241
1242+ // XXX: This should normally be true, but not yet relied on.
1243+// AssertMatchedAllocators(t, o);
10741244 // NOTE: No duplication check here. Symbols can be rebound.
10751245 // NOTE: The call is essentially same as %MatchParameter, with a bit better
10761246 // performance by avoiding %function instances.
1077- MakeParameterMatcher<_tTraits>([&](TermNode& o_tm, TNIter first,
1078- string_view id, TermTags o_tags, const EnvironmentReference& r_env){
1079- YAssert(ystdex::begins_with(id, "."), "Invalid symbol found.");
1080- id.remove_prefix(1);
1081- // NOTE: Ignore the name of single '.'.
1082- if(!id.empty())
1083- {
1084- const char sigil(ExtractSigil(id));
1085-
1086- // NOTE: Ignore the name of single '.' followed by a sigil.
1087- if(!id.empty())
1088- {
1089- const auto a(o_tm.get_allocator());
1090- const auto last(o_tm.end());
1091- TermNode::Container con(a);
1092-
1093- // XXX: As %TermReference::IsMovable for non-temporary objects.
1094- if((o_tags & (TermTags::Unique | TermTags::Nonmodifying))
1095- == TermTags::Unique || bool(o_tags & TermTags::Temporary))
1096- {
1097- if(sigil == char())
1098- LiftSubtermsToReturn(o_tm);
1099- // NOTE: This implements the copy elision of list members.
1100- con.splice(con.end(), o_tm.GetContainerRef(), first, last);
1101- MarkTemporaryTerm(env.Bind(id, TermNode(std::move(con))),
1102- sigil);
1103- }
1104- else
1105- {
1106- // NOTE: Make sublist reference.
1107- for(; first != last; ++first)
1108- BindParameterObject{r_env}(sigil, {}, o_tags,
1109- NPL::Deref(first), [&](const TermNode& tm){
1110- CopyTermTags(con.emplace_back(tm.GetContainer(),
1111- tm.Value), tm);
1112- }, [&](TermNode::Container&& c, ValueObject&& vo)
1113- -> TermNode&{
1114- con.emplace_back(std::move(c), std::move(vo));
1115- return con.back();
1116- });
1117- if(sigil == '&')
1118- {
1119- // NOTE: Irregular representation is constructed for the
1120- // sublist reference.
1121- // XXX: As %ReduceAsSubobjectReference in
1122- // NPLA1Internals.
1123- auto p_sub(YSLib::allocate_shared<TermNode>(a,
1124- std::move(con)));
1125- auto& sub(NPL::Deref(p_sub));
1126-
1127- env.Bind(id, TermNode(std::allocator_arg, a, [&]{
1128- TermNode::Container tcon(a);
1129-
1130- tcon.push_back(NPL::AsTermNodeTagged(a,
1131- TermTags::Sticky, std::move(p_sub)));
1132- return tcon;
1133- }(), std::allocator_arg, a, TermReference(sub, r_env)));
1134- }
1135- else
1136- MarkTemporaryTerm(env.Bind(id,
1137- TermNode(std::move(con))), sigil);
1138- }
1139- }
1140- }
1141- }, [&] NPL_Impl_NPLA1_BindParameter_InlineAnnotate((const TokenValue& n,
1142- TermNode& b, TermTags o_tags, const EnvironmentReference& r_env)){
1143- BindSymbolImpl(r_env, n, b, o_tags, env);
1144- })(t, o, TermTags::Temporary, p_env);
1247+ MakeParameterMatcher<_tTraits>(t.get_allocator(), DefaultBinder(env))(t, o,
1248+ TermTags::Temporary, p_env);
11451249 }
1146-//@}
11471250
11481251 } // unnamed namespace;
11491252
@@ -1188,7 +1291,6 @@
11881291 // XXX: The empty type is specialized enough without %trivial_swap.
11891292 Guard += GuardPasses::HandlerType(std::allocator_arg, get_allocator(),
11901293 [](TermNode&, ContextNode& ctx){
1191- // TODO: Support guarding for other states?
11921294 return A1::Guard(std::allocator_arg, ctx.get_allocator(),
11931295 in_place_type<ReductionGuard>, ctx);
11941296 });
@@ -1435,7 +1537,7 @@
14351537 if(IsList(nd))
14361538 {
14371539 // XXX: As %BindParameterObject.
1438- if(!p_ref || p_ref->IsMovable())
1540+ if(NPL::IsMovable(p_ref))
14391541 // NOTE: This allows %nd owned by %term.
14401542 term.MoveContainer(std::move(nd));
14411543 else
@@ -2084,7 +2186,8 @@
20842186 void
20852187 CheckParameterTree(const TermNode& term)
20862188 {
2087- MakeParameterValueMatcher([&](const TokenValue&) ynothrow{})(term);
2189+ MakeParameterValueMatcher(term.get_allocator(),
2190+ [&](const TokenValue&) ynothrow{})(term);
20882191 }
20892192
20902193 string
@@ -2108,7 +2211,10 @@
21082211 const EnvironmentReference&)> bind_value, TermTags o_tags,
21092212 const EnvironmentReference& r_env)
21102213 {
2111- MakeParameterMatcher<ParameterCheck>(std::move(bind_trailing_seq),
2214+ // XXX: See %BindParameterImpl.
2215+// AssertMatchedAllocators(t, o);
2216+ MakeParameterMatcher<ParameterCheck>(t.get_allocator(),
2217+ std::move(bind_trailing_seq),
21122218 std::move(bind_value))(t, o, o_tags, r_env);
21132219 }
21142220
@@ -2140,11 +2246,8 @@
21402246 {
21412247 // NOTE: As %BindSymbolImpl expecting the parameter tree as a single symbol
21422248 // term without trailing handling.
2143- auto& env(NPL::Deref(p_env));
2144-
2145- BindSymbolImpl(p_env, n, o, TermTags::Temporary, env);
2249+ DefaultBinder(NPL::Deref(p_env))(n, o, TermTags::Temporary, p_env);
21462250 }
2147-#undef NPL_Impl_NPLA1_BindParameter_InlineAttr
21482251 #undef NPL_Impl_NPLA1_BindParameter_Inline
21492252
21502253
@@ -2195,7 +2298,6 @@
21952298 });
21962299 passes += Pass(std::allocator_arg, a, ReduceFirst);
21972300 # endif
2198- // TODO: Insert more optional optimized lifted form evaluation passes.
21992301 // NOTE: This implies the %RegularizeTerm call when necessary.
22002302 // XXX: This should be the last of list pass for current TCO
22012303 // implementation, assumed by TCO action.
@@ -2272,7 +2374,7 @@
22722374 {
22732375 #if NPL_Impl_NPLA1_Enable_TCO
22742376 if(const auto p_act = act.target<TCOAction>())
2275- if(p_act->OperatorName.type() == type_id<TokenValue>())
2377+ if(IsTyped<TokenValue>(p_act->OperatorName))
22762378 return make_observer(&p_act->OperatorName);
22772379 #else
22782380 yunused(act);
diff -r eae6ca1f576c -r e1756f2eba1f YFramework/source/NPL/NPLA1Forms.cpp
--- a/YFramework/source/NPL/NPLA1Forms.cpp Thu Jun 30 07:43:19 2022 +0800
+++ b/YFramework/source/NPL/NPLA1Forms.cpp Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Forms.cpp
1212 \ingroup NPL
1313 \brief NPLA1 语法形式。
14-\version r27403
14+\version r27404
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 882
1717 \par 创建时间:
1818 2014-02-15 11:19:51 +0800
1919 \par 修改时间:
20- 2022-06-30 06:48 +0800
20+ 2022-07-05 05:49 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -368,7 +368,7 @@
368368 // XXX: This is served as the addtional static environment.
369369 auto& env(NPL::Deref(p_env));
370370
371- MakeParameterValueMatcher([&](const TokenValue& n){
371+ MakeParameterValueMatcher(t.get_allocator(), [&](const TokenValue& n){
372372 string_view id(n);
373373
374374 ExtractSigil(id);
diff -r eae6ca1f576c -r e1756f2eba1f YFramework/source/NPL/NPLA1Internals.h
--- a/YFramework/source/NPL/NPLA1Internals.h Thu Jun 30 07:43:19 2022 +0800
+++ b/YFramework/source/NPL/NPLA1Internals.h Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Internals.h
1212 \ingroup NPL
1313 \brief NPLA1 内部接口。
14-\version r22387
14+\version r22439
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 882
1717 \par 创建时间:
1818 2020-02-15 13:20:08 +0800
1919 \par 修改时间:
20- 2022-06-16 01:42 +0800
20+ 2022-07-12 12:50 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 非公开模块名称:
@@ -1295,10 +1295,11 @@
12951295 YB_NORETURN void
12961296 ThrowNestedParameterTreeMismatch();
12971297
1298-//! \since build 917
1298+//! \since build 949
12991299 YB_NORETURN inline PDefH(void, ThrowFormalParameterTypeError,
1300- const TermNode& term, bool has_ref)
1301- ImplExpr(ThrowTypeErrorForInvalidType(type_id<TokenValue>(), term, has_ref))
1300+ const TermNode& term, bool has_ref, size_t n_skip = 0)
1301+ ImplExpr(
1302+ ThrowTypeErrorForInvalidType(type_id<TokenValue>(), term, has_ref, n_skip))
13021303
13031304 //! \since build 917
13041305 char
@@ -1317,38 +1318,44 @@
13171318
13181319
13191320 //! \since build 917
1320-//@{
13211321 template<typename _fBindValue>
13221322 class GParameterValueMatcher final
13231323 {
1324+ //! \since build 949
1325+ using MEntry = pair<TNCIter, TNCIter>;
1326+
13241327 public:
13251328 _fBindValue BindValue;
13261329
13271330 private:
1328- mutable Action act{};
1331+ //! \since build 949
1332+ mutable YSLib::stack<MEntry, vector<MEntry>> remained;
13291333
13301334 public:
1331- //! \since build 939
1332- template<class _tParam, yimpl(typename
1333- = ystdex::exclude_self_t<GParameterValueMatcher, _tParam>)>
1335+ //! \since build 949
1336+ template<typename... _tParams>
13341337 inline
1335- GParameterValueMatcher(_tParam&& arg)
1336- : BindValue(yforward(arg))
1338+ GParameterValueMatcher(TermNode::allocator_type a, _tParams&&... args)
1339+ : BindValue(yforward(args)...), remained(a)
13371340 {}
13381341
13391342 void
13401343 operator()(const TermNode& t) const
13411344 {
1345+ // As %ParameterCheck::WrapCall in NPLA1.cpp.
13421346 try
13431347 {
1348+ Match(t, {});
13441349 // NOTE: This is a trampoline to eliminate the call depth
13451350 // limitation.
1346- Match(t, {});
1347- while(act)
1351+ while(!remained.empty())
13481352 {
1349- const auto a(std::move(act));
1353+ auto& e(remained.top());
13501354
1351- a();
1355+ if(e.first == e.second)
1356+ remained.pop();
1357+ else
1358+ Match(NPL::Deref(e.first++), {});
13521359 }
13531360 }
13541361 CatchExpr(ParameterMismatch&, throw)
@@ -1356,49 +1363,52 @@
13561363 }
13571364
13581365 private:
1359- YB_FLATTEN void
1360- Match(const TermNode& t, bool t_has_ref) const
1366+ void
1367+ Match(const TermNode& t, bool indirect) const
13611368 {
13621369 if(IsList(t))
1363- {
1364- if(IsBranch(t))
1365- MatchSubterms(t.begin(), t.end());
1366- }
1370+ MatchList(t);
13671371 else if(const auto p_t = TryAccessLeafAtom<const TermReference>(t))
13681372 {
13691373 auto& nd(p_t->get());
13701374
1371- ystdex::update_thunk(act, [&]{
1372- Match(nd, true);
1373- });
1375+ if(IsList(nd))
1376+ MatchList(nd);
1377+ else
1378+ MatchNonList(nd, true);
13741379 }
13751380 else
1376- HandleOrIgnore(std::ref(BindValue), t, t_has_ref);
1381+ MatchNonList(t, indirect);
13771382 }
13781383
1384+ //! \since build 949
13791385 void
1380- MatchSubterms(TNCIter i, TNCIter last) const
1386+ MatchList(const TermNode& t) const
13811387 {
1382- if(i != last)
1383- {
1384- // XXX: Use explicit captures here to ensure ISO C++20
1385- // compatibility.
1386- ystdex::update_thunk(act, [this, i, last]{
1387- return MatchSubterms(std::next(i), last);
1388- });
1389- Match(NPL::Deref(i), {});
1390- }
1388+ YAssert(IsList(t), "Invalid term found.");
1389+ if(IsBranch(t))
1390+ remained.emplace(t.begin(), t.end());
1391+ }
1392+
1393+ //! \since build 949
1394+ void
1395+ MatchNonList(const TermNode& t, bool indirect) const
1396+ {
1397+ YAssert(!IsList(t), "Invalid term found.");
1398+ HandleOrIgnore(std::ref(BindValue), t, indirect);
13911399 }
13921400 };
13931401
1394-//! \relates GParameterValueMatcher
1402+/*!
1403+\relates GParameterValueMatcher
1404+\since build 949
1405+*/
13951406 template<typename _fBindValue>
13961407 YB_ATTR_nodiscard inline GParameterValueMatcher<_fBindValue>
1397-MakeParameterValueMatcher(_fBindValue bind_value)
1408+MakeParameterValueMatcher(TermNode::allocator_type a, _fBindValue bind_value)
13981409 {
1399- return GParameterValueMatcher<_fBindValue>(std::move(bind_value));
1410+ return GParameterValueMatcher<_fBindValue>(a, std::move(bind_value));
14001411 }
1401-//@}
14021412
14031413
14041414 // NOTE: This implements binding tags of temporary objects, to allow distinguish
diff -r eae6ca1f576c -r e1756f2eba1f doc/ChangeLog.V0.9.txt
--- a/doc/ChangeLog.V0.9.txt Thu Jun 30 07:43:19 2022 +0800
+++ b/doc/ChangeLog.V0.9.txt Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file ChangeLog.V0.9.txt
1212 \ingroup Documentation
1313 \brief 版本更新历史记录 - V0.9 。
14-\version r9866
14+\version r10004
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 800
1717 \par 创建时间:
1818 2020-10-12 17:19:23 +0800
1919 \par 修改时间:
20- 2022-06-30 07:11 +0800
20+ 2022-07-12 18:45 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -32,6 +32,145 @@
3232
3333 $now
3434 (
35+ + DD "'\pre' commmand" @ "Doxygen comment" @ "member function \
36+ %memory_resource::deallocate" @ %YBase.YStandardEx.MemoryResource,
37+ / %YFramework.NPL $=
38+ (
39+ / %NPLA $=
40+ (
41+ (
42+ + 2 "function templates %AssertMatchedAllocators" >> %SContext
43+ $dep_to "lifting allocator assertions";
44+ / $lib $design "adjusted assertion messages" @ "remained overloads"
45+ @ "function template overloads %AssertMatchedAllocators"
46+ ),
47+ (
48+ + "function template %ResolveBy";
49+ + "function template %ResolveSuffix",
50+ / DLDI "simplified function template %ResolveTerm" ^ "%ResolveBy"
51+ ),
52+ + DLI 'inline' @ "function template %ResolveTerm",
53+ // This is a bit significant to the generate code performance.
54+ / "added last parameter with default value '0' to support skipping \
55+ prefix subterms"
56+ @ "functions %(TermToString; TermToStringWithReferenceMark)",
57+ / $re_add(b928) "simplified member function \
58+ %Environment::CheckParent" ^ "%IsTyped",
59+ / DLDI "declaration order" @ "function %LiftPrefixToReturn",
60+ (
61+ + "function %LiftSuffixToReturn";
62+ / DLDI "simplified function %LiftElementsToReturn as inlining \
63+ function"
64+ ),
65+ (
66+ + "function %LiftPrefixToReturn with 2 parameters";
67+ / DLDI "simplified %LiftPrefixToReturn#1"
68+ )
69+ ),
70+ / %SContext $=
71+ (
72+ / DLDI "declaration order" @ "destructor and move %operator="
73+ @ "class %TermNode",
74+ + "2 function template overloads %AssertMatchedAllocators"
75+ $dep_from "lifting allocator assertions"
76+ ),
77+ / %Exception $=
78+ (
79+ / "added last parameter with default value '0' to support skipping \
80+ prefix subterms" @ "functions with 'const TermNode&' parameter"
81+ $effective @ "functions %(ThrowInsufficientTermsError, \
82+ ThrowListTypeErrorForAtom, ThrowListTypeErrorForInvalidType, \
83+ ThrowListTypeErrorForNonList, ThrowTypeErrorForInvalidType)"
84+ $impl $dep_from ("%TermToStringWithReferenceMark" @ %NPLA),
85+ (
86+ + "function %ThrowTypeErrorForInvalidType with 2 'const char*' \
87+ parameters";
88+ / DLDI "simplified function %ThrowTypeErrorForInvalidType#2"
89+ )
90+ ),
91+ / "added last parameter with default value '0' to support skipping \
92+ prefix subterms" @ "functions %ThrowFormalParameterTypeError"
93+ @ %NPLA1Internals
94+ $impl $dep_from ("%TermToStringWithReferenceMark" @ %NPLA),
95+ / %NPLA1Internals $=
96+ (
97+ / DLI "optimized internal matching thunked calls"
98+ @ "class template %GParameterValueMatcher"
99+ ^ "%(YSLib::stack, vector)" ~ "%Action",
100+ // As %(MatchParameter, BindParameter, BindParamaterWellFormed).
101+ / "enabled allocators" (@ "class template %GParameterValueMatcher";
102+ "function template %MakeParameterValueMatcher")
103+ ),
104+ / %NPLA1 $=
105+ (
106+ / $re_add(b928) DLDI "simplified function %QueryTailOperatorName"
107+ @ 'NPL_Impl_NPLA1_Enable_TCO' ^ "%IsTyped",
108+ / @ "functions %(MatchParameter, BindParameter, \
109+ BindParamaterWellFormed)" $=
110+ (
111+ * "minor internal comments on \
112+ %NPL_Impl_NPLA1_BindParameter_ExpandLevel" $since b948,
113+ / DLI "enabled allocators for parameter matcher",
114+ / DLI "refactored internal calls",
115+ / DLI "optimized internal matching thunked calls"
116+ ^ "%(YSLib::stack, vector)" ~ "%Action",
117+ // Without %vector, it is less efficient than the previous \
118+ implementation.
119+ * "unexpected support of more than 1 level of indirection on \
120+ uncollapsed references" @ "parameter tree"
121+ $mismatch(Documentation.NPL)
122+ $orig (@ "function %MatchParameter" $since b855),
123+ // By adjustment of inlining with one less 'YB_FLATTEN', \
124+ the fix makes the generated code a bit efficient, but \
125+ the compilation time still increses due to more split \
126+ function templates.
127+ * "missing reference mark" @ "exception message for last \
128+ parameter subterm check failure" $since b917
129+ ^ "%ResolveTerm" ~ "%ResolveReference",
130+ // This should respect not only to the term being \
131+ resolved, but also to the subterm itself.
132+ / "trailing ellipsis handling" $=
133+ (
134+ * "overly restrictive assertion wrongly against references \
135+ to the ellipsis sequence token subterm on the matching \
136+ after the check" $since b858,
137+ // The original code was added in function \
138+ %MatchParameter since b780. This only supports to \
139+ resolve references to symbols since b858.
140+ $= (/ $impl ^ "%(IsTyped, ReferenceTerm)");
141+ / $dev $lib @ "initial check" $=
142+ (
143+ / "supported subterms having irregular representation \
144+ along with the %TokenValue value"
145+ // This is not visible since no irregular \
146+ representation of symbol are yet supported. \
147+ However, this is more compatible to any future \
148+ handling on these currently unused \
149+ representatios.
150+ $= (/ $impl ^ "%IsAtom" ~ "%IsLeaf");
151+ // The use of %IsLeaf was also introduced in \
152+ b858.
153+ / $impl "simplified" ^ "%TryAccessLeaf"
154+ ~ "%TryAccessLeafAtom"
155+ )
156+ ),
157+ / DLDI "simplified move check" ^ "%IsMovable",
158+ ),
159+ / DLDI "simplified move check" @ "function %ReduceToReferenceList"
160+ ^ "%NPL::IsMovable"
161+ ),
162+ / $forced "enabled allocator for parameter value matcher" $efficient
163+ @ ("function %CheckParameterTree" @ %NPLA1,
164+ "function %Forms::DefineWithRecursion" @ %NPLA1Forms)
165+ $dep_from ("%MakeParameterValueMatcher" @ %NPLA1Internals),
166+ // As %(MatchParameter, BindParameter, BindParamaterWellFormed).
167+ ),
168+ + "message after linking finished" @ "member function %BuildContext::Build"
169+ @ %Tools.SHBuild.Main
170+),
171+
172+b948
173+(
35174 / $lib $impl %YBase.YStandardEx.MemoryResource $=
36175 (
37176 / $re_ex(b945) "disabled the attribute" @ (!'defined(NDEBUG)',
diff -r eae6ca1f576c -r e1756f2eba1f doc/CommonRules.txt
--- a/doc/CommonRules.txt Thu Jun 30 07:43:19 2022 +0800
+++ b/doc/CommonRules.txt Tue Jul 12 18:45:26 2022 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2009-2021 FrankHB.
2+ © 2009-2022 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 CommonRules.txt
1212 \ingroup Documentation
1313 \brief 公用规则指定和说明。
14-\version r4738
14+\version r4774
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 282
1717 \par 创建时间:
1818 2012-02-03 16:44:56 +0800
1919 \par 修改时间:
20- 2021-07-21 18:18 +0800
20+ 2022-07-09 18:18 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -76,7 +76,7 @@
7676
7777 @0.3 可读性(readability) :
7878 一般意义的可读性包含主观因素,即在一定的背景下,特定读者对特定书面文本材料的理解能力可能不同。
79-为便于规范描述, [Documentation] 中的文档约定忽略主观可读性,由理想情况下的某个读者(参考读者)对不同材料的认识界定使之成为文本材料决定的性质。
79+为便于规范描述,[Documentation] 中的文档约定忽略主观可读性,由理想情况下的某个读者(参考读者)对不同材料的认识界定使之成为文本材料决定的性质。
8080
8181 @1 绪论(Introduction) :
8282
@@ -90,7 +90,8 @@
9090 — ISO/IEC 14882:2003, Information technology — Programming languages — C++
9191 — ISO/IEC 14882:2011, Information technology — Programming languages — C++
9292 — ISO/IEC 14882:2014, Information technology — Programming languages — C++
93-— ISO 80000-2:2009, Quantities and units — Part 2: Mathematical signs and symbols to be usedin the natural sciences and technology
93+— ISO 80000-2:2009, Quantities and units
94+ — Part 2: Mathematical signs and symbols to be usedin the natural sciences and technology
9495 — 1003.1-2003 - IEEE Standard for IEEE Information Technology - Portable Operating System Interface (POSIX(R))
9596 — 1003.1-2008 - IEEE Standard for IEEE Information Technology - Portable Operating System Interface (POSIX(R))
9697 — The Unicode Standard — Version 6.0 – Core Specification
@@ -98,14 +99,14 @@
9899 http://gcc.gnu.org/codingconventions.html
99100
100101 @1.1.1 引用简写:
101-ISO C : ISO/IEC 9899 。
102-ISO C11 : ISO/IEC 9899:2011 。
103-ISO C17 : ISO/IEC 9899:2018 。
104-ISO C++ : ISO/IEC 14882 。
105-ISO C++03 : ISO/IEC 14882:2003 。
106-ISO C++11 : ISO/IEC 14882:2011 。
107-ISO C++14 : ISO/IEC 14882:2014 。
108-POSIX.1 : ISO/IEC 9945:2003 (引用内容同 IEEE Std 1003.1-2003 )。
102+ISO C :ISO/IEC 9899 。
103+ISO C11 :ISO/IEC 9899:2011 。
104+ISO C17 :ISO/IEC 9899:2018 。
105+ISO C++ :ISO/IEC 14882 。
106+ISO C++03 :ISO/IEC 14882:2003 。
107+ISO C++11 :ISO/IEC 14882:2011 。
108+ISO C++14 :ISO/IEC 14882:2014 。
109+POSIX.1 :ISO/IEC 9945:2003(引用内容同 IEEE Std 1003.1-2003 )。
109110 POSIX.1-2008 :IEEE Std 1003.1-2008 。
110111
111112 @1.2 术语:
@@ -181,7 +182,7 @@
181182 ……
182183
183184 @2.1.6 实现模式/代码模式/惯用法(Idioms) :
184-语言相关。举例: C++ 的 pImpl 、 copy & swap 。
185+语言相关。举例: C++ 的 pImpl 、copy & swap 。
185186
186187 @2.1.7 其它通用设计要点:
187188
@@ -771,7 +772,7 @@
771772 使用 C++ 的 pImpl 惯用法(@2.1.6) 辅助封装并分离接口与实现(@3.5.1) 。
772773
773774 @3.5.2.1 封装范围限制实例:
774-对以类实例的整体为客体的操作(如 Activate 方法;注意无参数的 Clear 、 Reset 等方法不在此列),除了和对象生存期相关的行为(例如构造、析构和初始化),在翻译单元及命名空间内而不是类的内部封装。
775+对以类实例的整体为客体的操作(如 Activate 方法;注意无参数的 Clear 、Reset 等方法不在此列),除了和对象生存期相关的行为(例如构造、析构和初始化),在翻译单元及命名空间内而不是类的内部封装。
775776
776777 @3.5.3 基于类的(class-based) 面向对象设计:
777778 使用合适的语言特性限制类外对类成员或基类的访问:
@@ -858,7 +859,7 @@
858859 迁移困难;
859860 平台依赖。
860861 限制使用的范围包括:
861-公开的特定厂商的规范,如 Microsoft COM/DCOM 、 Mozilla XPCOM 、CATIA CAA 等;
862+公开的特定厂商的规范,如 Microsoft COM/DCOM 、Mozilla XPCOM 、CATIA CAA 等;
862863 公开的厂商中立的规范,如 CORBA ;
863864 其它基于特定应用类似非公开机制。
864865 合理使用特定语言的 FFI(foreign function interface) 。
@@ -1152,12 +1153,12 @@
11521153 *RRef :右值引用(类型为非 const 右值引用类型)
11531154 *To :操作名称起始,第一参数指定目标的函数。
11541155 Be*With :* 为操作名称过去分词(被动语态);按参数完成特定操作达成特定状态。
1155-Get* : * 不以 Of 结尾,为目标名称;取特定目标的获取器(@3.12.4) ;通常是 const 非静态成员函数或模板。
1156-Get*Of : 同 Get* ,但通常为非成员函数或模板,第一参数为目标操作对象的 const 引用。
1157-Is* : * 为目标名称;谓词:计算目标状态,返回类型为 bool 的结果。
1158-Set* : * 不以 Of 结尾,为目标名称;修改特定目标的设置器(@3.12.4) ;通常是非 const 非静态成员函数或模板。
1159-Set*Of : 同 Set* ,但通常为非成员函数或模板,第一参数为目标操作对象的非 const 引用。
1160-On* : * 为描述事件及事件响应目标的名称;事件处理器。
1156+Get* :* 不以 Of 结尾,为目标名称;取特定目标的获取器(@3.12.4) ;通常是 const 非静态成员函数或模板。
1157+Get*Of :同 Get* ,但通常为非成员函数或模板,第一参数为目标操作对象的 const 引用。
1158+Is* :* 为目标名称;谓词:计算目标状态,返回类型为 bool 的结果。
1159+Set* :* 不以 Of 结尾,为目标名称;修改特定目标的设置器(@3.12.4) ;通常是非 const 非静态成员函数或模板。
1160+Set*Of :同 Set* ,但通常为非成员函数或模板,第一参数为目标操作对象的非 const 引用。
1161+On* :* 为描述事件及事件响应目标的名称;事件处理器。
11611162 Check* :含义同 check_*(@4.1.7.4) 。
11621163 Throw* :含义同 throw_*(@4.1.7.4) 。
11631164 Try* :含义同 try_*(@4.1.7.3) 。
@@ -1194,7 +1195,7 @@
11941195 cend :用于 range based for 的终止只读迭代器。
11951196 包括以下前缀:
11961197 try_* :尝试进行状态检查并非阻塞地调用,失败时可能对非预期状态进行原地处理,可捕获并重新抛出异常,或抛出其它异常(包括嵌套异常)。
1197-类似 ISO C++ 标准库的 try_lock 、 try_emplace 和 WG21 P0042 的 try_recover 等;可对应没有前缀的接口抛出异常表示这些错误,此时带前缀 try_* 的版本在返回值中应蕴含操作是否成功。
1198+类似 ISO C++ 标准库的 try_lock 、try_emplace 和 WG21 P0042 的 try_recover 等;可对应没有前缀的接口抛出异常表示这些错误,此时带前缀 try_* 的版本在返回值中应蕴含操作是否成功。
11981199
11991200 @4.1.7.4 其它惯用命名:
12001201 包括以下前缀:
@@ -1329,12 +1330,12 @@
13291330 @5.2.4 括号:
13301331 除了典型的防止被认为错误(如 if 条件中的赋值表达式或不同层次连续嵌套的 if 和 else 子句)避免警告,不使用不改变语义的冗余的小括号或大括号。
13311332 注意小括号可避免使用 ADL ([Documentation::LanguageConvention @@5.2.4.1]) 。
1332-alignas 、 alignof 和 sizeof 表达式使用最外层有括号的形式。
1333+alignas 、alignof 和 sizeof 表达式使用最外层有括号的形式。
13331334 注意 decltype 表达式的子表达式的括号的语义。
13341335 return 语句使用无最外层括号形式。
13351336
13361337 @5.2.5 填充空白符:
1337-作为后缀表达式的组成部分或控制语句紧接关键字后的组成部分的 ( 和左边的标识符(包括关键字,如 catch 、 if 、 for 和 while )之间不插入额外的空白符。
1338+作为后缀表达式的组成部分或控制语句紧接关键字后的组成部分的 ( 和左边的标识符(包括关键字,如 catch 、if 、for 和 while )之间不插入额外的空白符。
13381339 不使用针对标点的后缀空白符。
13391340
13401341 @5.3 行首空白符:
@@ -1407,7 +1408,7 @@
14071408 对使用内建 operator, 的情况,除语法意义上的不同语句可以合并为同一语句这一变化外,仅作为对代码阅读者的提示。
14081409
14091410 @5.4.5 括号:
1410-括号 { 、 } 、 [ 、] 、 ( 、 ) 标识声明符以上层次的语法(包括领域专门内嵌语言(domain-specific embedded language))结构(如函数体)时应独立占一行,除了以下例外:
1411+括号 { 、} 、[ 、] 、( 、) 标识声明符以上层次的语法(包括领域专门内嵌语言(domain-specific embedded language))结构(如函数体)时应独立占一行,除了以下例外:
14111412 构成空序列时连写(如 {} 表示空函数体)独立占一行,除非表示此处代码未完成(需要继续扩展);
14121413 lambda 表达式中起始的 { 前不换行。
14131414 除此之外,列表分行以括号右边优先于括号左边,即左括号在分行后作为末尾的记号。例如函数参数列表或后缀调用表达式太长时,可在 ( 之后而不是之前分行;模板参数列表太长时,可在 < 之后而不是之前分行。
@@ -1456,7 +1457,7 @@
14561457 除临时用途(如测试)外,块内不使用函数声明,因此空行可有效分隔和标识 using /类型/变量声明和其它代码。
14571458 必要时多个声明可以被拆分为若干不连续的部分,之间允许存在其它代码。
14581459 除非特殊需要,连续的声明的顺序依次为 using 声明、类型声明和变量声明。
1459-因逻辑原因标识分隔语句块的,应考虑改用(未命名命名空间的、 inline 的)函数 、 lambda 表达式或其它实体形式包装,而不是插入空行进行分隔。另见 @5.8.1 。
1460+因逻辑原因标识分隔语句块的,应考虑改用(未命名命名空间的、inline 的)函数 、lambda 表达式或其它实体形式包装,而不是插入空行进行分隔。另见 @5.8.1 。
14601461
14611462 @5.6 分词:
14621463 除 @5.3 和 @5.4 外,仅使用空格分隔记号。
@@ -1527,7 +1528,7 @@
15271528 按 @5.8.2.4 处理。
15281529
15291530 @5.8.2.4 非复合语句缩进、分行和空行策略:
1530-试验符合 @5.3 、 @5.4.1 、 @5.4.2 和 @5.6 的规则约束下最紧凑(尽可能少地使用空白符)的代码格式,记录代码行数;
1531+试验符合 @5.3 、@5.4.1 、@5.4.2 和 @5.6 的规则约束下最紧凑(尽可能少地使用空白符)的代码格式,记录代码行数;
15311532 在满足以上规则和结果行数的约束下增加 @5.4 的其余约束,若存在解则算法结束;
15321533 否则结果行数增加 1 ,继续验证上述增加 @5.4 的其余约束的解是否存在。
15331534 显然不限制结果行数上限时解存在。
@@ -1568,7 +1569,7 @@
15681569 分为两个区段,以静态数据成员、非静态数据成员的顺序排列。
15691570
15701571 @5.9.5.2.1 静态数据成员
1571-按 cv-qualifier 和 constepxr 分为以下区段,以 constexpr 、 const 、 const volatile 、 无限定 、 volatile的顺序排列。
1572+按 cv-qualifier 和 constepxr 分为以下区段,以 constexpr 、const 、const volatile 、无限定 、volatile的顺序排列。
15721573
15731574 @5.9.5.3 其它成员或非成员:
15741575
@@ -1586,7 +1587,7 @@
15861587 对同一个名称的成员,成员重载先于非成员重载。
15871588 对仅有参数列表不同的重载函数或重载函数模板,以从左至右的前缀顺序排列。
15881589 涉及特殊成员函数的重载以默认构造函数、其它构造函数/构造模板、复制构造函数、转移构造函数、析构函数、其它赋值操作符函数/模板、复制赋值操作符函数、转移复制操作符函数的顺序声明,优先于以下子区段规则。
1589-仅限定符不同或仅修饰相同位置参数中的限定符构成重载的区段:首先按 cv-qualifier 以无限定、const 、volatile 、const volatile 排列,其次按 ref-qualifier 以无限定、 & 、 && 排列;但命名空间作用域中若在接口语义或实现中存在声明(调用)依赖则允许使用其它顺序(但应尽少改变)。
1590+仅限定符不同或仅修饰相同位置参数中的限定符构成重载的区段:首先按 cv-qualifier 以无限定、const 、volatile 、const volatile 排列,其次按 ref-qualifier 以无限定、& 、&& 排列;但命名空间作用域中若在接口语义或实现中存在声明(调用)依赖则允许使用其它顺序(但应尽少改变)。
15901591 类型和所有的限定符一致的成员函数先于成员函数模板。
15911592
15921593 @5.9.5.3.4 伪重载区段:
@@ -1629,18 +1630,18 @@
16291630 和复制赋值操作符的第一参数相同的其它转移操作符;
16301631 (对类模板)和复制赋值操作符仅有第一个函数参数类型的模板参数不同的简单赋值操作符模板;
16311632 第一参数为初值符列表的赋值操作符;
1632-组合赋值操作符(以 += 、 -= 、 *= 、 /= 、 %= 、 <<= 、 >>= 、 &= 、 |= 的顺序);
1633-一元操作符(以 ! 、 ~ 、 + 、 - 、 & 、 * 的顺序);
1633+组合赋值操作符(以 += 、-= 、*= 、/= 、%= 、<<= 、>>= 、&= 、|= 的顺序);
1634+一元操作符(以 ! 、~ 、+ 、- 、& 、* 的顺序);
16341635 重载 operator-> ;
16351636 重载 operator->* ;
16361637 自增/自减(以前缀自增、后缀自增、前缀自减、后缀自减的顺序);
16371638 重载 operator[] ;
1638-关系操作符(以 == 、 != 、 < 、 <= 、 > 、 >= 的顺序);
1639-算术操作符(以 + 、 - 、 * 、 / 、 % )的顺序;
1640-二元位操作符(以 << 、 >> 、 & 、 | 、 ^ 的顺序);
1641-逻辑操作符(以 && 、 || )的顺序;
1639+关系操作符(以 == 、!= 、< 、<= 、> 、>= 的顺序);
1640+算术操作符(以 + 、- 、* 、/ 、% )的顺序;
1641+二元位操作符(以 << 、>> 、& 、| 、^ 的顺序);
1642+逻辑操作符(以 && 、|| )的顺序;
16421643 重载 operator() ;
1643-分配/去配操作符(以 new 、new[] 、 delete 、 delete[] 的顺序);
1644+分配/去配操作符(以 new 、new[] 、delete 、delete[] 的顺序);
16441645 转换操作符(以整数类型、枚举类型、其它基本类型、类类型顺序;整数类型之间以转换阶(conversion rank) 从小到大的顺序排列);
16451646 其它函数或函数模板。
16461647
diff -r eae6ca1f576c -r e1756f2eba1f doc/LanguageConvention.txt
--- a/doc/LanguageConvention.txt Thu Jun 30 07:43:19 2022 +0800
+++ b/doc/LanguageConvention.txt Tue Jul 12 18:45:26 2022 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2009-2021 FrankHB.
2+ © 2009-2022 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 LanguageConvention.txt
1212 \ingroup Documentation
1313 \brief 语言使用规约。
14-\version r2580
14+\version r2634
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 543
1717 \par 创建时间:
1818 2012-10-08 05:13:05 +0800
1919 \par 修改时间:
20- 2021-12-21 12:33 +0800
20+ 2022-07-09 10:14 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -211,12 +211,15 @@
211211 非类类型对象或最终派生类(most derived class) 类型的对象是最终派生对象(most derived object) 。
212212 除了位域(bit-field),最终派生对象保证占用连续的非零存储单元。一般第一个单元对应的即表示对象存储的地址。
213213 不同的对象占据不同的存储单元的完整对象具有不同的地址。
214+注意标准布局(standard-layout) 的对象具有附加保证,其它对象的布局一般是互操作使用的实现细节。
215+特别地,ISO C++11 要求标准布局的类能通过 reinterpret_cast 转换得到指向第一个非静态数据成员的指针,这实质要求标准布局的空基类不占用空间,即空基类优化(EBO, empty base optimization)
216+ISO C++20 的规则(自 [WG21 P840R2] )进一步明确了这些基类的大小为 0 ,但没有改变要求。
214217
215218 @5.1.11 程序执行:
216219 ISO C++ 使用抽象机描述程序语义,但不要求实际实现与之完全一致。
217220
218221 @5.1.11.1 可观察行为:
219-至少遵循的语义包括对 volatile 对象的访问、程序终止状态和可被动态观察的输入和输出对应的行为,称为可观察行为 (observable behavior) 。
222+至少遵循的语义包括对 volatile 对象的访问、程序终止状态和可被动态观察的输入和输出对应的行为,称为可观察行为(observable behavior) 。
220223
221224 @5.1.11.2 as-if 规则和程序优化:
222225 实现的语义遵循 as-if 规则:不影响可观察行为的程序行为可以被实现改变。这基本和 ISO C 一致。
@@ -352,7 +355,7 @@
352355 @5.2.10 值类别(value category) :
353356 注意值类别是表达式的属性。
354357 注意区分 lvalue 、xvalue 和 prvalue 。
355-注意右值引用为 xvalue 的条件: xvalue 是右值对象引用。
358+注意右值引用为 xvalue 的条件:xvalue 是右值对象引用。
356359 注意函数表达式的值类别。
357360
358361 @5.2.10.1 左值访问限制:
@@ -406,27 +409,44 @@
406409 注意适当使用 begin 和 end 重载函数配合 range-based for 的使用。
407410
408411 @5.3.3 宏定义:
409-有源代码兼容性和可读性良好替代方案的情况下,尽可能不使用宏。
410-在不能确定特定宏未被定义时,为避免重复定义,在定义前应使用 #undef 或条件包含判断宏是否需要定义。
412+存在源代码兼容性和可读性良好替代方案时,尽可能不使用宏。
411413 注意保留名称规则(@6.1.2) 也适用于宏。
412414
413415 @5.3.3.1 常量:
414416 尽可能使用 constexpr 关键字而不是宏定义常量。
415417 尽可能使用 const 关键字定义只读对象。
416-另见 @5.5.4.2 。
418+关于预处理中使用的常量,另见条件包含(@5.3.4) 。
419+另见类作用域(@5.5.4.2) 。
417420
418-@5.3.3.2
421+@5.3.3.2 实现特性宏:
419422 某些宏影响实现特性的宏,如 __STRICT_ANSI__ 或 _POSIX_SOURCE 。注意应在标准库头前包含之前决定唯一定义。
420423
424+@5.3.3.3 取消定义:
425+在不能确定特定宏未被定义时,为避免重复定义,在定义前应使用 #undef 或条件包含判断宏是否需要定义。
426+在源代码中引入的不对其它预处理翻译单元可见的宏定义应在不使用时以 #undef 指令取消定义。
427+
421428 @5.3.4 条件包含:
422-使用条件包含指令控制文件包含,而不是编译器相关的非标准预处理指令(例如 #pragma_once 和 #include_next )。
429+条件包含可支持不同条件下的实现方式,这可适合以下情形:
430+支持可变的配置,包括平台配置和构建选项;
431+相同配置下选取单一的实现,但同时展示其它实现便于对比。
423432 除此之外,尽可能不使用条件编译。
424-注意区分 #if 和 #ifdef 。
433+若需控制不同配置下的包含,使用条件包含指令控制文件包含,而不是编译器相关的非标准预处理指令(例如 #pragma_once 和 #include_next )。
434+应注意区分 #if 和 #ifdef 。可以假定 #if 对未定义的宏适用。
435+**注释** 这明确不需要兼容 GCC 选项 -Werror=undef 。
436+如有可能,需要表达逻辑真和逻辑假时,在 #if 、#ifdef 和 #ifndef 的常量中使用 true 和 false 代替 1 和 0 。
437+**原理** 这具有更好的可读性。
438+**注释** 和 C 兼容的实现宏一般可能仍使用 0 或 1 ,如 __has_include 的默认值使用 0 。
439+使用 #if true 或 #if false 标记总是启用或不启用的程序片段。
440+若需要展示不同的实现但没有确定可在预处理器中指定的条件,也可使用 #if true 和 #if false ,同时在 #else 分支中指出其它替代的备选实现。
441+一般地,条件包含中,第一个分支应使用当前启用的最特化(如有)的实现,之后的分支使用其它更一般的条件或展示替代的备选实现。
442+**注释** 展示不同的实现时,若没有更具体的包含条件,这种方式即一般首先为 #if true 。
443+但是,为注释的可读性(顺序性和连贯性),也可先使用 #if false ,再在其它分支中描述和先前不同的实现方式。
425444
426-@5.3.5
445+@5.3.5 诊断:
427446 可以使用 #error 和 #line ,但没有确定环境支持时,仅使用基本字符集的字符,以免出现不符合的预期文本。
428447 对 #error 提示用户的文本内容,一般尽可能使用字符串字面量而不是直接的记号序列,以完整地表达含义且便于搜索。
429448 在确定环境支持时,可使用 #warning 。
449+**注释** 此处的环境支持通常由平台配置决定。
430450
431451 @5.3.6 #include 指令:
432452 使用 <头名> 或 <相对路径> 表示外部库依赖项(仅允许头), "文件名" 或 "相对路径" 表示内部库依赖向(特殊情况下允许源文件,但需在文档中说明目的)。
@@ -574,11 +594,19 @@
574594
575595 @5.5.6.1 一般语法:
576596 在必要时使用 inline 关键字。
577-类定义内不使用冗余的 inline (类模板的成员不适用)。
597+类定义内不使用冗余的 inline (成员模板函数不适用)。
578598 注意属性应在指示符之前出现。
579599 使用 static inline 而不是 inline static ,使用 thread_local inline 而不是 inline thread_local ;原因参见 @5.5.4.5 。
580600 保持和 static inline 风格一致性,使用 explicit inline ,而不是 inline explicit 。
581601 具体顺序规则参见 @5.5.6.7 。
602+**原理**
603+成员模板函数的 inline 不被 ISO C++ 隐含。
604+**注释**
605+实际实现可能不完全忽略声明的 inline 生成内联代码。
606+例如,G++ 的 -O3 和 -Os 选项隐含 -finline-functions ,尝试对未声明为 inline 的函数内联。
607+但是,判断最终是否内联函数体代码的启发式条件可能不同,且可能影响其它函数。
608+G++ __attribute__((flatten)) 可影响被调用的函数的是否内联。特别地,函数模板的 inline 是否存在 inline 在存在其它选项时(至少如 -O3 )也可影响包括被调函数启发式判断。
609+注意实现中可能有多个优化遍在不同阶段处理内联,如 GCC 至少有 tree-einline (tree early inlining) 和 ipa-inline (inter-procedural analysis inlining) 。
582610
583611 @5.5.6.2 内联语义:
584612 注意 ISO C++ 规定一个 inline 函数总是应被声明为 inline ,这和 ISO C 不同。
@@ -592,7 +620,7 @@
592620 对模板实例,使用显式实例化和 extern 声明保证。
593621 必要时可使用实现的扩展(如 GNU 扩展的 noinline 属性)指示不内联。
594622 以下情形应考虑避免内联:
595-典型实现的虚析构函数的符号位置可能影响 ABI 规范定义的虚表(vtable) 的位置(若所有虚函数定义通过 #include 指令引入,则在每个翻译单元生成虚表: Clang++ 警告 [-Wweak-vtables] )。需要确保符号唯一时,至少一个虚函数(通常是虚析构函数)应仅在类中声明,而定义在确定的翻译单元中(通常非头文件),即使使用显式默认的析构函数。
623+典型实现的虚析构函数的符号位置可能影响 ABI 规范定义的虚表(vtable) 的位置(若所有虚函数定义通过 #include 指令引入,则在每个翻译单元生成虚表:Clang++ 警告 [-Wweak-vtables] )。需要确保符号唯一时,至少一个虚函数(通常是虚析构函数)应仅在类中声明,而定义在确定的翻译单元中(通常非头文件),即使使用显式默认的析构函数。
596624 作为公开接口的回调函数在库中应保留明确的定义以便确定唯一实体。对动态库,应避免实现自动导入或其它修饰。
597625
598626 @5.5.6.4 链接:
@@ -675,6 +703,8 @@
675703 类作用域内使用 using 声明应直接使用被声明的成员所在的直接或间接基类以便找到原始成员声明,除非至少存在以下情况之一:
676704 需要避免多个基类声明路径不唯一造成歧义;
677705 需要强调作为公开接口的 using 声明不依赖引入这个成员的所在的类而是其派生类。
706+注意类内的 using 声明不应冲突。
707+另见 [WG21 P1787R6] 。在此之前,基类成员模板即使和派生类具有不同的返回类型和模板类型,也会被其它部分相同的派生类同名函数模板隐藏。
678708
679709 @5.5.7.2 using 指令:
680710 除非特别指定(例如文档说明),应避免在命名空间作用域内,尤其是头文件中使用 using 指令,以防名称污染。
@@ -706,7 +736,7 @@
706736 在 ISO C++11 实现及启用某些扩展的实现(如 Clang++ )中,可在命名空间定义(namespace-definition) 中使用 inline ,可用于区分版本等。
707737 和函数不同,对命名空间声明,可以只在原始命名空间定义( original-namespace-definition ,之前未存在其它定义)中使用,而不需要所有声明都指定 inline 。
708738 只要能确定命名空间定义在翻译单元中是原始命名空间定义,其它同一命名空间的定义( extension-namespace-definition ,扩展命名空间定义)中的 inline 冗余使用并非语言规则强制必要;但一般仍应使用 inline 清楚地表示 inline 命名空间。(已知 Clang++ 3.4 (trunk186265) 默认会对不使用 inline 的情况产生警告。)
709-注意 ISO C++ 要求若在定义中使用 inline ,则必须是原始命名空间定义。实现可能不严格检查(如 G++ 和 libstdc++ 的 bug : http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53402 )。
739+注意 ISO C++ 要求若在定义中使用 inline ,则必须是原始命名空间定义。实现可能不严格检查(如 G++ 和 libstdc++ 的 bug :http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53402 )。
710740
711741 @5.5.9 友元:
712742 禁止在类定义中定义友元类。
@@ -719,7 +749,7 @@
719749 友元无视访问权限控制的限制。
720750
721751 @5.5.9.2 名称查找:
722-友元被允许 ADL(@5.2.4.1) 查找。
752+友元被允许使用 ADL(@5.2.4.1) 。
723753 参见 http://en.wikipedia.org/wiki/Barton%E2%80%93Nackman_trick 。
724754
725755 @5.6 const 和 volatile 限定符(cv-qulifier) :
@@ -828,7 +858,7 @@
828858 四则运算、取余和向左移位均可能导致溢出。
829859 除数为 -1 可导致溢出,如 http://kqueue.org/blog/2012/12/31/idiv-dos/ 。
830860 注意 n 位无符号数的内建二元 + 、二元 - 、* 、++ 、-- 、<< 及对应复合赋值和一元 - 操作遵循模算术(modular arithmetic) ,保证不溢出而保证结果回绕为 2 ^ n 的余数。
831-应特别注意避免无符号数减法导致非预期的结果。参考: https://www.securecoding.cert.org/confluence/display/seccode/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap 。
861+应特别注意避免无符号数减法导致非预期的结果。参考:https://www.securecoding.cert.org/confluence/display/seccode/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap 。
832862
833863 @5.8.8.3 浮点数:
834864 注意浮点数的舍入误差、非数值(如 NaN 和 Inf 等)和浮点环境。
@@ -1105,7 +1135,11 @@
11051135 尽可能使用 static_cast 代替 reinterpret_cast 。
11061136 接口类型([Documentation::CommonRules @@2.4.1]) 转换可选 dynamic_cast ,但能保证类型安全且不被虚继承时首选 static_cast ,以提升性能。
11071137 如果可能(对 simple-type-specfier 和 typename-specifier ),一般应使用函数调用形式代替 static_cast ,以保持简洁。
1108-注意 const_cast 禁止用于转换 const 动态类型的对象,以免引起未定义行为。
1138+注意 const_cast 转换 const 动态类型的对象为非 const 的值修改时引起未定义行为。
1139+一般只在如下已知动态类型不是 const 对象的情形使用 const_cast 转换移除 const 限定:
1140+作为释放动态分配的资源的特定接口的参数上移除 const ,如 delete 和去配函数释放的对象;
1141+迭代器等类型的转换;
1142+访问需要具有和 mutable 成员等价的语义的基类子对象时(如 ISO C++20 前没有 [[no_unique_address]] 支持而需要确保空基类子对象(@5.1.10) )。
11091143 注意在函数指针和对象指针之间的 reinterpret_cast 是有条件支持的(@5.1.5) 。
11101144
11111145 @5.11.5 成员指针转换限制:
@@ -1140,7 +1174,7 @@
11401174
11411175 @5.12.2.1 参数列表:
11421176 注意只有没有参数的函数允许使用 void 参数类型,在 C++ 中一般省略。
1143-注意非定义的函数声明中 C 和 C++ 对空参数列表的不同: C 的“ () ”相当于 C++ 的“ (...) ”,而 C 的“ (void) ”相当于 C++ 的“ () ”。
1177+注意非定义的函数声明中 C 和 C++ 对空参数列表的不同:C 的“ () ”相当于 C++ 的“ (...) ”,而 C 的“ (void) ”相当于 C++ 的“ () ”。
11441178 函数定义中 C 的“ () ”和 C++ 相同,但一般和原型声明保持一致以保持清晰。
11451179 注意使用 std::va_list 的实际参数时的所在的活动调用。改变活动调用的操作如 RAII(@5.10.5.3.4) 不总是可用。
11461180
@@ -1394,7 +1428,7 @@
13941428 关于友元,参见 @5.5.9 。
13951429
13961430 @5.13.5.1 隐式访问权限:
1397-注意隐式访问权限: class 默认具有 private ; struct 默认具有 public 。
1431+注意隐式访问权限:class 默认具有 private ; struct 默认具有 public 。
13981432 访问权限控制对大多数成员声明能体现实际语义,因此不依赖隐式访问权限,除了以下例外:
13991433 省略成员访问权限:友元声明和 static_assert 声明,对此无论何种访问权限都不显式改变语义,因此这些声明应出现在类定义中的第一个 access-specifier 前;
14001434 省略基类访问权限:明确的基类继承,如 struct 元函数([Documentation::CommonRules @@3.10]) 继承或混入操作符模板类继承。
@@ -1411,7 +1445,7 @@
14111445 @5.13.5.3 非静态数据成员访问权限:
14121446 注意显式指定非静态数据成员的访问权限可能影响布局和分配顺序。
14131447 ISO C++03 9.2/12 规定不同 asscess-specifier 声明的非静态数据成员所在的地址相对顺序和之间的分配顺序是未指定的,这包括相同的访问权限。
1414-ISO C++11 放宽了语言规则的限制但限制了实现: ISO C++11 9.2/13 规定不同访问权限的非静态数据成员所在的地址相对顺序和之间的分配顺序是未指定的。
1448+ISO C++11 放宽了语言规则的限制但限制了实现:ISO C++11 9.2/13 规定不同访问权限的非静态数据成员所在的地址相对顺序和之间的分配顺序是未指定的。
14151449 此外, ISO C++11 9/7 规定含有不同访问权限的非静态数据成员的类不是标准布局类,因此也不是标准布局类型(@5.2.2) 。
14161450
14171451 @5.13.6 继承:
@@ -1640,6 +1674,8 @@
16401674 类型特征于 ISO C++ TR1 和 ISO C++11 中引入,属于元编程设施。
16411675 合理使用类型特征进行关系判断,如 is_same 判断类型相同。
16421676 合理使用类型特征进行类型操作,如 std::add_lvalue_reference 可避免泛型代码中出现非法的 void& 。
1677+使用 ! 判断类型特征时,直接使用 value 成员,而不是通过构造对象后进行转换,以避免被非预期的 ADL(@5.2.4.1) 重载影响。
1678+**注释** 因为没有限制用户提供非成员 operator! 重载,ADL 可能因此得到错误的结果。且不论是否存在重载,当类型参数是类类型时,ADL 会不必要地限定类型参数是完整类型。
16431679
16441680 @6.9 存储管理:
16451681
diff -r eae6ca1f576c -r e1756f2eba1f doc/NPL.txt
--- a/doc/NPL.txt Thu Jun 30 07:43:19 2022 +0800
+++ b/doc/NPL.txt Tue Jul 12 18:45:26 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPL.txt
1212 \ingroup Documentation
1313 \brief NPL 规范和实现规格说明。
14-\version r28517
14+\version r28845
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 304
1717 \par 创建时间:
1818 2012-04-25 10:34:20 +0800
1919 \par 修改时间:
20- 2022-06-03 08:50 +0800
20+ 2022-07-12 18:44 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -704,7 +704,7 @@
704704
705705 @4 语义:
706706 NPL 的语义规则(@1.2.4) 构成演绎系统(deductive system) ,通过对翻译单元(@3.1) 中的表达式(@3.4.2) 的求值(@4.1) 表达。
707-除非派生实现另行指定,仅使用表达式(@3.4.2) 指定关于对象语言中的计算的语义。
707+除非派生实现另行指定,仅使用表达式(@3.4.2) 指定关于对象语言(@1.2.4) 中的计算的语义。
708708 基本语义规则要求:
709709 所有不需要诊断消息(@1.2.4) 的规则由派生实现定义;
710710 本章内的规则应不引入未定义行为(@1.2.4) 。
@@ -752,6 +752,7 @@
752752 **注释**
753753 在实现执行的上下文,生存期概念兼容 IEC 2382 的 lifetime 定义:
754754 portion of the execution duration during which a language construct exists
755+有序对作为绑定的抽象表示,不需要被对象语言支持。
755756 典型地,作用包括计算得到的值、产生的副作用以及其它可由区域和变化的状态二元组描述的实体。
756757 一等对象同时是对象。
757758 为满足可作为表达式被求值,一等实体总是能关联表达求值结果的值,称为实体的值。
@@ -1000,7 +1001,8 @@
10001001 注意有引用的语言设计中不能排除被引用对象,否则无法确定对象的值以表达计算;相反,不对引用而直接对值操作仍然能实现一些足够有意义的程序。
10011002 因此,若存在引用,无法忽略非引用(即便非引用不能在对象语言被直接使用);引用可以由非引用的对象添加语义规则派生。
10021003 要求语言操作的一等对象总是关联到引用的设计实质上使对象语言的一等对象都是引用。但这不表示引用是自然的一等实体,因为引用的作用仅是操作被引用对象,无法把引用自身作为一等对象进行操作。
1003-反之,通过使引用和被引用对象同为一等对象(@4.2.1) ,NPL 支持一等引用(first-class reference) 。
1004+反之,通过使引用和其它一些非引用的对象同为一等对象(@4.2.1) ,NPL 支持一等引用(first-class reference) 。
1005+一等引用支持一等对象作为被引用对象。若实现允许非一等对象作为被引用对象,可作为被引用对象的非一等对象由实现定义。
10041006 特定的操作可能预期非引用,或总是隐含通过引用访问被引用对象,这不改变引用被作为一等对象使用的普遍支持。
10051007 一等引用的相等关系(@4.1.4) 定义被引用对象的名义同一性相等(@4.2.1) 。这允许相等的引用上推理引用透明性(@4.1.4.1) 。
10061008 一等对象的使用仍然可以通过要求引用访问(access) 以避免在任意上下文中需要不同的对象副本。但这并不应排除其它形式的一等对象操作。
@@ -1558,6 +1560,7 @@
15581560 表达式相等蕴含参数匹配一致性和替换结构一致性。实现可分别定义其它规则扩充这些等价关系的外延。
15591561 替换参数的值蕴含对实际参数的计算的依赖,即参数的值的计算先序(@4.4.3) 函数应用的求值;但其它求值顺序没有保证。
15601562 **注释**
1563+一般地,根据 E1 的值,操作数或操作数的求值结果被作为实际参数。
15611564 过程(@4.5.2.2) 及其调用在其操作语义的元语言中通常表达为函数及函数合并(@4.5.3) 。
15621565 若过程的结果被忽略,则通常表达为单元类型(@4.7) 的值。
15631566 此外,一些语言中忽略过程的结果是空类型(@4.7) ,以检查错误的使用。NPL 不要求语言具有静态类型(@4.7.1) 规则,也不要求这些检查。
@@ -2000,6 +2003,8 @@
20002003 推论:若一等实体不是一等对象,存储可能和一等对象的存储方式不同。派生实现可在必要时约定与其它一等实体存储的差异。
20012004 和宿主语言类似,对象的生存期(@4.1) 是存储期的子集。创建对象基于已确保可访问的存储;销毁对象结束后释放存储。
20022005 基于宿主操作等意义,作为一等对象相同方式传递的一等实体都在此都视为一等对象;仅当不依赖一等对象的性质时,实现以非一等对象的方式实现一等实体的操作。
2006+**注释**
2007+NPLA 支持特定的非一等对象作为引用值(@5.8.3) 的被引用对象(@4.2.3) 。
20032008
20042009 @5.6.2 求值和对象所有权:
20052010 被求值的表达式的内部表示中的对象具有 NPLA 对象的所有权。
@@ -2095,6 +2100,11 @@
20952100 除非另行指定,同一个的对象不同子对象的存储期起始、存储期结束、生存期起始、生存期结束之间分别无序。
20962101 对象对其存储期和生存期的其它约束和宿主语言相同。
20972102 对象可通过子对象引用和与其生存期相关或无关的其它对象。
2103+通过子对象访问的被引用对象(@4.2.3) 上的副作用(@4.1.3) 是否蕴含对象上的副作用未指定。
2104+**原理**
2105+和宿主语言不同,为简化非一等对象表示(@6.3) 的项上的操作,子对象之间不一定共享表示。
2106+特别地,通过子对象引用(@6.8.3.3) 访问的对象的子对象之间不一定具有同一性(@4.1) 。
2107+子对象不一定支持可修改的一等状态(@4.2.2.1) 。修改子对象可能导致或不导致对象或先前通过相同方式取得的子对象的改变(@4.1.4.2) 。
20982108
20992109 @5.7 NPLA 环境:
21002110 求值环境(@4.6.1) 维护名称和作用域。
@@ -2112,8 +2122,8 @@
21122122 父环境一般不共享环境记录。通过这种方式实现重定向(@4.3.3) 的环境表示是链接的(linked) 而非平坦的(flat) 。
21132123 **注释**
21142124 变量名以字符串表示,没有直接值的限制,可能为空串。
2115-被绑定对象除具有和一等对象相同的表示外,可能还具有附加的非一等对象表示的状态,参见临时对象标签(@6.2.2) 。
2116-若环境记录直接持有被引用对象,则这些对象是环境记录的子对象(@5.6.6) 。
2125+被绑定对象除具有和一等对象相同的表示外,可能还具有附加的非一等对象表示(@6.3.4) 的状态,参见临时对象标签(@6.2.2) 。
2126+若环境记录直接持有被引用对象(@4.2.3) ,则这些对象是环境记录的子对象(@5.6.6) 。
21172127 关于环境的内部表示,参见环境数据结构(@6.11.1) 。
21182128
21192129 @5.7.2 环境引用:
@@ -2128,7 +2138,7 @@
21282138 否则,为支持影响可观察行为的环境的修改(@4.1) ,非环境记录的环境引用是必要的。
21292139 环境引用也是一种较简单且一般普遍高效的父环境的实现表示,可直接实现链接的(@5.7.1) 环境而不需要证明和实现特设的其它内部表示能和抽象机(@2.6) 意义上链接的环境保持语义等价。
21302140 续延捕获(@4.5.3.3) 若复制续延,可能引起关联的环境的复制,影响可观察行为并引起不必要的实现开销。为此,区分环境引用是必要的。
2131-以环境引用作为一等对象使访问被引用对象等环境记录的子对象时需要间接访问,在环境实际不需要被复制的大部分其它场景引起开销。这种开销是可接受的,因为:
2141+以环境引用作为一等对象使访问被引用对象(@4.2.3) 等环境记录的子对象时需要间接访问,在环境实际不需要被复制的大部分其它场景引起开销。这种开销是可接受的,因为:
21322142 考虑到一等环境的普遍性,有必要有效支持对象语言中创建环境临时对象(而不仅仅是环境对象的引用值(@5.8.3) )的使用使之避免复制;
21332143 实现可能提供附加的证明以在优化的翻译(@2.4.1) 过程中替换环境引用为环境记录或其它不需要间接访问的中间表示,以消除这些开销。
21342144 不论这样的证明是否存在,环境强引用和弱引用仍在对象语言中区分,以明确接口上的所有权语义(@4.2.2.3) 。
@@ -2222,10 +2232,12 @@
22222232 @5.8.3 引用值(reference value) :
22232233 在对象语言中,引用值(reference value) 作为引用的值,可保存在一等对象中,这样的一等对象是引用对象(reference object) 。
22242234 引用值和引用对象的值具有引用类型(@5.8.1.2) 。
2225-在特定上下文中引用和其它一等对象(@4.2.1) 的值的相同具有不同的语义。这主要体现在引用值被按值直接初始化传递和按引用传递时(@4.4.6.5) 。
2235+在特定上下文中,引用和其它一等对象(@4.2.1) 的值的相同具有不同的语义,主要体现在引用值被按值直接初始化传递和按引用传递时(@4.4.6.5) 。
22262236 **注释** 差异和 ISO C++ 中使用对象类型和引用类型作为参数类似。
22272237 NPLA 引用值总是假定和被引用对象(@4.2.3) 关联。
22282238 **注释** 和宿主类型类似,引用类型没有空值。
2239+仅当以下情形中,NPLA 引用值的引用对象是非一等对象(@4.2.3) :
2240+作为临时对象(@5.8.5) 的被绑定对象(@5.7.1) 。
22292241
22302242 @5.8.3.1 有效性:
22312243 引用值有效当且仅当访问被引用对象不引起未定义行为。
@@ -2261,7 +2273,7 @@
22612273 唯一引用可被假定不被共享,被引用对象不被别名(@4.2.3.4.3) 。
22622274 通过不可修改引用的左值的对象访问不包含修改(@4.1) 。否则,若没有引起错误,程序行为未定义;但除非另行指定,不引起宿主语言的未定义行为(@5.4) 。
22632275 **原理** 宿主语言的互操作不被总是要求保证对象语言程序的可移植性,但不应引起实现自身的行为无法预测。
2264-对引用值的操作满足特定引用属性的传播性质,当且仅当:
2276+对引用值的操作传播(propagate) 特定的引用属性,当且仅当:
22652277 若操作数是具有特定引用属性的引用值,且结果是引用值时,结果具有和操作数相同的特定属性。
22662278 **注释**
22672279 唯一引用蕴含的假定类似 ISO C 约定的 restrict 关键字,但程序违反假定的约束时不引起未定义行为。
@@ -2281,7 +2293,7 @@
22812293 消除完全折叠的引用值的结果总是右值(@5.8.1) 。
22822294 推论:因为引用值不循环引用(@4.2.4) 自身,除非引用值已完全折叠(@5.8.3.2) ,继续消除引用值得到的值和引用值是不同的值。
22832295 **原理**
2284-特定的引用值消除可蕴含对不可修改的传播性质(@5.8.3.3) 的要求。这和 ISO C++ 初始化引用时遵循的 const 安全性,属于类型安全性的一种。
2296+特定的引用值消除可蕴含对不可修改的传播(@5.8.3.3) 的要求。这和 ISO C++ 初始化引用时遵循的 const 安全性,属于类型安全性的一种。
22852297 但是,消除引用不一定总是预期这种性质,特别当折叠不被预期时。
22862298 例如,ISO C++ 内建指针的不同级 const 不会被隐式转换直接折叠合并。消除间接的指针值不是隐式的(而依赖内建一元 * 操作符),这是因为指针作为类型构造器自身的类型安全需要;是否消除 const 限定符仍然需要基于其它理由考虑。
22872299 而当被引用对象实现子对象时,修饰被指向的类型的 const 不会自动传播到子对象的类型中,此时可有 std::experimental::propagate_const 可选引入这种性质。
@@ -2326,7 +2338,7 @@
23262338 临时对象实质化转换中,纯右值被实质化(materialized) 。
23272339 在求值子表达式时,按表达式具有的语义,必要时(如按 @5.8.1.1 判断上下文的值类别)进行值类别转换。
23282340 为支持引用值(@5.8.3) 作为一等对象(特别是未折叠的引用值(@5.8.3.2) ),NPLA 提供比左值到右值转换更精细的引用值提升转换,即以下转换操作:
2329-若操作数是引用值,则结果是操作数引用的被引用对象;
2341+若操作数是引用值,则结果是操作数引用的被引用对象(@4.2.3) ;
23302342 否则,结果是操作数。
23312343 根据引用值的性质,易知左值到右值转换的规约是引用值提升转换的规约的传递闭包,即:
23322344 若操作数是已完全折叠的引用值(@5.8.3.5) ,则引用值提升转换等价左值到右值转换;
@@ -2498,7 +2510,7 @@
24982510
24992511 @6.1 实现方案概述:
25002512 NPLA 实现支持可扩展的 C++ API ,作为解释器(interpreter) 的实现基础。
2501-本方案中,除非另行指定(如总体架构(@6.1.4) 中的描述),API 在模块 NPLA 提供。
2513+本方案中,除非另行指定(如总体架构(@6.1.3) 中的描述),API 在模块 NPLA 提供。
25022514 NPLA 以外的附属模块主要包括 NPLAMath(@6.14) 。
25032515 如 NPLAMath 这样没有被模块 NPLA 依赖的附属模块不是 NPLA 设计(@5) 的直接实现,而为派生实现提供和 NPLA 公共实现的并列的基础功能。
25042516
@@ -2539,12 +2551,37 @@
25392551 @6.1.2.2 外部实现依赖配置:
25402552 宏定义 NPL_Impl_NPLAMath_Has_UInt128 和 NPL_Impl_NPLAMath_UseQuadMath 配置 NPLAMath(@6.14) 的实现,详见外部表示 API 的优化实现(@6.16.6.1)
25412553
2542-@6.1.3 安全保证:
2554+@6.1.3 总体架构:
2555+NPLA 核心实现为对数据结构的管道-过滤器(pipe-filter) 架构模式的处理框架。每个处理节点实现一个或若干个阶段(@2.4.1) 。
2556+这里的数据结构是语言实现的 IR(@2.4.1) 或通过代码生成(@2.4.1) 得到的代码。在 NPLA 实现中后者是可选的。
2557+本节下文首先指定翻译单元经过词法分析(@6.1.5) 和语法分析(@6.1.6) 这两个前端(frontend) ,之后的处理见本章中的其余节。
2558+除非另行指定,本节中的 API 都位于模块 NPLA ,详见 NPLA 公共语言实现接口(@6.1.1) 。
2559+本章(@6) 下文中,C++ 名称使用限定名称(带有 :: 的前缀)以避免混淆,除以下例外:
2560+ 本章在引入的非限定的类型名称,视为在 C++ 命名空间 NPL 中引入。
2561+ **注释** 这些名称包含指定为类等具体声明等形式,以及类型别名。
2562+ 以下名称视为在 C++ 命名空间 YSLib 中引入 NPL :
2563+ shared_ptr
2564+ observer_ptr
2565+ weak_ptr
2566+ **注释** 实现可直接通过 using 声明引入这些名称。
2567+ **注释** 满足本文档的要求时,实现也可在相关命名空间引入不在本文档中要求的 YSLib 名称。
2568+本章下文中,项目的模块命名空间([ProjectRules @@3.2]) 名称若为 NPL 模块,省略 NPL 前缀;其它 YSLib 模块需使用限定名称;非 YSLib 模块还需指定库的来源。
2569+本章下文中,使用 namespace 关键字指定 C++ 命名空间。
2570+明确为类或枚举等(而非以“类型”描述)的具体类型实体直接定义在所在的作用域中(不带有内联命名空间,也不被定义为类型别名)。除此之外,类型声明是否为某个类模板的特化的别名是未指定的。
2571+**注释**
2572+明确类型定义保证能明确限制特定的 ADL 函数调用。
2573+例如,在命名空间 NPL 中,因为 TermNode(@6.2) 明确为非模板特化的类类型,仅有以 TermNode 类型的实际参数的函数可不需要 NPL:: 前缀限定调用。
2574+对本文档中未明确引入的名称,调用时是否使用 ADL ,按实现提供的声明和注释约定确定。
2575+
2576+@6.1.4 安全保证:
2577+NPLA API 和实现提供特定的整体安全保证以满足对象语言的要求,并提升实现行为的可预测性。
2578+
2579+@6.1.4.1 嵌套调用安全:
25432580 除以下例外,NPLA 实现及对应可能由用户提供的替换的实现应支持嵌套调用安全(@5.11) :
2544- NPL::TermNode 上的部分遍历节点的操作。
2581+ TermNode(@6.2) 上的部分遍历节点的操作。
25452582 直接遍历参数节点的操作。
25462583 **注释** 包括通过 ValueNode 构造及比较相等或不等的操作。
2547- 访问 NPL::TermNode::Value 引起嵌套重入的操作。
2584+ 访问 TermNode::Value 引起嵌套重入的操作。
25482585 **注释** 例如对任意目标类型(@6.2) 的值数据成员的比较。
25492586 **注释** 析构保证嵌套调用安全,除非其中的值数据成员的析构非嵌套安全。
25502587 直接递归遍历节点的操作。
@@ -2555,17 +2592,18 @@
25552592 对 TermNode 的复制构造实现的嵌套调用安全依赖内部实现选项(@6.1.2) NPL_Impl_SContext_Enable_ThunkedActions 。
25562593 嵌套调用可能影响性能。在复制支持嵌套调用安全时,宏 NPL_Impl_SContext_Enable_ThunkedThreshold 指定使用允许直接嵌套调用(但在一定范围内不保证安全)的最大宿主函数调用层数。
25572594
2558-@6.1.4 总体架构:
2559-NPLA 核心实现为对数据结构的管道-过滤器(pipe-filter) 架构模式的处理框架。每个处理节点实现一个或若干个阶段(@2.4.1) 。
2560-这里的数据结构是语言实现的 IR(@2.4.1) 或通过代码生成(@2.4.1) 得到的代码。在 NPLA 实现中后者是可选的。
2561-本节下文首先指定翻译单元经过词法分析(@6.1.5) 和语法分析(@6.1.6) 这两个前端(frontend) ,之后的处理见本章中的其余节。
2562-除非另行指定,本节中的 API 都位于模块 NPLA ,详见 NPLA 公共语言实现接口(@6.1.1) 。
2563-本章下文中,除本节和本节在 C++ 命名空间 NPL 中引入的类型名称以及如下列出的使用 C++ 命名空间 YSLib 的名称外,其余 C++ 名称使用限定名称(带有 :: )以避免混淆:
2564-shared_ptr
2565-observer_ptr
2566-weak_ptr
2567-本章下文中,项目的模块命名空间([ProjectRules @@3.2]) 名称若为 NPL 模块,省略 NPL 前缀;其它 YSLib 模块需使用限定名称;非 YSLib 模块还需指定库的来源。
2568-本章下文中,使用 namespace 关键字指定 C++ 命名空间。
2595+@6.1.4.2 异常安全:
2596+基本内容参见 [Documentation::LanguageConvention @@5.10.5.3] 和 [Documentation::YFramework @@3.2] 。
2597+在此基础上,NPLA 对表示对象语言一等对象的基本操作上提供有限的强异常安全保证([Documentation::LanguageConvention @@5.10.5.3.2]) :
2598+ 修改最小的不可分割的一等对象表示(@6.3.4) 的操作若失败,则回滚到修改这个对象之前的状态。
2599+ 除非另行指定,本机实现(@5.3) 中可能通过用户程序引入的非平凡析构的对象表示的资源对象,满足强异常安全保证。
2600+**原理**
2601+TermNode(@6.2) 可表示用户程序引入的对象可包括对象语言程序引入的对象。
2602+这些对象一般是用户定义的,不能保证宿主语言异常抛出时的状态,因此实现提供更强的运行时状态的可预测性可能减少互操作和本机实现(@5.3) 的复杂性。
2603+但是,保证多个对象复制的强异常安全的事务(transaction) 维护复制的状态而需要冗余开销。
2604+为平衡性能和可预测性,除 TermNode 直接支持的操作,在一等对象的边界以外部分不提供事务性保证。
2605+**注释**
2606+关于本机实现对异常的使用的其它情形仍然遵守一般约定([Documentation::YFramework @@3.2]) 。
25692607
25702608 @6.1.5 词法分析:
25712609 词法分析以源代码为输入(@5.2.3) ,遵循词法分析规则(@3.3.5) 输出分析的记号序列(@2.4.1) 。
@@ -2605,7 +2643,7 @@
26052643 SContext 模块在 namespace NPL 通过别名声明引入若干 YSLib 类型名称,包括(但不限于):
26062644 ValueObject 、ValueNode 、observer_ptr 和 LoggedEvent 。
26072645 关于这些类型,另见 [Documentation::YSLib @@3.16] 和 [Documentation::YSLib @@3.2] 。
2608-语法分析提供 TermNode 类型(@6.2) 作为表示分析树(根据以上讨论,同时也是语法对象和后续使用的 AST )的处理结果类型,及其相关操作。
2646+语法分析提供 TermNode 类类型(@6.2) 作为表示分析树(根据以上讨论,同时也是语法对象和后续使用的 AST )的处理结果类型,及其相关操作。
26092647 以下类型和 TermNode 关联:
26102648 TNCIter
26112649 TNIter
@@ -2641,7 +2679,7 @@
26412679
26422680 @6.2 IR 节点数据结构:
26432681 NPLA 实现使用节点(node) 数据结构表示实现使用的 IR 中间表示递归的构造,如 SContext 产生的 AST 的节点和语义分析使用的项。
2644-这样的节点类型为 TermNode ,也用于表示单一的树(如整个 AST(@6.1.6) )。
2682+这样的节点类型为 NPL 中定义的类 TermNode ,也用于表示单一的树(如整个 AST(@6.1.6) )。
26452683 NPLA 实现语义规则时对 TermNode 进行处理,包括节点上的规约(@4.1) ,即树规约(tree reduction) 。TermNode 对象在树规约中表示被规约项(reduced term) 。
26462684 树规约的输入和各个规约步骤的结果中,变量不需要直接被表示,规约允许和输入 AST 中一致的未求值的名称,参见 @6.8.1 ;同时,允许在同一个树中包含经不同过程转换的结果,不需要显式区分阶段(@2.4.1) 。
26472685 因为绑定(@4.1) 的表示方法不在 IR 内特设指定,树规约不需要使用 HOAS(Higher-Order Abstract Syntax ,高阶抽象语法)。接受表达式到表达式的映射附加处理可通过派生实现约定对象语言中特定类型的对象实现,不要求被编码在规约的结果中。
@@ -2653,9 +2691,13 @@
26532691 其它的宿主对象(@5.5) 也可能实现 ValueObject 类似的动态类型机制,保存相应的目标类型的目标对象,但不一定支持其它对象。
26542692 **注释** ValueObject::GetObject 是无检查访问目标对象的基本接口。其它通常使用类型擦除(@4.7.3) 实现的类型,如 std::function 的实例,也有类似的目标对象的概念。
26552693 目标对象是目标函数(target function) ,当且仅当是宿主语言的函数对象。
2656-TermNode 对子节点和值数据成员具有直接的独占所有权。
2657-作为子节点的容器,TermNode 子节点的迭代器、指针和引用保持稳定,即移除子节点时保持其它项的迭代器、指针和引用有效而不被无效化(invalidated) 使访问引起宿主语言的未定义行为(@5.4) 。
2658-TermNode 子节点的稳定性也支持项被转移时(如维护临时对象的内部存储(@5.6.2) )不需要附加操作维护已在其它位置被引用的子项有效性。
2694+子节点和值数据成员是节点的子对象(@5.6.6) 。节点对子节点和值数据成员具有直接的独占所有权。
2695+每个节点中:
2696+子节点(若存在)的初始化先序(@4.4.3) 值数据成员的初始化;
2697+子节点(若存在)的销毁后序(@4.4.3) 值数据成员的销毁;
2698+不同子节点(若存在)之间的初始化和销毁顺序未指定。
2699+作为子节点的容器,子节点的迭代器、指针和引用保持稳定,即移除子节点时保持其它项的迭代器、指针和引用有效而不被无效化(invalidated) 使访问引起宿主语言的未定义行为(@5.4) 。
2700+子节点的稳定性也支持项被转移时(如维护临时对象的内部存储(@5.6.2) )不需要附加操作维护已在其它位置被引用的子项有效性。
26592701 针对 TermNode 的一些操作如移除第一个子节点的 RemoveHead 一般使用 ADL 方式调用。
26602702 树规约可按需添加或删除 TermNode 的子节点。具体添加或删除的时机未指定,取决于具体的规约算法。
26612703 除非派生实现另行指定,删除值数据成员和删除子节点的作用非决定性有序(@4.4.3) 。
@@ -2667,8 +2709,12 @@
26672709 除非另行指定,NPLA 及其派生实现的使用 TermNode 的 API 隐含上述关于分配器相等性的前置条件。
26682710
26692711 @6.2.1 项节点结构分类:
2670-节点容器的内容被视为子节点按迭代顺序确定的有限的序列,即真列表(proper list) 。
2671-真列表不包含环(cycle) 。其它形式的列表(list) 数据结构,如 Scheme 和 Kernel 中可能有环的非真列表(improper list) 不被支持。
2712+节点容器的内容被视为子节点按迭代顺序确定的有限的序列。
2713+当这些序列中的每个节点表示一等对象时,即构成无环列表(acyclic list) 的表示。
2714+推论:列表对其元素具有所有权。
2715+无环列表不包含环(cycle) 。
2716+若节点的值数据成员同时为空值(即 ValueObject() ),则列表是真列表(proper list) 。
2717+真列表中每个子节点是构成真列表的元素(element) 的对象的表示。
26722718 按内容的结构,项节点具有如下互斥的基本分类(使用 TermNode 的 empty() 判断):
26732719 枝节点(branch node) 或非叶节点,即具有子节点的节点,表示非空列表或同时具有子节点和叶节点的非正规(@6.2.1) 的非列表;
26742720 叶节点(leaf node) ,即不具有子节点的节点,表示空列表或不具有子节点的正规的(@6.2.1) 非列表。
@@ -2686,17 +2732,20 @@
26862732 先判断是否为枝节点,再判断是否为符合预期类型的非空叶节点,分派不同的操作;
26872733 根据项表示的值(@4.1) 考虑子节点及值数据成员。
26882734 **注释**
2735+真列表以外的其它形式的一般列表(list) 数据结构及其表示另见真列表以外的其它列表(@6.3.9.1) 。
2736+如 Scheme 和 Kernel 中可能有环的非真列表(improper list) 不被支持。原理参见循环引用(@4.2.4) 。
26892737 关于规约后的一般表示,参见正规表示(@6.10.6) 。
26902738
26912739 @6.2.2 项的标签(tag) :
26922740 枚举 TermTagIndices 和枚举 TermTags 作为索引和对应的掩码提供项使用的标签作为特定的对象表示。
26932741 每个项中包含一个 TermTags 对象作为标签数据供用户程序使用。
26942742 标签符合 ISO C++ [bitmask.types] 定义的位掩码(bitmask) 类型。TermTagIndices 是位掩码的索引。
2743+没有没有设置任何掩码位的默认值是 Unqualified :非限定对象。
26952744 TermTags 中的枚举项对应取值不相等的索引,表示的标签主要有:
2696-Unqualified :非限定对象,是没有设置任何掩码位的默认值。
2697-Unique :唯一引用(@5.8.3.3) 。
2698-Nonmodifying :不可修改(@5.8.3.3) 。
2699-Temporary :临时对象(@5.8.5) 。
2745+ Unique :唯一引用(@5.8.3.3) 。
2746+ Nonmodifying :不可修改(@5.8.3.3) 。
2747+ Temporary :临时对象(@5.8.5) 。
2748+ Sticky :粘滞位,不构成单独的一等对象表示(@6.3.4) ,用于特定的对象的表示的一部分(@6.3.9.1) 。
27002749 在 TermTagIndices 中的枚举项带有 Index 后缀,和这些枚举项一一对应。
27012750 当前标签值都和引用值的对象访问对应的含义相关,参见引用值的属性(@5.8.3.3) 。
27022751
@@ -2723,24 +2772,42 @@
27232772 另见绑定操作(@7.7.3) 及其中关于临时对象标签的描述(@7.7.3.2) 。
27242773
27252774 @6.2.3 项节点访问:
2726-模块 SContext 提供访问项节点的 API 。
2775+TermNode 中,值数据成员可直接访问。
2776+修改值数据成员可直接赋值,但也可能使用其它辅助的成员(如 SetValue )简化访问(如需要使用分配器时)。
2777+SetValue 对单一参数是 ValueObject 左值或者右值以外的情形使用分配器。
2778+模块 SContext 在类 TermNode 外还提供其它访问项节点的 API 。
27272779 命名空间 NPL 中,TermNode 在类定义外提供关于节点内容的辅助 API 简化操作,以 Doxygen 命令标识为 \relates TermNode 。
2728-其中,判断项节点基本分类(@6.2.1) 的谓词以 Is 前缀起始:
2780+其中,判断项节点分类(@6.2.1) 的谓词以 Is 前缀起始。
2781+根据是否存在子项和非空的值数据成员的分类谓词包括:
27292782 IsBranch
27302783 IsBranchedList
27312784 IsEmpty
27322785 IsExtendedList
27332786 IsLeaf
27342787 IsList
2788+按以上基本分类和第一个子项(若存在)的粘滞位(@6.2.2) ,提供以下扩展分类谓词:
2789+IsAtom
2790+IsPair
27352791 判断值数据成员(@6.2) 具有的类型或值的模板以 Is 或 Has 起始:
27362792 IsTyped
27372793 IsTypedRegular
27382794 HasValue
2795+一些 API 专用于查找或判断标签包含粘滞位的项:
2796+函数模板 FindSticky
2797+函数 FindStickySubterm
2798+函数 HasStickySubterm
27392799 函数模板 NPL::Access 和 NPL::AccessPtr 访问值数据成员中持有对象的引用或指针。重载兼容 TermNode 和 ValueNode 。
27402800 其余 API 包括:
27412801 函数模板 NPL::TraverseSubnodes
27422802 以上操作通常仅在不涉及语义(仅涉及语法,或更基本的底层操作实现)时使用其访问项中非列表类型(@6.2.1) 的值。
27432803 涉及更具体的语义时,使用项访问操作(@6.9.2) 、项引用和项引用访问操作(@6.9.3) 的 NPLA API 代替。这些接口提供了确切的项结构检查以符合正规化表示的相关假设(@6.10.6.1) 。
2804+**原理**
2805+原子节点定义为不能表示为一等对象有序对的节点表示。其命名来自传统 Lisp 的原子谓词。在这个定义下,原子谓词判断的原子数据类型和有序对类型互补。
2806+这和多数 Lisp 方言中对原子节点谓词的定义一致。仅有少数方言如 InterLisp 存在不是原子也不是有序对的数据类型。
2807+**注释**
2808+修改值数据成员为环境引用的宿主类型(@6.8.2) 和项引用(@6.8.3) 的值通常使用成员 SetValue ,且在合适时使用 in_place_type 实例参数。
2809+一般地,bool 、算术类型和枚举等较小的类型使用分配器缺少开销收益,不使用分配器。
2810+关于原子谓词在 Lisp 方言中的兼容性的注释,另见 https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node73.html 。
27442811
27452812 @6.3 表达式和对象的表示(@1.2.4) :
27462813 NPLA 表达式及其求值过程中得到的值以 TermNode(@6.2) 表示。
@@ -2776,16 +2843,18 @@
27762843 可表示一等对象的值的标签值是表示一等对象的值的项的标签中可能具有的标签值。
27772844
27782845 @6.3.2 临时对象的表示:
2779-作为一等对象的临时对象和其它一等对象表示方式一致。
2846+作为一等对象的临时对象和其它一等对象表示(@6.3.4) 方式一致。
27802847 具有特定表示的 NPLA 非一等对象(@4.1) 是临时对象(@5.8.5) ,包括:
27812848 所有权可被项独占(@6.4.5) 而不能作为一等对象访问的对象;
27822849 具有临时对象标签(@6.2.2) 的项作为内部表示的对象。
2850+对临时标签对象决定的非一等对象,去除临时对象标签后,应具有一等对象表示(@6.3.4) 。
27832851 **原理**
27842852 临时对象作为对象语言中不可见的对象和一等对象相同的宿主类型(项(@6.2) )作为内部表示。因此,区分其内部表示并非通过宿主语言中的类型,而需通过运行时性质(宿主语言中项对象的值)确定。
27852853 一些表示可能仅出现在临时对象中,而不是合法的一等对象表示。实现可据此进行一定运行时检查,以排除互操作(@5.3) 或者错误实现中的误用。
2854+对临时对象标签决定的非一等对象和一等对象表现之间的要求简化实现的一些操作,例如提升(@6.9.4.5) 转换临时对象到对应的非一等对象,使实质化(@5.8.4) 不需依赖另行分配的资源。
27862855 **注释**
27872856 直接构成项的非一等对象可以是通过外部表示(@4.1.1) 经翻译(@2.4.1) 变换得到的具有内部表示的数据结构(@6.2) 的非一等对象。
2788-临时对象引用(@5.8.3.3) 的元数据(@6.8.3.1) 以外的临时对象标签仅在被绑定对象上存在(@6.2.2.2) 。
2857+临时对象引用(@5.8.3.3) 的元数据(@6.8.3.1) 以外的临时对象标签仅在被绑定对象(@5.7.1) 上存在(@6.2.2.2) 。
27892858
27902859 @6.3.3 引用值(@5.8.3) 的表示:
27912860 引用值可使用以下方式表示:
@@ -2794,7 +2863,7 @@
27942863 其中,前者是值数据成员为项引用(@6.8.3) 对象的项。
27952864 引用项中的项引用对象引用一个(其它的)项,用于在必要时引入可被引用的一个项而不在 TermNode 中直接储存这个项的值。
27962865 对实现作为一等对象(@4.1) 的列表(@6.2.1) 的引用(@4.2.3) ,支持引用整个项的中间值是必要的;但项引用也支持引用非列表项。
2797-临时对象(@5.8.5) 可作为引用值的被引用对象。
2866+临时对象(@5.8.5) 可作为引用值的被引用对象(@4.2.3) 。
27982867 因为临时对象不是一等对象(@5.8.5) ,临时对象的引用值可代替关联的被引用对象使之作为一等对象被访问,因此使用被引用对象添加 TermTags::Temporary(@6.2.2) 标签表示。
27992868 与此不同,非临时对象的引用值可作为一等对象而总是需要区分作为不同对象的同一性(@4.1) 。
28002869 引用值可能是左值引用(lvalue reference) 或右值引用(rvalue reference) 。
@@ -2807,7 +2876,8 @@
28072876 否则,对应的值类别是右值。
28082877
28092878 @6.3.4 一等对象的表示:
2810-一部分项可以作为对象语言中的一等对象或值的表示(@1.2.4) 。
2879+一部分项可以作为对象语言中的一等对象或值的表示(@1.2.4) ,即一等对象表示。
2880+一等对象的内部表示(@1.2.2) 应具有一等对象表示。
28112881 由标签的使用范围(@6.2.2.1) 的推论,表示一等对象的值的项不具有默认值外的标签(@6.2.2) 。
28122882 **注释** 一等对象也包括对象语言中可访问的列表的元素。因此,这些列表项的子节点也不具有默认值外的标签。
28132883 TermNode 还可能表示求值的中间结果(即求值规约中取得对象语言表达式的值之前出现的值),如表示某些中间值(@6.8) 的情形。
@@ -2867,19 +2937,47 @@
28672937 作为 NPL 语义(@4) 的扩展,作用于非表达式项上的规约规则不是求值规则。
28682938
28692939 @6.3.9 非平凡(@6.3.8) 非正规表示:
2870-NPLA 非正规表示应符合以下约定:
2871-规约中,只存在受支持的非平凡非正规的规约表示;否则,行为未定义。
2940+NPLA 非正规表示应符合以下约定,否则行为未定义:
2941+带有粘滞位(@6.2.2) 的标签仅在非平凡非正规表示中的子项中出现;
2942+规约中,只存在受支持的非平凡非正规的规约表示。
28722943 NPLA 仅支持本节中的以下非平凡非正规的规约表示。
2873-派生实现可另行指定支持的其它非平凡非正规表示。
2874-**注释**
2875-NPLA 实现可假定不符合要求的规约表示不存在。
2944+派生实现可另行指定支持不和以上规则冲突的其它非平凡非正规表示。
2945+除非另行指定,本文档以下的非正规表示指非平凡非正规表示。
2946+**原理**
2947+NPLA 实现可假定不符合要求的规约表示不存在,以确保较高效的实现。
2948+对粘滞位标签的限制确保可使用值数据成员非空判断不是真列表。同时,IsList(@6.2.3) 的实现不需要访问标签。
2949+**注释**
2950+带有粘滞位的标签不在表示一等对象的项的标签中出现,但可在其子项的标签中出现。
28762951 在互操作的意义上(@5.3) ,若实现对表示是否满足本节约定进行检查,不论是否假定存在,都应作为接口约束,以允许派生实现可靠地复用和扩展其它形式的表示。
28772952
2878-@6.3.9.1 子对象引用(@6.8.3.3) :
2953+@6.3.9.1 无环非真列表(acyclic improper list) :
2954+无环非真列表是不属于其它情形的非正规表示可表示的一等对象,具有至少一个子项表示一等对象。
2955+不考虑所有权时,无环非真列表总是能被表示为一个对象和一个非列表对象构成的有序对(pair) 。取得无环非真列表的有序对形式的表示分解这个无环非真列表。
2956+无环非真列表分解后的第二个元素可能继续可被分解。经有限次这样的分解后第二个元素不再是有序对而不能继续分解,得到完全分解。一个无环非真列表的完全分解的对象是它的元素。
2957+推论:无环非真列表最后一个元素不是空列表。
2958+类似真列表(@6.2.1) :
2959+无环非真列表不支持环;
2960+无环非真列表对其中的元素具有所有权,但其中的元素的销毁和释放顺序未指定。
2961+除最后一个元素外,无环无环非真列表的元素依次由表示无环非真列表的项的子项表示。
2962+若最后一个元素能以单一的 ValueObject 表示,则表示无环非真列表的项的值数据成员是这个元素的表示;
2963+否则,表示无环非真列表的项子项中的若干个后缀和值数据成员是这个元素的表示,其中第一个子项的标签具有粘滞位(@6.2.2) 。
2964+**原理**
2965+区分有序对和其它非平凡非正规表示可直接通过检查项中是否存在带有粘滞位的第一个子项。这一操作的时间复杂度是 O(1) 。
2966+**注释**
2967+构造有序对的操作称为 cons ,有序对又称为 cons 对。
2968+若 cons 对的第二个元素是列表,则这个 cons 对是真列表(尽管这个元素不被视为真列表的元素)。
2969+因此,无环非真列表和真列表类似,可通过 cons 逐次构造,区别是逻辑上结尾不含空元素。
2970+cons 对的所有权及初始化和销毁的相对顺序和仍遵循作为其表示的项节点对其子对象的关系。
2971+
2972+@6.3.9.2 子对象引用(@6.8.3.3) :
28792973 值数据成员持有 NPL::TermReference(@6.8.3) 类型的值,保留某个子项的引用。
28802974 子对象引用的项的子项数应为 1 ,该子项持有 shared_ptr 的实例的非空值且其指向的对象和值数据成员持有的 NPL::TermReference 值的 get() 结果应引用同一个项对象。
2881-
2882-@6.3.9.2 带有记号值的非空列表:
2975+**原理**
2976+使用 shared_ptr 的实例间接持有项,以满足项引用(@6.8.3) 对被引用对象作为一等对象的要求。
2977+**注释**
2978+子对象引用的项不表示一等对象,但 shared_ptr 的实例的值指向的项具有一等对象表示(@6.3.4) 。
2979+
2980+@6.3.9.3 带有记号值的非空列表:
28832981 表达式在求值过程中的中间表示可在非空列表的表示的基础上,用值数据成员附加记号值的表示作为辅助信息。
28842982 **注释**
28852983 非空列表满足 IsBranch(@6.2.3) 。
@@ -2887,7 +2985,7 @@
28872985 @6.4 资源管理:
28882986 NPLA 的实现约定统一的资源管理语义,并以此支持存储和对象模型(@5.6) 。
28892987 除非另行指定(如明确资源被共享使用),NPLA API 提供类型的对象按值传递(@4.4.6.5) 传递资源所有权(复制或转移资源)。
2890-违反 NPLA 资源管理要求可引起宿主语言的未定义行为并违反 NPLA 内存安全保证(@5.6.3.3) 。
2988+违反 NPLA 资源管理要求可引起宿主语言的未定义行为(@5.4) 并违反 NPLA 内存安全保证(@5.6.3.3) 。
28912989 基本的资源所有权约束由宿主语言的规则蕴含。
28922990 部分数据结构可有更进一步的上下文相关的约定,如 TermNode 对象在作为被归约项(@6.2) 和被绑定对象(@6.11.1) 时具有不同的规则(参见有关的所有权规则(@6.4.5) )。
28932991 NPLA 还引入称为间接值(indirect value) 的对象访问和管理特定的资源,详见间接值分类和所有权(@6.4.3) 。
@@ -2975,7 +3073,7 @@
29753073 @6.4.5 被规约项(@6.2) 所有权:
29763074 具有值类别(@6.3.1) 的被规约项若指称对象,对应的对象是这个项的项对象(term object) 。项对象是求值结果(@4.1) 或求值的中间结果(@6.3) 对应的对象。
29773075 项对象可能不直接由项自身表示,即可以通过项引用其它途径引入的对象。
2978-基于 @6.4.3 ,为保证内存安全,避免临时对象被引用,仅在泛左值(@5.8.1) 中允许引入被可能引用的间接值。
3076+基于间接值分类和所有权(@6.4.3) ,为保证内存安全,避免临时对象被引用,仅在泛左值(@5.8.1) 中允许引入被可能引用的间接值。
29793077 推论:泛左值的项对象不是临时对象,被环境所有。
29803078 通常纯右值作为其它项的子项而被独占所有权,求值时可能通过临时对象实质化转换(@5.8.4) 标识创建的临时对象(@5.8.5) 。
29813079 为实现临时对象求值和对象所有权(@5.6.2) ,临时对象以项自身作为表示(@6.3) ,被纯右值所有,也间接被其它项所有。
@@ -2998,7 +3096,7 @@
29983096 @6.4.6.1 引用值(@6.3.3) 作为间接值:
29993097 由于左值(@5.8.1) 的项对象被环境所有(@6.4.5) ,在项上的求值需要其它项对象作为中间值。
30003098 这种中间值通过间接引用左值以确保左值标识的对象可作为一等对象(@4.1) 使用,也是一种间接值,即引用值(@6.3.3) ,是引用(@4.2.3) 的实例。
3001-NPL::TermReference(@6.8.3) 类型是可能需要区分引用与被引用对象的值的内部表示。
3099+NPL::TermReference(@6.8.3) 类型可能需要区分引用与被引用对象的值的内部表示。
30023100 这类似宿主语言的对象类型的左值引用和右值引用的差异(@6.3.3) 。
30033101 消亡值(@5.8.1) 以作为其表示的引用项(@6.3.3) 内部的 TermTags::Unique 标签(@6.2.2) 标记,即唯一引用。
30043102 基于引用的左值到右值转换(@5.8.4) 可通过 NPL::ReferenceTerm(@6.9.3) 实现。
@@ -3091,8 +3189,9 @@
30913189 @6.5.1 助手函数:
30923190 下列可能重载的函数帮助抛出异常:
30933191 NPL::ThrowInsufficientTermsError
3192+NPL::ThrowListTypeErrorForAtom
30943193 NPL::ThrowListTypeErrorForInvalidType
3095-NPL::ThrowListTypeErrorForNonlist
3194+NPL::ThrowListTypeErrorForNonList
30963195 NPL::ThrowTypeErrorForInvalidType
30973196 部分异常的消息由 NPLA 辅助项访问操作(@6.9.2) 格式化。
30983197
@@ -3171,17 +3270,20 @@
31713270 不论必要性,被续延捕获强引用是可接受的,因为上下文的切换已修改了上下文的当前环境,在对象语言程序中不能作为一等对象通过创建绑定而构造循环引用。
31723271
31733272 @6.8.3 项引用(term reference) :
3174-宿主语言中的 NPL::TermReference 是项引用。
3273+宿主语言中,类 TermReference 是项引用。
31753274 项引用是泛左值(即临时对象引用以外的引用值)的内部表示的值类型对象的宿主值(@6.3.5) 。
31763275 **注释** 临时对象不使用项引用,而直接使用具有临时对象标签的项表示(@6.3.2) 。
31773276 其中,子项只被子对象引用(@6.8.3.3) 的情形使用。
3277+通过项引用可访问引用值的被引用对象,即间接操作(@6.9.3) 。
3278+引用值的被引用对象(@4.2.3) 应为一等对象(@4.2.1) 或被绑定对象(@5.7.1) 。
3279+**原理**
31783280 子对象引用使用的这种形式的表示通常因为需要更多的操作比其它引用值的类似操作低效,但这种表示可避免依赖宿主语言中的本机对象内部表示(如成员布局)的依赖。
31793281 一般地,在 C++ 的意义上不存在能满足语义的更有效的可移植表示,所以这种表示在和宿主语言的互操作(@5.3) 上是必要的。
3180-**原理**
31813282 临时对象引用不使用项引用表示,允许生存期延长(@5.8.5.4) 引入的临时对象不跟随引用项(@6.3.3) 而作为被绑定对象在环境中保存。
31823283 这同时简化动态 PTC(@5.10.4.2) 的实现。当前实现参见 TCO 临时对象管理(@7.10.7) 。
31833284 **注释**
31843285 对 NPL::TermReference 和可能使用 NPL::TermReference 访问项的相关操作详见项引用和项引用访问操作(@6.9.3) 。
3286+因为被引用对象是一等对象(@4.2.1) 或被绑定对象,被引用对象的表示在去除临时对象标签后是一等对象表示(@6.3.2) 。
31853287
31863288 @6.8.3.1 项引用的元数据(@4.2.3.3.1) :
31873289 NPL::TermReference 包含一些附加的只通过引用形式访问对象的元数据作为优化设计(@4.2.3.3.2) ,包括标签(@6.2.2) 和关联的环境。
@@ -3205,11 +3307,14 @@
32053307 由被转发(@5.8.2.2) 表达式的值确定:除上述条件外,表示绑定临时对象的引用值(具有临时对象标签(@6.2.2) )的项蕴含被引用对象(@4.2.3) ,也是可转移的(用例如 @10.5 )。
32063308
32073309 @6.8.3.3 子对象引用:
3208-特定的引用值是子对象引用(subobject reference) ,其被引用对象是被另一个对象所有的、作为这个对象的子对象(@5.6.6) 的非一等对象。
3209-子对象引用通过一个引用值和它的子对象构造。
3310+特定的引用值是子对象引用(subobject reference) ,其被引用对象是被另一个对象所有的、作为这个对象的子对象(@5.6.6) 的一等对象。
32103311 子对象引用的表示详见非平凡非正规表示(@6.3.9) 。
3312+**原理**
32113313 子对象引用使用的这种形式的表示通常因为需要更多的操作比其它引用值的类似操作低效,但这种表示可避免依赖宿主语言中的本机对象内部表示(如成员布局)的依赖。
32123314 一般地,在 C++ 的意义上不存在能满足语义要求且总是更简单高效的可移植表示,所以这种表示在和宿主语言的互操作上是必要的。
3315+子对象引用的表示使用项引用,因此要求被引用对象具有一等对象表示。这同时允许子对象引用的被引用对象被提升得到一等对象。
3316+**注释**
3317+典型地,子对象引用通过一个引用值构造,得到引用值的被引用对象的子对象的引用。
32133318
32143319 @6.9 上下文无关节点处理 API :
32153320 NPLA 实现提供不依赖一般项规约逻辑(@5.8) 的公共 TermNode(@6.2) 操作 API 。
@@ -3217,12 +3322,15 @@
32173322
32183323 @6.9.1 规约合并项 API :
32193324 规约合并项是满足以下结构约束的被规约项(@6.2) 之一:
3220-具有子项的枝节点(@6.2.1) ;
3221-符合作为带有记号值的非空列表(@6.3.9.2) 的非平凡非正规表示。
3325+ 具有子项的枝节点(@6.2.1) 。
3326+ 特定的非平凡非正规表示(@6.3.9) 之一:
3327+ 符合作为带有记号值的非空列表(@6.3.9.3) 。
3328+ 无环非真列表(@6.3.9.1) 。
32223329 这些条件可使用函数 NPL::IsCombiningTerm 判断。
32233330 函数 NPL::ClearCombiningTags 清除可能在规约合并项中遗留的标签而使之能作为一等对象的值的表示(@6.3) 。
32243331 **原理**
32253332 规约合并项支持求值算法(@4.4.1) 中函数合并(@4.5.3) 的实现。
3333+对一般的规约合并,通常不直接蕴含 NPL::ClearCombiningTags 调用。对标签的维护通常延迟到正规化操作(@6.10.6.1) 实现。
32263334 **注释**
32273335 具有子项的枝节点满足 IsBranchedList(@6.2.3) 。
32283336 利用规约合并项的一个实例参见 NPLA1 求值算法(@7.8.2) 。
@@ -3255,10 +3363,10 @@
32553363 决定替换变换是提升的条件为:被提升的项(源)是提升后得到的项(目标)的一个直接、间接子项或项的子对象变换得到的项。
32563364 **注释** 以树表示项则为枝节点被子节点经变换后取代。项的子对象变换的一个例子是通过值数据成员保存的项引用(@6.8.3) 得到表示被引用对象的项。
32573365 这可以视为作为语法变换的消去 λ 抽象的 lambda 提升(详见 https://en.wikipedia.org/wiki/Lambda_lifting )的一般化,但此处和 λ 抽象没有直接关联。
3258-被提升的项往往被转移,因此一般地,需要在宿主语言中可修改(@4.1.4.2) 。若被提升的项表示对象语言的值,一般也需要在对象语言中可修改。
32593366 提升时可能进行检查,包括为满足接口行为的语义检查和实现为预防宿主语言的未定义行为的附加检查(@5.4) 。
32603367 提升项对象通过变换操作取作为项的值数据成员(@6.2) 。在此基础上有递归版本。
32613368 除非另行指定,提升项修改被替换的项的内容,其中子项、值数据成员和标签(@6.2.2) 都可能被修改。
3369+**注释** 被提升的项往往被转移,因此一般地,需要在宿主语言中可修改(@4.1.4.2) 。若被提升的项表示对象语言的值,一般也需要在对象语言中可修改。
32623370 提升项可引入或消除(@6.4.6) 间接值(@6.4.3) 。
32633371 提升项通过被引用的对象替换作为项的值数据成员的引用值而消除引用值(@5.8.3.4) ,但不保证修改其中引用的元数据(@6.8.3.1) 。
32643372 提升项的求值结果(@6.10.1) 是消除引用值的结果。
@@ -3275,6 +3383,12 @@
32753383 当提升操作的被提升和被替换的项都是对象语言中一等对象的值的表示(@6.3) 时,不引入非一等对象的值的表示。
32763384 转移提升操作的第一参数在提升后保证表示一等对象。
32773385 **注释** 转移提升状态通常用于直接实现对象语言中的操作。
3386+提升项的操作可能具有可被忽略的表示提升后中间状态的返回值。否则,返回类型是 void 。
3387+**注释**
3388+提升操作作用于项。被提升的项可能有具体的前置条件约束;提升可能满足特定的后置条件。
3389+例如,一些项的提升操作预期被提升的项具有一等操作表示(@6.3) 。
3390+不是所有项都满足前置条件,使用不适当的提升可能破坏接口约束。
3391+为避免误用,一般地,尽量使用和特定的项相关的提升操作而非作用于一般的项的提升操作。
32783392
32793393 @6.9.4.1 基本提升操作:
32803394 基本提升操作包括直接转移赋值及以下 API :
@@ -3293,25 +3407,38 @@
32933407 以下值操作可能引入间接值:
32943408 函数 NPL::LiftCollapsed
32953409 函数 NPL::MoveCollapsed
3296-函数 NPL::LiftTermRef 提升项引用:提升项的内容为参数指定的项或值的引用值(@6.3.3) 。
3297-引用值通过参数指定的值对象(@6.2) 上创建得到。
3410+函数 NPL::LiftTermRef 提升项引用:提升项的内容为参数指定的项或引用持有者(@6.4.3.3) 构成的间接值。
32983411 函数 NPL::LiftPropagatedReference 提升引用项(@6.3.3) 为引用项,实现不可修改标签的传播(@6.8.3.1) 。
32993412 函数 NPL::LiftToReference 提升项对象为引用。
33003413 若项对象表示引用值则提升项,否则对 ValueObject 进行基于 ValueObject 所有权的检查(间接进行生存期检查)并取引用这个项的引用值。
33013414 运行时进行的检查类似于强制 C++ 的一元 & 对表达式值类别(另见 @4.2.3 )的要求但更严格(尽管仍然不能保证避免未定义行为),避免临时对象(@5.8.5) 被保存为引用值。
33023415
33033416 @6.9.4.3 消除中间值的提升操作:
3304-函数 NPL::LiftMoved
3305-函数 NPL::LiftMovedOther
3306-函数 NPL::LiftToReturn
3307-函数 NPL::MoveRValueToReturn
3308-函数 NPL::MoveRValueToForward
3309-函数 NPL::LiftSubtermsToReturn
3417+以下函数可消除参数中的中间值:
3418+NPL::LiftMoved
3419+NPL::LiftMovedOther
3420+NPL::LiftToReturn
3421+NPL::MoveRValueToReturn
3422+NPL::MoveRValueToForward
3423+NPL::LiftElementsToReturn
3424+NPL::LiftPrefixToReturn
3425+NPL::LiftSubtermsToReturn
3426+NPL::LiftSuffixToReturn
33103427 本节的函数作用在被规约项时,对引用值的操作实现临时对象实质化转换(@5.8.4) 所在的求值规约的最后部分的操作,一般在尾上下文(@4.4.8) 中进行处理。
33113428 提升消除中间值只作用在被提升项,不作用在子项。需要递归复制的操作不在这里提供,以避免抽象泄漏。
33123429 为消除中间值需要进行复制消除(@5.8.5.3) ,其中使用不同的可转移条件(@5.8.3.6) 决定使用转移而非复制。
33133430 当前实现在转移项时使用的宿主转移操作总是使用宿主对象的转移(基于 NPL::SetContentWith(@6.2.3) )而不是 TermNode 的转移。
3431+在 TermNode 的操作以外,遍历多个子对象的复制操作不满足强异常安全,这包括:
3432+NPL::LiftElementsToReturn
3433+NPL::LiftPrefixToReturn
3434+NPL::LiftSubtermsToReturn
3435+NPL::LiftSuffixToReturn
33143436 另见间接值的消除(@6.4.6.3) 。
3437+**原理**
3438+对象语言的语义不需要支持递归复制。
3439+多个一等对象的复制不保证强异常安全。参见 NPLA API 异常安全保证(@6.1.4.2) 。
3440+**注释**
3441+对表示单一一等对象的项目的复制仍然基于 TermNode 上的操作提供强异常安全保证。
33153442
33163443 @6.9.4.4 辅助提升操作:
33173444 函数 NPL::LiftFirst 和 NPL::LiftLast 提升第一个和最后一个子项。
@@ -3564,7 +3691,7 @@
35643691 NPLA 提供维护程序运行状态的数据结构的接口,包括环境、上下文和相关的其它 API 。
35653692
35663693 @6.11.1 环境数据结构:
3567-类 Environment(@6.11.1.1) 表示环境记录(@5.7.1) 。
3694+NPL 中定义的类 Environment(@6.11.1.1) 表示环境记录(@5.7.1) 。
35683695 环境对象(@5.7.1) 以类 Environment 相关的类型表示。
35693696 显式的数据结构直接支持求值环境是一等环境(@5.7) 。
35703697 TermNode 对象在名称绑定映射中表示被绑定对象(@5.7) 。
@@ -3628,7 +3755,7 @@
36283755 NPL::Reduer 能以兼容 ReducerFuntionType 的方式调用,可选提供一个 ContextNode& 参数。
36293756
36303757 @6.11.3 上下文数据结构:
3631-上下文(@4.6) 使用 ContextNode 类表示。
3758+上下文(@4.6) 使用类 ContextNode 表示。
36323759 ContextNode 对象保存上下文相关的公共状态。
36333760 上下文的公共状态包括当前处理的环境的强引用(@5.7.2) ,指定活动记录中当前的处理的帧(@4.5.3.4) ,表示对象语言中对应当前处理的帧的当前环境(@5.7.3) 。
36343761 环境强引用决定上下文对环境具有共享所有权(@5.6) ,通过环境对象的引用可以取指向它的引用(@6.11.1.1) 。
@@ -4084,7 +4211,7 @@
40844211 命名空间 NPL::A1 提供了特定于 NPLA1 的 API 。以下命名空间 A1 指 NPL::A1 。
40854212 命名空间 A1::Forms 提供了 NPLA1 的语法形式(@3.4.3) 对应的功能的实现(@8.4) 。
40864213 以下限定名称中,无歧义的嵌套限定命名空间使用非嵌套类型名称(如 Forms 指 A1::Forms )。
4087-除此以外,本章以 @6.1.4 约定的限定名称的方式使用 API 中出现的 C++ 名称和项目模块命名空间。
4214+除此以外,本章以 NPLA 总体架构(@6.1.3) 约定的限定名称的方式使用 API 中出现的 C++ 名称和项目模块命名空间。
40884215
40894216 @7.1 NPLA1 一般约定:
40904217 在 NPLA 的一般约定(@5.2) 外,本节概述不通过具体 API 指定的一般特性。
@@ -4095,7 +4222,7 @@
40954222 和 NPLA 不同,这些选项主要用于提供参考实现,不应被直接作为公开接口;通常情形不需要更改。
40964223 特定的 API 可间接依赖这些特性,以便派生实现提供不同的优化实现,包括:
40974224 A1::REPLContext::IsAsynchronous(@7.8.1)
4098-部分 NPLA 安全保证(@6.1.3) 依赖:
4225+部分 NPLA 安全保证(@6.1.4) 依赖:
40994226 特定的实现选项支持(如 NPL_Impl_NPLA1_Enable_ThunkedSeparatorPass(@7.9.2) ),当没有启用时可能不满足。
41004227 被调用的互操作(@5.3) 实现支持相应的安全保证。
41014228 **注释**
@@ -4177,13 +4304,13 @@
41774304 @7.1.4.3 非内存安全项:
41784305 引用持有者(@6.4.3.3) 仅存在有限的求值中间过程中,调用引入间接值 NPLA API(@5.6.3.2) 在 NPLA1 实现中可能仍能保证内存安全。
41794306 NPLA1 不直接调用非内存安全的提升操作(@6.9.4.5) ,除非之后立即同步地消除访问时无法保证内存安全的结果(@6.9.4.3) 。
4180-提升引入引用值和消除引用值通常使用 NPLA API :消除引用值的实现可按 @6.4.6.3 调用;引入和消除间接值也可使用 @6.9.4.1 的 API 。派生实现也可调用这些函数处理引用持有者。
4181-其它 NPLA1 非内存安全操作(@5.6.3.1) 包括实现中调用的明确取无所有权引用的 API ,参见 @8.1.2 关于间接值的约定以及 @8 中具体函数的描述。
4307+提升引入引用值和消除引用值通常使用 NPLA API :消除引用值的实现可间接值的消除(@6.4.6.3) ;引入和消除间接值也可使用基本提升操作(@6.9.4.1) 。派生实现也可调用这些函数处理引用持有者。
4308+其它 NPLA1 非内存安全操作(@5.6.3.1) 包括实现中调用的明确取无所有权引用的 API ,参见关于间接值的约定(@8.1.2) 以及 @8 中具体函数的描述。
41824309 NPLA1 对后者的使用可选地使用 NPLA 运行时检查(@6.1.2.1) 补充基于项引用(@6.8.3) 和环境的引用计数(@6.11.3.1) 的检查机制(如 @8.4.5.2 )。
41834310
41844311 @7.1.5 互操作约定:
41854312 非 NPLA1 实现提供的类型的宿主 == 操作不要求支持嵌套调用安全(@5.11) 。
4186-作为嵌套调用安全的约定(@6.1.3) 的扩展,若存在 == 操作不支持嵌套调用安全的类型,具体类型由派生实现的定义。
4313+作为嵌套调用安全的约定(@6.1.4) 的扩展,若存在 == 操作不支持嵌套调用安全的类型,具体类型由派生实现的定义。
41874314 **原理**
41884315 调用假定异步规约不存在的实现允许使用直接风格(@6.12) 代替异步调用(@6.11.3) ,一般能显著节约调用开销,但对非特定类型,这不保证提供(在宿主语言无法提供的)嵌套调用安全保证。
41894316 本机实现的 == 因为宿主语言的接口缺乏一般异步调用支持,不提供在宿主语言无法提供的嵌套调用安全保证。
@@ -4239,7 +4366,8 @@
42394366 A1::ThrowNonmodifiableErrorForAssignee
42404367 A1::ThrowUnsupportedLiteralError
42414368 A1::ThrowValueCategoryError
4242-一般地,对预期左值的操作数项,使用等价 Forms::CheckListReference(@8.4.7) 的检查而不是 NPL::ThrowInsufficientTermsError(@6.9.2) 或错误处理例程。
4369+**注释**
4370+一般地,对预期有序对左值的参数,使用等价 Forms::CheckPairReference(@8.4.7) 的检查而不是 NPL::ThrowInsufficientTermsError(@6.9.2) 或错误处理例程。
42434371
42444372 @7.4 规约实现:
42454373 NPLA1 在 NPL::A1 中提供不同粒度的规约和求值实现 API 。
@@ -4278,9 +4406,15 @@
42784406 A1::Reduce(@7.4.4) 满足迭代顺序(@7.4.5) 提供的保证优先检查子节点数,列表遍仅用于子节点数大于 1 的非叶节点(@6.2.1) 求值。
42794407 这个特性允许在对象语言的对应确定被求值的子项的表达式外添加匹配的括号而不改变语义,且在列表遍中可简化子项求值(如 @7.6.4 直接使用 ReduceCombined(@7.6.4.2) 而不需检查节点数量是否大于 1 )。
42804408 一般列表遍不依赖以上假设,可能对叶节点或具有一个节点的平凡列表节点对应的表达式求值。因此列表遍不是枝节点(@6.2.1) 遍。
4281-遍的调用不一定需要维护共享状态,也因此一般多个遍被调用时不保证求值的强异常安全;其它情形仍然遵守一般约定([Documentation::YFramework @@3.2]) 。
4409+遍的调用不一定需要维护共享状态,因此一般多个遍被调用时,不保证求值中异常时的状态完全确定;但除非另行指定,仍满足 NPLA API 异常安全保证(@6.1.4.2) 。
4410+当前实现中,若实现包含以下操作实现,则不提供强异常安全保证:
4411+涉及多个一等对象的复制。
42824412 除合并子调用创建操作(@7.6.4.2) ,NPLA1 规约实现不保证进行正规化操作(@6.10.6.1) :
42834413 在正规化操作不保证存在时,实现列表遍的用户代码需注意清理(@6.10.2.1) 不需要的子项。
4414+**原理**
4415+参见 NPLA API 异常安全保证。
4416+**注释**
4417+不提供事务性保证的典型例子之一是消除中间值的提升操作(@6.9.4.3) 中对多个子节点表示的一等对象的复制。
42844418
42854419 @7.4.1.5 规约函数实现约定:
42864420 关于规约函数(@6.10.5) 的基本约定,参见规约函数形式约定(@6.10.5) 。
@@ -4314,7 +4448,7 @@
43144448 进入来自同一具现的一次续延的任何副本只允许一次显式(续延应用)或者隐式(如被函数调用的返回蕴含)的调用时,共享状态都应被标记。
43154449
43164450 @7.4.3 NPLA1 上下文状态:
4317-类 A1::ContextState 是 ContextNode 的 public 派生类,其对象表示 NPLA1 上下文状态,包含 NPLA 上下文的状态(@6.11.3) 。
4451+类 ContextState 是 ContextNode 的 public 派生类,其对象表示 NPLA1 上下文状态,包含 NPLA 上下文的状态(@6.11.3) 。
43184452 A1::ContextState 包含作为之后的异步求值规约动作(@6.12.1) 使用的当前项(@6.10) 的下一求值项。
43194453 NPLA1 上下文状态提供以下的供迭代的公共遍:
43204454 守卫遍(guard pass) ,类型是一个 GuardPasses(@7.4.1.3) ,用于提供调试回调等。
@@ -4348,7 +4482,7 @@
43484482 对支持异步规约的实现(@7.9.2) ,上述处理器还应符合和同步规约实现兼容性(@7.9.1) 中的规则。
43494483 A1::ReduceOnce 和辅助规约函数(@6.10.7) 类似,能处理一般的项,没有附加对项的内容的要求的前置条件。参数中 TermNode(@6.2) 表示的表达式语法意义(@3.4.2) 上非空,但没有实现限制(即不附加检查)。
43504484 A1::ReduceOnce 循环调用遍(@7.4.1) 即迭代规约。迭代规约响应必要的 ReductionStatus::Retrying 规约状态支持重规约(@6.10.1) 的实现,但在此仅处理规约实现的内部公共状态。具体的重规约由遍(@7.4.1) 的调用实现。
4351-对异步规约中立的规约(包括遍的实现和 NPL 中如 ContextNode::RewriteLoop(@6.11.3.1) 这样的调用规约函数的重写入口)同时对重规约中立。默认实现中,仅有公共的叶遍(@7.4.3) 的实现直接实现重规约。这种实现方式是同步的,即判断需要重规约后不使用异步规约(@6.12) ,但仍保证嵌套调用安全(@6.1.3) 。
4485+对异步规约中立的规约(包括遍的实现和 NPL 中如 ContextNode::RewriteLoop(@6.11.3.1) 这样的调用规约函数的重写入口)同时对重规约中立。默认实现中,仅有公共的叶遍(@7.4.3) 的实现直接实现重规约。这种实现方式是同步的,即判断需要重规约后不使用异步规约(@6.12) ,但仍保证嵌套调用安全(@6.1.4.1) 。
43524486 遍的同步实现一般通过 NPL::CheckReducible(@6.10.3) 判断是否需要同步的重规约。这同时处理 ReductionStatus::Partial ,同步已被异步规约的部分规约。
43534487 在具体操作的异步规约实现中实现的重规约,参见异步规约基本支持(@7.9.2.1) 和异步重规约(@7.10.8) 。当前默认实现中,这由列表遍(@7.4.3) 调用。
43544488 A1::ReduceOnce 的实现中不直接指定需要重规约;所有 ReductionStatus::Retrying 都来自遍的调用。
@@ -4526,9 +4660,9 @@
45264660 调用处理器前:
45274661 判断项满足 NPL::IsCombiningTerm(@6.9.1) ,否则直接返回 ReductionStatus::Regular(@6.10.1) 。
45284662 通过判断节点的值数据成员(@6.2) 是否持有 A1::ContextHandler 类型的值或引用到 A1::ContextHandler 的值上的 NPL::TermReference 的值,确定节点中是否存在上下文处理器。
4529- **注释** 这可能是 A1::ContextHandler 对象的子对象引用(@6.8.3.3) ,符合平凡非正规表示(@6.3.9.1) 。
4663+ **注释** 这可能是 A1::ContextHandler 对象的子对象引用(@6.8.3.3) ,符合平凡非正规表示(@6.3.9.2) 。
45304664 若不存在这样的值,则抛出 NPL::ListReductionFailure 异常(@6.5) 。
4531- 其中,带有记号值的非空列表(@6.3.9.2) 中的记号值被视为函数名,构成错误消息。
4665+ 其中,带有记号值的非空列表(@6.3.9.3) 中的记号值被视为函数名,构成错误消息。
45324666 **原理** 因为失败包含子项是列表的情形,此时不抛出 NPL::ListTypeError(@6.5) 。这也避免提示错误时,和具体的上下文处理器的具体宿主类型(当前为 A1::ContextHandler )耦合。
45334667 对上下文处理器是左值和右值的情形,分别清空和设置被规约项的标签的 TermTags::Temporary(@6.2.2) 。
45344668 **原理** 被调用的处理器必要时可利用它作为提示进行优化。
@@ -4587,7 +4721,7 @@
45874721 部分求值规约操作使用同返回值转换(@6.4.6.4) 效果相同的方式实现消除引用值(@6.9.4) :
45884722 A1::RelayForCall 和 A1::RelayForEval(@7.7.2) 间接依赖 NPL::LiftToReturn(@6.9.4.3) 。
45894723 对间接值的相关使用和检查另见 @7.6.5 。
4590-非引用值间接值(@6.4.6.2) 对应复制或转移间接值参见 @8.1.2 。
4724+非引用值间接值(@6.4.6.2) 对应复制或转移间接值参见关于间接值的约定(@8.1.2) 。
45914725 关于具体操作,另见 @8 中的保证,如过程抽象(@8.4.5) 。
45924726
45934727 @7.7.3 绑定操作:
@@ -4599,21 +4733,28 @@
45994733 绑定操作匹配待绑定的形式参数和操作数或它们的子项。
46004734 成功的匹配决定形式参数对应的操作数或其子项,作为其实际参数(@4.5.3) 。这种对应关系是单射但不一定是满射,即匹配成功后,每个参数总存在对应的操作数或其子项,而操作数和子项允许不对应形式参数而被忽略。
46014735 NPLA 形式参数树的叶节点为符号(@6.8.1) 或其引用值(@6.3.3) 。不符合要求的对象构造形式参数树时可引起错误(@2.5.2) 。
4602-被绑定的项的操作数是待匹配的项的子项(而不会是此项自身,满足 @6.10.5 )。
4603-被绑定的参数可以作为函数的形式参数(@4.5.2.1) ,也可以其它初始化变量的语法构造的基础。
4604-和 Kernel 类似,形式参数树是 DAG ,但通过真列表(@6.2.1) 的性质蕴含而不需要另行限制。形式参数树中的节点在匹配时被视为右值。
4605-和 Kernel 不同,操作数树同时支持作为引用的左值(@5.8.1) 和非引用的右值(@5.8.1) ,在实现上需要解析引用(类似 @6.9.2 的部分操作)。
4736+被绑定的项的操作数中的元素或值数据成员对应是项中的元素。
4737+形式参数树是 DAG 。
4738+形式参数树中的引用值可能被间接访问一次,其余元素在匹配时被视为右值。
46064739 绑定操作的 API 详见绑定支持(@7.7.4) 。
46074740 绑定操作符合以下小节的绑定规则。其它具体行为参见对应 API (@7.7.4) 的详细描述。
4741+**原理**
4742+被绑定的参数可以作为函数的形式参数(@4.5.2.1) 。绑定操作对形式参数的处理也可以作为其它初始化变量的语法构造的基础。
4743+**注释**
4744+DAG 要求和 Kernel 类似,但通过无环列表(@6.2.1) 的性质蕴含而不需要另行限制。
4745+和 Kernel 不同,操作数树同时支持作为引用的左值(@5.8.1) 和非引用的右值(@5.8.1) ,在实现上需要解析引用(类似 @6.9.2 的部分操作)。
46084746
46094747 @7.7.3.1 绑定初始化:
4610-类似宿主语言,绑定的对象节点的值和子节点元素被复制初始化。
4748+绑定的对象节点的值和子节点元素被复制初始化。
46114749 绑定时不对形式参数对应的函数参数进行修改,所以不对形式参数除访问引用值外的间接处理。
4612-若形式参数可能由求值得到,需在匹配前另行处理。
4613-除非另行指定,不同变量的绑定初始化之间无序(@4.4.3) 。
4614-**注释**
4750+绑定前不对形式参数或实际参数中的元素求值。
4751+除非另行指定,不同变量的绑定初始化之间非决定性有序(@4.4.3) 。
4752+**注释**
4753+初始化元素类似宿主语言的参数传递中可发生初始化。
4754+若形式参数或实际参数可能由求值得到,需在匹配前另行处理。
46154755 由非决定性规约规则(@4.4.3) ,一般地,变量仅通过初值的求值决定的依赖关系(@4.4.6) 及子对象(@5.6.6) 决定之间初始化的相对顺序。
4616-即使初值符位于相邻的语法构造,也没有隐含顺序;这和宿主语言不同。
4756+因为绑定的初始化不负责实际参数的求值,一般地,即使初值符位于相邻的语法构造,也不保证隐含顺序;这和宿主语言不同。
4757+初始化的顺序规则和宿主语言初始化不同的函数参数类似。
46174758
46184759 @7.7.3.2 绑定临时对象(@5.8.5.1) 标签:
46194760 绑定临时对象对标签的处理(@6.2.2.2) 对应宿主语言中的转发引用参数中的情形(@6.2.2) :
@@ -4657,7 +4798,7 @@
46574798 **注释**
46584799 函数合并构成的操作数树包括作为合并子的第一个子项和作为操作数的之后余下的子项。
46594800 数据结构和匹配算法类似 Kernel 中用于 $define! 和 $vau 等操作子(@4.5.3.2) 的递归的匹配机制,但有以下不同(另见 @9.9.5 ):
4660-不支持 cons 对(pair) 的中缀 . ,但支持形式参数树中的列表最后以带省略的符号匹配多个列表项的参数,绑定结尾列表(trailing list) ;
4801+不支持 cons 对(@6.3.9.1) 的中缀 . ,但支持形式参数树中的列表最后以带省略的符号匹配多个列表项的参数,绑定结尾列表(trailing list) ;
46614802 对参数子项的符号中可选的 . 起始以及之后可选的前缀作为标记字符作为引用标记进行处理;
46624803 不提供转义,若符号去除可选的前缀及标记字符 . 后为空则忽略绑定;
46634804 若参数子项按引用传递(@4.4.6.5) 则间接访问并绑定被引用项内容;
@@ -4679,14 +4820,14 @@
46794820 不存在标记字符时,对操作数按值绑定,实际参数值传递给对应的形式参数。
46804821 若实际参数是泛左值(@5.8.1) ,则实际参数上首先隐含左值到右值转换(@6.4.6.1) 。
46814822 存在标记字符 % 或 & 时,按引用推断规则直接绑定或转发操作数。
4682- 当实际参数是引用值时,在可能对其它标签进行的处理(@7.7.3.5) 后,隐含一次引用值的消除(@5.8.3.4) 。
4823+ 当实际参数是引用值时,在可能对其它标签进行的处理(@7.7.3.5) 后,隐含一次引用折叠(@5.8.3.5) 。
46834824 存在标记字符 @ 时,绑定以实际参数作为被引用对象(@4.2.3) 的引用值,不论操作数的类型和值类别。
4684- 初始化引用值时,没有引用折叠(@5.8.3.5) 或引用值的消除。
4825+ 初始化引用值时,没有引用值的消除(@5.8.3.5) 。
46854826 当前暂时不支持修改被绑定操作数。支持修改操作数的绑定的其它标记字符可能在未来支持。
46864827
46874828 @7.7.3.5 非递归绑定:
46884829 非递归绑定在一次匹配之后创建对应的变量绑定。
4689-合并使用或不使用引用标记字符(@7.7.3.4) 的情形,非结尾列表(@7.7.3.3) 的单一参数对象的绑定初始化包含以下过程:
4830+合并使用或不使用引用标记字符(@7.7.3.4) 的情形,非结尾序列(@7.7.3.3) 的单一参数对象的绑定初始化(@7.7.3.1) 包含以下过程:
46904831 若不存在绑定标记字符 @ ,则:
46914832 若操作数为可转移的(@5.8.3.6) 对象的引用值,则被绑定对象是按以下规则初始化的蕴含隐含的引用折叠(@7.7.3.4) 的引用值:
46924833 存在标记字符时,使用引用推断规则(@7.7.3.4) ,被绑定对象是操作数直接初始化的项引用,其标签由操作数的(引用值)的标签决定:
@@ -4771,7 +4912,7 @@
47714912 对实际参数,检查尾部参数是否为省略(@7.7.3.3) 后,匹配前判断是否为 TermNode 以支持引用(@6.4.6.3) 。
47724913 绑定尾部参数列表时,若对应的操作数是右值,直接转移子项(@7.7.3.6) 。
47734914 一般地,对操作数树的递归操作应在分配资源失败时,引起(可能派生)std::bad_alloc 的宿主异常而非宿主语言的未定义行为(@5.4) 。
4774-为支持嵌套调用安全的约定(@6.1.3) ,实现应避免无法预测嵌套深度的递归调用,但实现内调用的重定向操作以尾调用形式进行递归调用,类似使用跳板的异步规约动作(@6.12.1) 。
4915+为支持嵌套调用安全的约定(@6.1.4) ,实现应避免无法预测嵌套深度的递归调用,但实现内调用的重定向操作以尾调用形式进行递归调用,类似使用跳板的异步规约动作(@6.12.1) 。
47754916 引入绑定的规则参见绑定操作(@7.7.3) 。
47764917 另见环境修改操作(@8.4.4.3) 和过程抽象(@8.4.5) 。
47774918 函数 A1::BindParameterWellFormed
@@ -4825,7 +4966,7 @@
48254966 以下各节描述描述 NPLA1 规范求值算法的局部性质。
48264967 **原理**
48274968 NPLA1 规范求值算法和 [RnRK] 的求值算法具有近似的简单性。
4828-因为 NPLA1 不支持存在环的非真列表(@6.2.1) ,cons 对的描述被对应替换。
4969+因为 NPLA1 不支持存在环的非真列表(@6.2.1) ,cons 对(@6.3.9.1) 的描述被对应替换。
48294970 求值算法使用的环境同 [RnRK] 。
48304971 同 [RnRK] ,而非 [RnRS] ,NPLA1 规范求值算法避免对顶层(top-level) 的特殊引用,以避免上下文相关性的不同规则带来的复杂性和限制。
48314972 使用顶层的不同求值规则的限制可能简化一些编译实现需要的假设。但这泄漏了抽象,且在实际使用中引起的大量问题,如 https://gist.github.com/samth/3083053 。
@@ -4895,7 +5036,7 @@
48955036 @7.9.2 异步规约 API 支持:
48965037 部分 NPLA1 API 提供同步和异步的不同实现。一些 API 明确支持异步规约。
48975038 启用异步规约的实现定义为实现选项(@7.1) NPL_Impl_NPLA1_Enable_Thunked 。
4898-此外,启用分隔符中缀变换(@7.5.2) 的异步的实现定义为实现选项 NPL_Impl_NPLA1_Enable_ThunkedSeparatorPass ,使用单独的异步实现保证嵌套调用安全(@6.1.3) 。
5039+此外,启用分隔符中缀变换(@7.5.2) 的异步的实现定义为实现选项 NPL_Impl_NPLA1_Enable_ThunkedSeparatorPass ,使用单独的异步实现保证嵌套调用安全(@6.1.4.1) 。
48995040 NPL::NPLA1Forms(@8.1) 等同时支持同步和异步规约的 API 可基于这些 API 实现。
49005041
49015042 @7.9.2.1 异步规约基本支持:
@@ -4982,8 +5123,8 @@
49825123 直接在实现中变换宿主语言的递归调用为循环可实现宿主 TCO(@5.10.4.2) ;
49835124 基于异步规约 API(@7.9.2) ,使用重写规则(直接通过现有 API 支持的即重规约(@7.4.4) )代替直接使用宿主语言的递归形式可实现目标 TCO(@5.10.4.2) ;
49845125 动态 TCO(@5.10.4.2) ,具体机制详见 TCO 操作复用(@7.10.4) 。
4985-NPLA 除个别例外(@6.1.3) 均支持宿主 TCO ,不需要其它形式的静态 TCO(@5.10.4.2) 。
4986-在此基础上,NPLA1 通过允许和动态 TCO 互操作的异步重规约的方式支持目标 TCO(@7.10.8) ;但当前 NPLA1 部分 API 不保证支持宿主 TCO(@6.1.3) ,这可能在以后完善。
5126+NPLA 除个别例外(@6.1.4) 均支持宿主 TCO ,不需要其它形式的静态 TCO(@5.10.4.2) 。
5127+在此基础上,NPLA1 通过允许和动态 TCO 互操作的异步重规约的方式支持目标 TCO(@7.10.8) ;但当前 NPLA1 部分 API 不保证支持宿主 TCO(@6.1.4) ,这可能在以后完善。
49875128 NPLA1 支持的动态 TCO 按实现策略即 TCM(@5.10.4.2) 。
49885129 综上,使用 NPLA1 的 PTC 有三类实现策略:
49895130 通过直接重写宿主语言的实现(使用循环代替部分递归)提供宿主 TCO ;
@@ -5316,7 +5457,7 @@
53165457 当前实现并没有显式支持 evlis tail recursion(@5.10.3) ,但因为调用前切换环境时已提前进行了收集,所以以 TCO 动作为边界,保存的帧应与之等效。
53175458 尽管可能有更优的渐进复杂度,所需空间的大小仍取决 TCO 活动记录最终保留帧前分配对象和碎片所占空间大小之和的最大值;实现应该不依赖这项特性。
53185459 回收使函数对象在表达式求值外完毕之前结束,这仍然符合尾上下文约定(@5.10) 的要求。
5319-当前 NPL::LiftToReturn(@6.9.4.3) 的调用非嵌套调用安全(@6.1.3) 。在项的嵌套深度依赖调用深度时,不满足 PTC 。
5460+当前 NPL::LiftToReturn(@6.9.4.3) 的调用非嵌套调用安全(@6.1.4.1) 。在项的嵌套深度依赖调用深度时,不满足 PTC 。
53205461 **注释**
53215462 需要注意,在 WHNF 的第一项嵌套调用时,Kernel 不保证递归调用的 PTC ,因为这不是尾上下文:
53225463 ($sequence ($define! $f ($vau () #ignore (($f)))) ($f))
@@ -5355,7 +5496,7 @@
53555496 中缀变换(@7.5.2) ;
53565497 递归绑定支持 API(@7.7.4) 。
53575498 例外参见:
5358-NPLA 安全保证例外(@6.1.3) 。
5499+NPLA 安全保证例外(@6.1.4) 。
53595500 另见 @7.9.2 。
53605501
53615502 @8 NPLA1 对象语言扩展实现:
@@ -5528,7 +5669,7 @@
55285669 Vau 抽象的应用在的新创建的空的但以上述静态环境为父环境(@5.7) 的局部环境(local environment) 中对抽象中指定的表达式(函数体)的副本求值,以包含局部环境(@4.6.1.2) 的数据结构作为活动记录帧(@5.6) 。
55295670 记号值(@6.8.1) 可表示待替换的符号(@6.8.1) ,用于实现形式参数(@8.4.5) 。
55305671 λ 抽象可通过 vau 抽象实现,但由于简单和性能原因,直接提供 API 支持。
5531-创建过程抽象的调用时递归检查形式参数树的叶节点是否为符号。当前递归检查符号非嵌套调用安全(@6.1.3) 。
5672+创建过程抽象的调用时递归检查形式参数树的叶节点是否为符号。当前递归检查符号非嵌套调用安全(@6.1.4.1) 。
55325673
55335674 @8.4.5.1 捕获:
55345675 环境中的变量被隐含地捕获。
@@ -5589,7 +5730,7 @@
55895730 Forms::WrapOnce 包装操作子为应用子。
55905731 Forms::Unwrap 解包装(@4.5.3.2) 应用子为合并子。
55915732 其中后两者会对合并子按 @7.6.1 的约定进行类型检查(@4.7.4) ,若不满足条件则抛出异常。
5592-当被解包装的应用子参数是引用时,构造具有非平凡非正规表示(@6.3.9.1) 的子对象引用(@6.8.3.3) 。
5733+当被解包装的应用子参数是引用时,构造具有非平凡非正规表示(@6.3.9.2) 的子对象引用(@6.8.3.3) 。
55935734
55945735 @8.4.7 错误检查:
55955736 Forms 提供以下可能抛出异常的错误检查函数:
@@ -5736,6 +5877,7 @@
57365877 get-module-return
57375878 load-external
57385879 promise-handle-result
5880+recover-source-name
57395881 require-return
57405882 在模块 NPL::Dependency 中,若特定对象语言操作使用非本机实现(@5.3) ,可使用不同的续延名称。
57415883
@@ -5748,8 +5890,8 @@
57485890 不一致的名称包括:
57495891 as-environment-return :Forms::AsEnvironment(@8.4.9) 返回环境。
57505892 captured-one-shot-continuation :通过 Forms::Call1CC(@8.4.10) 捕获的一次调用续延(@4.5.3.3) 。
5751-combine-let-return :非 TCO 的 Forms::Let 、Forms::LetRef 、Forms::LetWithEnvironment 、Forms::LetWithEnvironmentRef 、Forms::LetAsterisk 、Forms::LetAsteriskRef 、Forms::LetRec 、Forms::LetRecRef 和 Forms::AsEnvironment(@8.4.9) 的实现中设置规约合并最后的返回结果。
5752-combine-return :非 TCO 的规约合并求值(@7.6.4.2) 实现中设置规约合并最后的返回结果。
5893+combine-let-return :非 TCO 的规约合并求值(@7.6.4.2) 实现中的 Forms::Let 、Forms::LetRef 、Forms::LetWithEnvironment 、Forms::LetWithEnvironmentRef 、Forms::LetAsterisk 、Forms::LetAsteriskRef 、Forms::LetRec 、Forms::LetRecRef 和 Forms::AsEnvironment(@8.4.9) 的实现中设置规约合并最后的返回结果。
5894+combine-return :非 TCO 的规约合并求值实现中设置规约合并最后的返回结果。
57535895 equal-subterm :Forms::EqualTermValue(@8.4.1) 比较直接子项。
57545896 equal-siblings :Forms::EqualTermValue 比较同一个项中的剩余其它子项。
57555897 eval-$binds1?-env :Forms::LoadGroundContext(@8.5.2) 实现 $binds1?(@11.4.2) ,类似 klisp 的 eval-$binds-env 。
@@ -5772,12 +5914,14 @@
57725914 eval-lift-result :非 TCO 的求值实现调用 NPL::ReduceForLiftedResult(@6.10.7.1) 以及非尾上下文(@4.4.8) 求值提升返回结果。
57735915 eval-lift-sum :Forms::AccR、Forms::FoldR1 和 Forms::Map1(@8.4.9) 公共的部分和提升调用。
57745916 eval-map1-appv :Forms::Map1(@8.4.9) 应用子调用。
5775-eval-tail :TCO 动作尾上下文求值(和 klisp 不同,不依赖 GC(@5.6.4) 而在 TCO 动作单独保存资源(@7.10.5.1) )。
5917+eval-tail :TCO 动作尾上下文求值。
5918+**注释** 和 klisp 不同,不依赖 GC(@5.6.4) 而在 TCO 动作单独保存资源(@7.10.5.1) 。
57765919 eval-vau-parent :Forms::LambdaWithEnvironment 和 Forms::VauWithEnvironment(@8.4.5) 创建操作子时求值过程抽象的父环境的表达式。
57775920 get-module-return :异步规约的 LoadModule_std_io 实现 get-module 返回结果。
57785921 load-external :A1::RelayToLoadExternal(@8.5.2) 加载外部翻译单元。
57795922 match-ptree-recursive :支持延迟递归绑定( Forms::DefineWithRecursion 和 Forms::SetWithRecursion(@8.4.4.3) )。
57805923 provide-let-return :Forms::ProvideLet(@8.4.9) 返回环境。
5924+recover-source-name :非 TCO 的 A1::ReduceToLoadExternal 和 A1::RelayToLoadExternal(@8.5.1) 及 Forms::LoadModule_std_modules(@8.5.2) 在加载模块求值后恢复 REPLContext(@7.8) 中保存的源代码名称。
57815925 require-return :异步规约的 LoadModule_std_module 实现 require 加载翻译单元后设置和返回结果。
57825926 sequence-return :非 TCO 的 A1::ReduceOrdered(@7.4.6) 实现中设置规约合并最后的返回结果。
57835927 **注释**
@@ -5905,9 +6049,11 @@
59056049 求值得到的操作数的文法约定如下:
59066050 <object> :一般对象,包括引用对象的引用值(@9.9.1) 。
59076051 <reference> :对象引用值。
5908-<list> :列表。
6052+<list> :真列表(@6.2.1) :空列表或第二个元素为空列表的有序对。
59096053 **注释** 关于对列表类型的值的具体要求,参见列表(@9.9.4) 。
59106054 <lists> :元素都是列表的列表。
6055+<pair> :有序对。
6056+**注释** 可构成真列表或非真列表(@9.9.4) 。另见表示的说明(@6.2.3) 。
59116057 <boolean> :布尔值(@4.1),即取值为 #t 或 #f 的集合,是 NPLA 类型映射(@5.5) 指定的用于条件判断的单一值的类型。
59126058 推论:<boolean> 对应的宿主类型是 bool 。
59136059 <test> :类似 <object> ,通常预期为 <boolean> ,作为条件。
@@ -5926,6 +6072,9 @@
59266072 <object> 等求值得到的操作数不保证是语法意义上连续的词法组合,不能由多个表达式构成,因此即便出现在元素末尾,也不能如 <body> 一样减少括号。
59276073 <object> 表示类型全集(@4.7.5) ,其元素可被断言在论域(@1.5.3.6) 内,即任何其它类型都是 <object> 的子类型(@4.7.7) 。类型检查(@9.5.4.1) 可对此进行验证。
59286074 和 [RnRK] 的理由不同,允许布尔代数以外扩展的比较判断在此不认为是易错的,而是有意的设计(by design) 。这避免预设地假定类型的名义语用作用(“角色(role)” ),也避免限制语言和派生语言的类型全集(@4.7.5) 的设计。
6075+**注释**
6076+空列表构成的单元类型(@4.7) 是真列表的子类型,而不是有序对的子类型。
6077+非空真列表是有序对的子类型。
59296078
59306079 @9.2.2.4 文法形式补充约定:
59316080 除非另行指定,以 <symbols> 指定的值被作为 <definiend> 或 <formals> 使用时不引起错误。注意 <symbols> 在被其它上下文使用时仍可能引起错误。
@@ -5999,7 +6148,7 @@
59996148 以下使用 ...(@9.1) 作为函数的操作数时,可支持没有操作数的函数合并。此情形下应用表达式仍需要前缀 () ,但不在以下规约文法中显式地表示。
60006149
60016150 @9.4 对象语言内存安全保证:
6002-对象语言的内存安全(@5.6.3) 保证及违反内存安全的(不完全)检查(参见 @7.1.4.3 )的基本机制基于 NPLA1 关于被规约项、生存期、所有权和间接值的约定(@7.1) 。
6151+对象语言的内存安全(@5.6.3) 保证及违反内存安全的(不完全)检查(参见 NPLA1 非内存安全项(@7.1.4.3) )的基本机制基于 NPLA1 关于被规约项、生存期、所有权和间接值的约定(@7.1) 。
60036152
60046153 @9.4.1 对象语言基本内存安全保证:
60056154 对象语言提供关于内存安全的基本保证:
@@ -6050,7 +6199,7 @@
60506199 函数调用修改环境使环境对象保留间接值(绑定间接值或包含间接值作为子对象的对象作为被绑定对象(@6.11.1) )时,在环境中保留间接值。
60516200 函数调用修改一等对象或其子对象,使之保留间接值时,在对象中保留间接值。
60526201 在返回值中保留间接值、在环境中保留间接值、在对象中保留间接值的函数保留间接值。
6053-被保留的间接值是函数调用中的求值结果时,函数在结果中保留间接值。
6202+被保留的间接值是函数调用的求值结果时,函数在结果中保留间接值。
60546203
60556204 @9.4.4.2 被保留的引用值的来源:
60566205 函数返回包含间接值的对象由参数的值决定时,保留参数中的间接值。
@@ -6169,8 +6318,17 @@
61696318 基于表达式的类型(@5.8.6) ,对应对象语言表达式的表示实体的元素可指定操作数上明确的类型要求。
61706319 部分实体从属于其它实体类型而构成子类型(@4.7.2) 关系;部分的规约操作取得求值结果保证结果中的值可能具有的特定类型集合,这些类型也一并在以下描述中给出;其它情形不指定类型。
61716320 规约预期符合约束。若违反由项的值对应的动态类型(@6.3) 不匹配导致,则求值失败;否则,行为未指定。
6172-除非另行指定,不同类型检查的作用的顺序未指定。
6321+类型检查的完成应先序(@4.4.3) 依赖被检查特定类型的值的访问。
6322+除非另行指定,类型检查和程序中的其它作用(包括不同的其它类型检查)的顺序未指定。
61736323 类型错误(@4.7.4) 引发错误对象(@9.5.1) 。
6324+**原理**
6325+类型检查有助于维护程序的正确性,并及早发现编程错误。
6326+但是,类型检查自身存在开销;在一个阶段中集中检查类型的限制不是必要的。特别地,静态类型检查(@4.7.4) 不被要求。
6327+这些设计同时确保程序容易在程序在实现的不同执行阶段(@2.4.1) 重现相同的检查逻辑乃至直接复用其实现。
6328+为减小开销等目的,实现可能合并不同类型检查,而不改变程序的可观察行为(@4.1.3) 。
6329+**注释**
6330+一个值可被多次针对不同的对象进行类型检查。
6331+不同的类型检查中,对特定类型的值的访问之间没有必然的隐含联系。
61746332
61756333 @9.6 外部表示:
61766334 同 @4.1.1 ,外部表示由派生实现约定。
@@ -6267,7 +6425,7 @@
62676425 其它具体操作定义的求值。
62686426
62696427 @9.7.4.2 支持 PTC 的非调用尾上下文:
6270-通过环境(@9.9.3) 支持的名称解析(@6.11.1) 的实现在符合嵌套调用安全的约定(@6.1.3) 。
6428+通过环境(@9.9.3) 支持的名称解析(@6.11.1) 的实现在符合嵌套调用安全的约定(@6.1.4) 。
62716429 名称解析可能是尾上下文。
62726430
62736431 @9.7.4.3 未绑定对象:
@@ -6288,15 +6446,22 @@
62886446 实际实现当前支持这个例子的 TCO 。
62896447
62906448 @9.8 对象语义:
6291-关于存储和对象模型,基本内容参见 @5.6 。另见 @9.4 。
6449+关于对象的存储,基本内容参见 NPLA 存储和对象模型(@5.6) 。
6450+另见对象语言内存安全保证(@9.4) 。
62926451
62936452 @9.8.1 对象同一性(@4.1) :
62946453 NPLA1 的对象是一等对象(@4.1) 。由定义,NPLA1 的对象默认确保同一性。
6295-对象引用通常不保证对象同一性,包括唯一引用(@6.2.2) 的情形。但除非另行指定,类似 ISO C++ [res.on.arguments] ,作为函数实际参数的对象若是右值引用,则实现可假定被引用对象唯一。
6454+例外参见子对象(@9.8.2) 。
6455+对象引用通常不保证其被引用对象(@4.2.3) 和其它对象都不同一,包括唯一引用(@6.2.2) 的情形。但除非另行指定,作为函数实际参数的对象若是右值引用,则实现可假定被引用对象唯一。
6456+**注释**
6457+关于右值引用的保证类似 ISO C++ [res.on.arguments] 。注意这在对象语言而非宿主语言中适用。
62966458
62976459 @9.8.2 子对象:
62986460 基本内容参见 NPLA 子对象(@5.6.6) 。
62996461 子对象可具有引用值。子对象的引用值可能使用子对象引用(@6.8.3.3) 而非通常的项引用(@6.8.3) 实现。
6462+子对象引用访问的被引用对象不保证具有同一性。
6463+**原理**
6464+和宿主语言不同,通过相同方式构造的子对象引用访问的被引用对象未指定是否为同一对象(@5.6.6) 。
63006465 **注释**
63016466 和宿主语言不同,NPLA1 对象语言不直接提供访问子对象的内建语法。
63026467 库可以使用函数的形式提供访问特定的子对象的操作,如访问器(@10.5.5) 。
@@ -6323,7 +6488,7 @@
63236488
63246489 @9.8.3 对象的修改和改变:
63256490 对象作为实体可修改和改变,可具有可变数据状态及可变管理状态(@4.1.4.2) 。
6326-NPLA 约定(@5.2) 对象的表示同宿主实现的对象,其修改也同宿主对象的修改。
6491+NPLA 约定(@5.2) 的表示同宿主实现的对象,其修改也同宿主对象的修改。
63276492 隐藏状态(@4.1.3) 在针对对象语言的的讨论中被排除。除非另行指定(由具体操作的语义蕴含),所有可变状态都不属于这些被排除的状态。
63286493 类似 [RnRK] :
63296494 对明确不可变的对象进行改变的操作引起错误(@9.5.1) ;
@@ -6338,10 +6503,14 @@
63386503 除非另行指定,NPLA1 不限制任意对象不可修改。
63396504 等价关系和限制不可修改性的方法的方式不唯一,因此不可修改性也不唯一。
63406505 因为外部表示不唯一(@9.6) ,不需要基于此定义一种正规的关于外部表示的等价判断形式。
6506+对象的不保证同一性的子对象(@9.8.2) 的修改和改变不保证蕴含对对象的修改和改变。
63416507 **原理**
63426508 开放类型映射(@5.5) 不保证非特定对象之间的不可修改性具有唯一的定义。
6343-**注释**
6344-对象的子对象作为可变管理状态,使不可变对象具有允许这些状态改变的内部可变性(@4.1.4.2) 。
6509+对象的修改和改变作用在确定的对象上。
6510+若不同的对象之间不具有同一性,则作用之间无关。因此,修改和改变作为副作用,不保证在不同一的对象之间共享(@5.6.6) 。
6511+**注释**
6512+所有对对象的状态的约定针对同一个对象。
6513+对象的子对象作为可变管理状态,使不可变对象具有允许这些状态改变的内部可变性(@4.1.4.2) 而和对象的可变性不同。
63456514 环境中的被绑定对象在仅讨论不可变性的意义外仍是数据对象。
63466515 引起对象内的可变管理状态的改变而不改变对象的操作在宿主语言可通过类的 mutable 数据成员实现(@4.1.4.2) ,但 NPLA1 不提供特性使任意的子对象(@9.8.2) 的可修改性的限制如宿主语言的 const 限定符自动传播(而一般需要使用成员访问操作(@9.8.2.1) ),因此也不需要提供对应的类型检查修改机制。
63476516 冻结操作(@9.9.3.9) 是使环境对象上具有类似宿主语言的 const 传播约束的操作;和宿主语言不同,这不是静态类型系统约束。
@@ -6356,13 +6525,17 @@
63566525 复制赋值在自赋值时的源操作数复制仍可能出错。使用基本提升操作(@6.9.4.1) 实现时,一般使用 NPL::LiftTermOrCopy 而不是检查自赋值的 NPL::LiftOtherOrCopy 。
63576526 类似宿主语言,除非另行指定,赋值操作不保留源操作数的值类别(@9.7.2) 和可修改性。
63586527 **注释**
6359-赋值不保证子对象(@9.8.2) 的同一性不被改变;子对象的引用仍可能被赋值无效化(@9.8.6) 。
6528+作为推论,赋值不保证子对象(@9.8.2) 的同一性不被改变;子对象的引用仍可能被赋值无效化(@9.8.6) 。
6529+通过对象的子对象引用(@9.8.2) 修改对象的子对象不保证作用在对象上。
63606530
63616531 @9.8.3.2 转移:
63626532 转移可导致被转移对象的外部可见的修改。
63636533 转移不需要是直接显式求值特定的函数调用的副作用。
6364-例如,使用唯一引用(@6.2.2) 初始化对象,可转移被引用对象对应的项(@6.3.6) 。
6534+**注释** 例如,使用唯一引用(@6.2.2) 初始化对象,可转移被引用对象对应的项(@6.3.6) 。
63656535 和宿主实现不同,当前实现不直接通过初始化转移宿主对象(@5.8.2.3) 。
6536+被转移的对象在转移后具有有效但未指定(@5.6.2) 的状态。
6537+**注释**
6538+作为推论,通过转移对象的子对象引用(@9.8.2) 修改对象的子对象不保证作用在对象上。但和其它修改不同,这同时是被转移对象后的状态的规则覆盖。
63666539
63676540 @9.8.4 驻留(interning) :
63686541 出现在表达式中多个位置的值在实现中可共享一个对象作为内部表示。这个对象被驻留(interned) 。
@@ -6550,30 +6723,32 @@
65506723 可修改的对象若通过隐藏环境中的绑定提供,环境可为在隐藏环境提供的不被冻结的环境子对象,或其中的合并子返回的值。
65516724
65526725 @9.9.4 列表:
6553-列表都是真列表(@6.2.1) 。
6554-列表的元素(element) 是构成列表的对象。真列表的元素有限且不存在环。
6555-列表上的引用(@9.9.1) 也应不构成环(@9.9.1.1) 。这使处理列表的操作不需要考虑一些复杂的自引用(@9.9.1.2) 情形。
6556-作为真列表,列表直接被表示为子项元素的列表节点的序列,其宿主类型为 TermNode(@6.2) 。
6726+NPLA1 列表是真列表(@6.2.1) 或者满足特定要求的非真列表(@6.3.9.1) 。
6727+列表的元素是构成列表的对象。
6728+列表的元素数量有限。
6729+列表的元素之间不互相引用,不构成环(@6.2.1) 。列表的引用(@9.9.1) 构成其它对象时,也不构成环(@9.9.1.1) 。这使处理列表的操作不需要考虑一些复杂的自引用(@9.9.1.2) 情形。
6730+表示列表的宿主类型为 TermNode(@6.2) 。真列表直接被表示为子项元素的列表节点的序列(@6.2.1) 。非真列表同时使用值数据成员构成其表示(@6.3.9.1) 。
65576731 绑定构造(@9.7.3) 的形式参数树(@7.7.3) 是可能是符号(@9.9.2) 或真列表。
6732+除非另行指定,NPLA1 列表类型指真列表(@9.2.2.2) 。非真列表的类型是有序对(@9.2.2.2) 。
65586733 **原理**
65596734 和 Scheme 及 Kernel 不同,NPLA 支持的列表都是真列表。另见关于自引用数据结构和循环引用的分析(@4.2.4) 。
65606735 列表的这些特性确保基于列表的数据结构在对象语言逻辑上的简单性。也因此 NPLA1 对应的操作中,没有对环存在性的检查。
6561-没有环的结构能保证所有权语义能按需嵌入(embed) 到列表中,即列表可保证表示为同构的具有对节点所有权的嵌套 cons 对,即便当前实现不使用(@7.7.4) 。
6736+没有环的结构能保证所有权语义能按需嵌入(embed) 到列表中,即列表可保证表示为同构的具有对节点所有权的嵌套 cons 对(@6.3.9.1) ,即便当前实现不使用(@7.7.4) 。
65626737 **注释**
65636738 列表的元素以 TermNode 的子项表示。
65646739 利用 TermNode 中可选的值数据成员(@6.2) ,列表节点可以退化为非列表的值的表示;列表自身也可作为其它列表的节点。列表节点的实现的表示详见 @6.2.1 。
65656740
65666741 @9.9.4.1 列表的子对象和子对象引用:
65676742 列表的元素是列表的子对象(@9.8.2) ,列表对作为元素的表示具有所有权。同一个列表的元素节点之间没有所有权关系。
6568-子列表(sublist) 是列表的元素构成的子对象。
6569-子列表引用(sublist reference) 是一个列表中的任意元素的集合被作为列表的被引用对象的引用。
6743+子列表(sublist) 是一个列表中的任意元素的集合构成的子对象。
6744+子列表引用(sublist reference) 是子列表作为被引用对象的引用。
65706745 部分操作可能修改(@9.8.3) 列表的子对象。
65716746 除非另行指定,以子项构成的列表的子对象被转移(@9.8.3.2) ,使用项的转移(@6.3.6) 。被转移的子对象在被转移不在被转移的列表中存在。
65726747 **原理**
65736748 列表的元素以 TermNode 的子项表示。
65746749 列表对表示元素的节点所有权由 TermNode 的性质得到。
65756750 真列表的要求带来对元素引用的一些限制:列表的引用(@9.9.1) 只限引用完整的列表对象,而不引用部分列表(如 cons 的组成部分)。
6576-因此,引入列表的子对象(@9.9.4.1) 的引用可涉及具有非平凡非正规表示(@6.3.9.1) 的子对象引用(@6.8.3.3) 。
6751+因此,引入列表的子对象(@9.9.4.1) 的引用可涉及具有非平凡非正规表示(@6.3.9.2) 的子对象引用(@6.8.3.3) 。
65776752 子列表引用是这种情形的子对象引用。
65786753 **注释**
65796754 当前子列表引用总是引用至少一个列表元素。
@@ -6623,7 +6798,7 @@
66236798 使用 ktail_call 和 ktail_eval 使规约函数的实现被调用。
66246799 不使用宿主语言中的函数应用,因为使用的数据结构不是 C 函数类型 klisp_CFunction ,且宿主语言不支持这些类型的函数应用。
66256800 首先区分续延和操作子调用,而不是在规约中按需单独设置下一求值项和环境。
6626- 对操作子调用,首先以形式参数树(@7.7.3) 作为下一求值项,使用异步规约而不是在具体规约实现内部单独实现的其它机制维护嵌套调用安全性(@6.1.3) 。
6801+ 对操作子调用,首先以形式参数树(@7.7.3) 作为下一求值项,使用异步规约而不是在具体规约实现内部单独实现的其它机制维护嵌套调用安全性(@6.1.4) 。
66276802 使用 C 函数调用产生对象语言错误,不直接使用和支持本机的其它机制(如 setjmp/longjmp 和 C++ 异常)的交互。
66286803 在全局上下文中支持名称表。
66296804 此外 klisp 还有一些在 NPLA1 参考实现中不存在的实现缺陷:
@@ -6670,6 +6845,7 @@
66706845
66716846 @10.1.2 导入(import) 符号:
66726847 在环境中定义另一个环境中的同名变量,使被定义的变量是后者的引用值或值的副本,则称指定此变量名的符号在后者被导入前者。
6848+**注释** 用户程序可导入环境中的符号使用库中的绑定。
66736849
66746850 @10.2 模块:
66756851 同 [RnRK] ,以绑定提供的语言特性被分组归类为模块(@1.5.6.4) 。
@@ -6683,7 +6859,7 @@
66836859 一般地,模块和加载模块得到的环境对象没有直接对应关系:一个模块的绑定可以由一个或多个环境提供,一个已被加载的环境可能提供零个或多个程序可见的模块。但除非另行指定,一个模块的绑定不在超过一个的不相交的环境(之间没有直接或间接父环境关系)中提供。
66846860 程序可通过加载外部模块来源取得模块。除非另行指定,这种模块以一个一等环境对象(可包含作为环境的直接或间接子对象)中的绑定提供。
66856861
6686-@10.2.1 模块初始化:
6862+@10.2.1 标准库模块的初始化和加载:
66876863 标准库实现可作为语言实现(@1.2.3) 。实现初始化(@10.1.1) 时以提供模块时,可访问不作为公开接口提供的模块的源。
66886864 **注释**
66896865 派生实现可同时以标准库以外形式提供这些源为公开接口,用户程序也可显式地加载这些源对应的模块。
@@ -6786,7 +6962,7 @@
67866962 @10.4.3 保留引用值:
67876963 保留间接值,包括直接保留间接值和间接保留间接值(@9.4.4.2),适用间接值是引用值的情形,对应地称为保留引用值、直接保留引用值和间接保留引用值。
67886964 除非另行指定,被保留的引用值(@9.4.4) 不被折叠(@5.8.3.5) 。
6789-通过要求引用折叠,可保证不引入引用的引用值。
6965+**注释** 通过要求引用折叠,可保证不引入引用的引用值。
67906966 函数是否保留引用值以及保留引用值是否被折叠的要求,参见常规函数约定(@10.6) 、函数名称约定(@10.7) 和 NPLA1 参考实现环境(@10) 中的函数名称的约定(@11.2.2) 。
67916967 考虑内存安全时,因无法保证如引用值通过关联的环境(@6.8.3.1) 追溯被引用对象类似的机制可用,环境引用(@6.4.3.1) 外的非引用值间接值(@6.4.6.2) 视为不具有内存安全保证(@9.4.3) 。
67926968 间接保留引用值可包含部分直接转发引用值(@10.5) 的情形。包含这种情形的操作不因此被视为直接保留引用值,即便部分引用通过直接转发引用值被保留。这保证以上两个子类不相交。
@@ -6939,12 +7115,13 @@
69397115 可能间接保留引用值的操作:可涉及不同的环境,结果和作用依赖实际参数(需要时经过隐含的左值到右值转换)在这些环境中被求值后确定。
69407116 对可能在返回值中间接保留引用值的操作,以 % 结尾表示对应的函数返回时不要求返回非引用值(@10.4.2) 。
69417117 其它可能在返回值中直接保留引用值的提供不同引用标记字符的多个变体的操作:
6942-以 % 结尾表示函数可使用不进行左值到右值转换(@5.8.4) 的折叠(@5.8.3.5) 的引用值参数,或可返回折叠的引用值;
7118+以 % 结尾表示函数使用不进行左值到右值转换(@5.8.4) 的折叠(@5.8.3.5) 的引用值参数,或返回折叠的引用值;
69437119 以 & 结尾表示函数使用不进行左值到右值转换的折叠的引用值参数,或返回折叠的引用值;
69447120 以 @ 结尾表示函数使用不进行左值到右值转换的未折叠的引用值参数,或返回未折叠的引用值。
7121+以上引用值参数的使用指以依赖这些参数的方式构成函数的调用结果和/或决定产生的相应的副作用;返回的引用值来自引用值参数(若存在)。
69457122 **注释**
69467123 取得折叠的引用值的默认约定同间接值实现规则(@9.4.2) 蕴含的 NPLA1 默认规则(@7.1.3) 。
6947-按这些规则,以 & 结尾的操作在取折叠的引用值时,可能同时实现被引用对象的转发。这相当于被访问的被引用对象作为宿主语言的 std::forward 的参数后的调用结果作为操作的结果,但此处一般仍然保证支持 PTC(@9.7.4) 。
7124+按这些规则,以 % 结尾的操作在取折叠的引用值时,可能同时实现被引用对象的转发。这相当于被访问的被引用对象作为宿主语言的 std::forward 的参数后的调用结果作为操作的结果,但此处一般仍然保证支持 PTC(@9.7.4) 。
69487125 以 & 结尾的操作取得的折叠的引用值可能是唯一引用。
69497126
69507127 @10.7.3.1 引用折叠:
@@ -6965,15 +7142,17 @@
69657142 一般地,仅在明确需要引用值时使用引用标记字符结尾的操作,而避免返回悬空引用(这类似宿主语言函数的 auto 而非 auto&& 的返回类型,但宿主语言中返回非引用类型的表达式两者含义不同)。
69667143
69677144 @10.7.3.3 保留引用值(@10.4.3) 的约定:
6968-可能直接保留引用值(@10.4.3) 的操作中,不带有引用标记字符的操作转发参数(@10.5.2) 。
7145+可能直接保留引用值(@10.4.3) 的操作中,不带有引用标记字符的操作传递非引用值参数(@10.5.1) ,其它函数转发参数(@10.5.2) 。
69697146 可能直接保留引用值的操作包括容器构造器或访问器(@10.5.5) ,以及可能使对象中包含引用值(@9.4.4.1) 的修改操作(@9.8.3) 。
7147+这些操作的结果(@10.3.1) 或产生的副作用完全由实际参数(根据是否存在引用标记字符 % 指定是否不经过隐含的左值到右值转换)的值确定。
7148+其中,带有引用标记字符结尾的操作是直接保留引用值操作。
7149+容器构造器可在元素保留参数的引用值。作为结果的容器总是作为非引用值返回(@10.4.2) ,即在结果中保留参数的引用值。
7150+**原理**
69707151 以上操作是否确定地保留引用值在一些情形容易证明附加调用安全(@9.4.6.1) ,此时可放宽安全子集(@10.8.8) 的条件确保安全性;在此不作要求。
69717152 对构造器及部分修改操作区分引用标记字符结尾可强调一些非预期保留引用值的容易误用情形:
6972-尽管总是返回非引用值,因转发参数而被保留的引用值(@10.4.3) 不会被返回值转换(@10.4.2) 或类似的操作影响,在构造的容器对象作为非引用值返回仍会保留引用值。
7153+尽管总是返回非引用值。
7154+因转发参数而被保留的引用值(@10.4.3) 不会被返回值转换(@10.4.2) 或类似的操作影响,在构造的容器对象作为非引用值返回时,仍会保留引用值。
69737155 (对应宿主语言中,可有更显著的差异,如构造器对应的 std::tuple 的 std::make_tuple 和 std::forward_as_tuple 。)
6974-这些操作的结果(@10.3.1) 由实际参数(根据是否存在引用标记字符 % 指定是否不经过隐含的左值到右值转换)的值确定。
6975-其中,带有引用标记字符结尾的操作是直接保留引用值操作。
6976-容器构造器可在元素保留参数的引用值,作为结果的容器总是作为非引用值返回(@10.4.2) ,即在结果中保留参数的引用值。
69777156
69787157 @10.7.4 可提供以引用标记字符结尾变体的操作:
69797158 部分操作使用以引用标记字符结尾的函数名(@10.7.3) 。
@@ -7408,7 +7587,7 @@
74087587 unwrap(@11.3.8) 创建合并子的子对象引用;
74097588 rest&(@11.4.1) 创建列表的子对象引用;
74107589 使用带有省略(@7.7.3.3) 的形式参数树(@7.7.3)(如 <formals>(@9.2.2.1) )绑定操作数为结尾列表(@7.7.3.3) ,创建列表的子对象引用(@7.7.3.5) 。
7411-子对象引用具有非平凡非正规表示(@6.3.9.1) 。
7590+子对象引用具有非平凡非正规表示(@6.3.9.2) 。
74127591 注意子对象引用在宿主语言中不存在有效的共享状态的表示(@6.8.3) 。
74137592 特别地,由绑定使用引用标记字符的非递归绑定(@7.7.3.5) 的规则,绑定列表的子对象引用(@9.9.4.1) 不直接共享操作数列表对象,而共享元素是原容器元素的(经折叠的(@7.7.3.4) )引用值的列表。
74147593 **注释** 这类似宿主语言的容器对象一般不能转换为共享容器部分元素的 C++ 对象引用。
@@ -7441,6 +7620,8 @@
74417620 考虑(可变对象的)一等引用和绑定构造(@9.7.3) 绑定引用的的平摊复杂度,不提供需要同时转换不同层次子项的 copy-es-immutable 操作。
74427621 部分其它原理参见根环境对象(@11.3.1) 。关于引用值的处理另见函数分类(@11.2) 。
74437622 其它没有包含在以下节中的 Kernel 操作可能会在之后的版本中被支持。
7623+**注释**
7624+当前在根环境中的直接提供绑定的特性不依赖 <number>(@6.14.1) 。
74447625
74457626 @11.3.1 对象定义:
74467627 当前根环境不定义对象。
@@ -7529,7 +7710,7 @@
75297710 @11.3.4 对象:
75307711 模块约定:
75317712 注意因为真列表(@9.9.4) 的限制,列表左值只能引用完整的列表的对象,而不支持部分列表。
7532-这影响 set-rest! 和 set-rest%! 的 <list> 参数。
7713+这影响 set-rest! 和 set-rest%! 的第一个参数。
75337714 操作:
75347715 null? <object> :判断操作数是否为空列表。
75357716 nullv? <object> :判断操作数是否为空列表纯右值。
@@ -7587,7 +7768,10 @@
75877768 **注释** 和 Kernel 的 set-cdr! 类似,但检查列表是左值,且不保留被添加元素中的引用值。
75887769 set-rest%! <list> <object> :同 set-rest! 但保留引用值。
75897770 **注释** 和 Kernel 的 set-cdr! 类似,但检查列表是左值。
7590-**注释** 不检查修改操作(@9.8.3) 导致循环引用(@9.9.1.1) ,用户应自行避免未定义行为(@5.4) 。
7771+**注释**
7772+和 Kernel 不同,NPL 不支持列表中存在环。
7773+不使用相同的对象左值的 cons% 调用或修改操作(@9.8.3) 导致循环引用(@9.9.1.1) ,用户应自行避免未定义行为(@5.4) 。
7774+结果具有的属性不被影响。这和实现中的 NPL::LiftElementsToReturn(@6.9.4.3) 不依赖表示最后的有序对的元素项上的标签行为一致。
75917775
75927776 @11.3.6 符号:
75937777 desigil <symbol> :移除符号中的引用标记字符 & 或 %(@7.7.3.4) 。
@@ -7723,13 +7907,14 @@
77237907 **原理**
77247908 因为本机实现可能提前判断空列表,所以能比访问元素的检查更加准确。
77257909 **注释**
7910+类似基本特性(@11.3) ,当前在根环境中的直接提供绑定的特性不依赖 <number>(@6.14.1) 。
77267911 以上一致性要求和 [RnRK] 合并子的派生完全省略错误处理(@10.6.4) 而允许不同的诊断不同。
77277912 当前已知和派生实现的诊断消息不同的本机实现有:
77287913 Forms::Assq(@8.4.9)
77297914 Forms::Assv(@8.4.9)
77307915
77317916 @11.4.1 基本派生特性:
7732-基本派生特性可使用派生实现(可能需要使用 @11.3 或 @11.4.1 中的部分非派生实现)。
7917+基本派生特性可使用派生实现。这可能蕴含使用根环境基本特性(@11.3) 或已在基本派生特性中提供的特性中的部分非派生实现。
77337918 模块约定:
77347919 引入合并子的操作子对 <body> 的约定同 @11.3.8 。
77357920 因互相依赖,一些操作实现为派生操作时,不能用于直接派生特定一些其它操作。
@@ -8197,7 +8382,7 @@
81978382 保存待求值的表达式和这个表达式求值时的动态环境(@4.6.1.2) ,或已求值的结果。
81988383 除 $lazy/d 外,同 [RnRK] 的 promises 模块。
81998384 操作:
8200-promise? <object> :判断参数是否为 <promise> 类型的值。
8385+promise? <object> :<promise> 的类型谓词(@10.7.2.1) 。
82018386 memoize <object> :记忆化求值:以参数作为已被求值的结果创建 <promise> 对象。
82028387 在结果中保留参数的引用值(@10.7.3.3) 。
82038388 $lazy <body> :延迟求值:以参数为待求值的表达式,以当前环境作为这个表达式被求值的动态环境,创建 <promise> 对象。
diff -r eae6ca1f576c -r e1756f2eba1f doc/ProjectRules.txt
--- a/doc/ProjectRules.txt Thu Jun 30 07:43:19 2022 +0800
+++ b/doc/ProjectRules.txt Tue Jul 12 18:45:26 2022 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2013-2021 FrankHB.
2+ © 2013-2022 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 ProjectRules.txt
1212 \ingroup Documentation
1313 \brief 项目组织和管理规则。
14-\version r1244
14+\version r1251
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 381
1717 \par 创建时间:
1818 2013-02-14 17:59:49 +0800
1919 \par 修改时间:
20- 2021-09-29 19:41 +0800
20+ 2022-07-09 18:08 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -311,6 +311,7 @@
311311 除非涉及 C++ 实体(如参数类型)变更或另行指定,所有变更直接通过预处理记号序列比较,即宏的替换序列改变是宏定义的平凡的变更;但这不包括被替换的实例。
312312 推论:宏名不变但宏的替换序列改变导致的实体的 C++ 名称变化是非平凡变更。
313313 在确定上下文中语言构造之间的意义完全一致的变换是平凡变更,包括除公开接口名称改变的别名声明替换和 typedef 之间的等价变换以及初始化指针的 nullptr 修改为 {} 等。
314+**注释** 除非约定为实现细节,替换类型名称为类模板等可能会影响 ADL 结果而影响 API 使用方式的变换不视为意义完全一致。
314315 除以上规则外具有链接的名称变更和程序实体类型变更是非平凡的;除非另行指定(@2.2.7.7) ,同时是非闭合的。
315316 推论:(布局)不兼容的接口类型及相关属性(如对齐要求、别名分析限制等)的变化非平凡。
316317
@@ -356,10 +357,11 @@
356357 其它转移不是原子的;若在同一项目中,按移除后添加处理。
357358
358359 @2.2.7.7 其它实体变更的闭合性:
359-不满足析取变更性质(@2.2.7.5) 的声明的实体类别变更(如类模板替换为别名模板、 using 声明和非 using 声明之间的变更)非闭合。
360+不满足析取变更性质(@2.2.7.5) 的声明的实体类别变更(如类模板替换为别名模板、using 声明和非 using 声明之间的变更)非闭合。
360361 所有导致实体名称的链接或对应目标代码中的符号(symbol) 可见性(visibility) 变更的非平凡变更非闭合,除了以下例外:
361362 显式 = delete 之间的变换;
362- 预处理记号序列不变,仅由宏定义替换序列导致的相同含义的接口的 C++ 名称变更。
363+ 预处理记号序列不变,仅由宏定义替换序列导致的相同含义的接口的 C++ 名称变更;
364+ 可见性变更在约定范围之内的(包括显式未指定的可见性和指定不保证可见性的情形)。
363365 注意针对源代码而不是生成的代码,仅变换 inline 或 constexpr 而省略指定符号可见性(接口不保证符号可见)是平凡变更。
364366 除符号可见性变更以外所有 ISO C++ 或实现扩展的属性(attribute) 变更是闭合的。
365367 对宏,对象宏/函数宏之间的变更以及参数列表变更非闭合。
@@ -382,7 +384,7 @@
382384 对枚举,在枚举器上添加、移除或修改显式指定的常量表达式的变更非闭合,而调换枚举器顺序等导致的隐式值的变更是闭合的。
383385 注意调换枚举器顺序等导致的隐式值的变更是平凡的(@2.2.7.4) 。
384386 对类的成员,访问权限变更非闭合。
385-对类作用域的 using 声明(注意不是 typedef 声明、 using 指示、友元声明或别名声明),保持最终指称同一个类的同一个相同可访问性的实体不变时闭合。
387+对类作用域的 using 声明(注意不是 typedef 声明、using 指示、友元声明或别名声明),当且仅当保持最终指称同一个类或作为其非公开实现的基类的同一个相同可访问性的实体不变时闭合。
386388 在类定义内初始化的类的数据成员和首次声明时初始化的命名空间作用域变量的不等效的初值符变更非闭合。
387389
388390 @2.2.7.8 代码标示:
@@ -396,7 +398,7 @@
396398
397399 @2.2.8 非定义声明独立版本标识:
398400 直接位于块作用域内的声明不加注版本。
399-typedef 声明、 using 声明、 using 指示 、友元声明和别名声明可独立加注版本,以声明的名称确定版本一致性。
401+typedef 声明、using 声明、using 指示 、友元声明和别名声明可独立加注版本,以声明的名称确定版本一致性。
400402 static_assert 声明可独立加注版本,以记号序列等价性确定版本一致性。
401403
402404 @2.2.9 头文件包含:
diff -r eae6ca1f576c -r e1756f2eba1f doc/YCLib.txt
--- a/doc/YCLib.txt Thu Jun 30 07:43:19 2022 +0800
+++ b/doc/YCLib.txt Tue Jul 12 18:45:26 2022 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2012-2021 FrankHB.
2+ © 2012-2022 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 YCLib.txt
1212 \ingroup Documentation
1313 \brief YCLib 说明。
14-\version r576
14+\version r592
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 651
1717 \par 创建时间:
1818 2015-11-19 09:05:02 +0800
1919 \par 修改时间:
20- 2021-07-21 19:28 +0800
20+ 2022-07-09 18:09 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -79,8 +79,9 @@
7979 @1.3.2 using 指令和 using 声明:
8080 基本规则参见 [Documentation::CommonRules @@3.3.1] 和 [Documentation::LanguageConvention @@5.5.7] 。
8181 依赖 YSLib 的公开头文件不使用 using 指令或 using 声明引入 YSLib 名称。
82-注意在依赖 YSLib 的非头文件(实现)中,可能使用 using namespace YSLib; 引入 YSLib 名称。
83-由于 platform_ex 可能以整体引入 namespace platform 名称,而 YSLib 也可能引入相同的名称;当引入的名称实体不等价时直接使用可能造成歧义,此时有必要单独使用 using 声明以允许 platform 和 YSLib 中同名声明指称不同实体。
82+注意在依赖 YSLib 的非头文件(实现)中,可能使用 using namespace YSLib; 等 using 指令引入 YSLib 或其它名称。
83+为减小逻辑上的依赖性,除在公开头文件的公开 API 中引入的 YSLib 名称及作为(类、命名空间或枚举等的)成员关联的名称外,若 ystdex 及 platform 或其成员命名空间中存在相同的名称,使用后者代替 YSLib 名称。
84+由于 platform_ex 可能以整体引入 namespace platform 名称,而 YSLib 也可能引入相同的名称;当引入的名称实体不等价时直接使用可能造成歧义,此时有必要直接使用限定名称或单独使用 using 声明而不是非限定名称,以允许 platform 和 YSLib 中同名声明指称不同实体。
8485 如 platform_ex 通过 using platform::containers; 引入 platform::string ,而 YSLib 也声明 string (具体由 YAdaptor 指定)。默认实现类型相同,但配置 YAdaptor 后可能造成歧义。
8586 一般地,若指称的是被设计为不易转换或开销较大的对象类型(如 string )被作为参数时优先使用 YSLib 中的名称,以减少用户代码适配的困难;否则优先使用 platform 内的名称以简化(特别是头文件内无法用 using 声明或指令简化的)声明。
8687 另见 [Documentation::YSLib @@3.5] 。
@@ -168,6 +169,16 @@
168169 和 POSIX 不同,和特定文件关联的文件系统元数据(如文件修改时间)视为从属文件本身。因此相关操作位于 FileIO 而非 FileSystem 。注意其它项的操作(如创建新的符号链接)不属于此范围,仍位于 YCLib.FileSystem 。
169170 另见 @3.3 、@4.1.6 和 @4.2.1 。
170171
172+@1.6.3.1 文件系统重定向:
173+涉及文件系统操作的实现只考虑以下重定向行为:
174+基于文件支持的重定向,如符号链接和 NTFS 重分析点;
175+基于公开接口的系统重定向。
176+后者当前包括 64 位 Microsoft Windows 中 32 位进程的 WoW64 的重定向(如系统目录 System32 重定向到 SysWOW64 )。其它兼容环境(如 ReactOS 和 WINE )因为可能未实现对应的 API ,在此不保证提供支持。
177+这忽略文件系统以上不可见的其它重定向行为。例如,不考虑设备驱动实现对打开文件和读取文件元数据检查文件存在性的行为差异(详见 https://devblogs.microsoft.com/oldnewthing/20071023-00/?p=24713 )。
178+
179+@1.6.3.2 实现限制:
180+因为文件系统不保证事务操作的支持,允许存在一般意义上无法确保可在应用程序中修复的 TOCTTOU 缺陷。
181+
171182 @1.6.4 长度限制:
172183 不一定直接使用本机的长度限制。
173184 直接使用依赖系统 API 的宏仅适用于 YCLib 。在用户代码中可使用其它 API (如 YSLib 中使用 YSLib::IO::MaxPathLength )。
@@ -232,10 +243,12 @@
232243 注意 WINVER 和 Win32 SDK 实现中 _WIN32_WINNT 等宏的值决定某些 API 是否被声明。
233244 这些宏之间的关联和差异参见 http://blogs.msdn.com/b/oldnewthing/archive/2007/04/11/2079137.aspx 。
234245 选择宏的使用遵循最小依赖原则,如确定 Windows NT 实现的 Win32 使用 _WIN32_WINNT 而不是 WINVER 。后者一般只在 YCLib::NativeAPI(@5.2) 出现。
246+按支持的 MinGW32 平台([Documentation::Dependencies @@1.5]) ,这些值至少为 0x0501 。
235247 当前不涉及 DDK ,因此不适用 NTDDI_VERSION 以避免混淆。
236248
237249 @4.1.1.2 兼容性:
238-不直接调用 Windows Vista 后支持的 API ,以动态加载(通过 ::GetProcessAddress )代替。
250+不直接调用 SDK 头文件中声明的要求以上(@4.1.1.1) 中约定的 Windows 版本之后支持的 Win32 API ,以动态加载(一般通过 ::GetProcessAddress )后调用代替。
251+仅在必要时使用 Windows NT API 。这些 API 不从 SDK 头文件引入声明,因此总是使用动态加载。
239252 对需编译时确定特定 API 声明存在性的场合,尽可能使用 SFINAE([Documentation::Designation @@3.2.4)] 而不是显式判断版本宏(@4.1.1.1) 检查可用性并自动匹配调用。
240253 不使用当前最新版本 Win32 SDK 中被标记为过时的 API 如 IsBadReadPtr 。
241254
@@ -287,6 +300,7 @@
287300 例如使用 ::GetSystemWindowsDirectoryW 而不是 ::GetWindowsDirectoryW (区别参见 http://stackoverflow.com/questions/13489746/difference-between-getwindowsdirectory-and-getsystemwindowsdirectory )。
288301
289302 @4.2 POSIX :
303+使用 POSIX.1 文件系统 API 的实现要求最低版本 POSIX.1-2008 。
290304
291305 @4.2.1 平台约定:
292306 DS 平台可沿用部分 POSIX 限制。
diff -r eae6ca1f576c -r e1756f2eba1f doc/YFramework.txt
--- a/doc/YFramework.txt Thu Jun 30 07:43:19 2022 +0800
+++ b/doc/YFramework.txt Tue Jul 12 18:45:26 2022 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2009-2021 FrankHB.
2+ © 2009-2022 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 YFramework.txt
1212 \ingroup Documentation
1313 \brief YFramework 设计和实现说明。
14-\version r1596
14+\version r1602
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since 早于 build 132
1717 \par 创建时间:
1818 2009-12-02 05:14:30 +0800
1919 \par 修改时间:
20- 2021-12-21 12:12 +0800
20+ 2022-07-09 12:12 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -420,10 +420,9 @@
420420 ystdex::octet 是对象类型。
421421 **注释** 由 YDefinition([Documentation::YBase @@2.4]) ,这蕴含 CHAR_BIT == 8 。
422422 NPL::NPLAMath([Documentation::NPL @@6.14]) 对实现具有以下限制:
423- 不使用以下不符合标准的实现配置:
424- 不使用不能正确处理浮点特殊值的实现。
425- **注释** 例如,不使用 GCC 等蕴含 -ffinite-math-only 的 -ffast-math 或 Microsoft VC++ 的 /fp:fast 等选项。
426423 标准浮点类型满足以下要求:
424+ 支持带符号的特殊值:无限大值和不引起错误的 quiet NaN 值。
425+ **注释** std::numeric_limits 的对应类型的实例的成员 has_infinity 和 has_quiet_NaN 都不等于 false 。
427426 大小不超过 128 位。
428427 **注释** 由 CHAR_BIT 推论,大小不超过 16 字节。
429428 float 和 double 类型的有效数字都不大于 53 位。
@@ -434,6 +433,9 @@
434433 long double 实现和 double 的浮点格式不同,且 long double 的有效数字在 64 位到 113 位之间。
435434 **注释** <cfloat> 标准库宏 LDBL_MANT_DIG >= 64 且 LDBL_MANT_DIG <= 113 。
436435 **注释** 可以使用 IEC 60559 binary64 的扩展 80 位格式或 IEC 60559 binary128 格式。
436+ 不使用以下不符合标准的实现配置:
437+ 不使用不能正确处理浮点特殊值的实现。
438+ **注释** 例如,不使用 GCC 等蕴含 -ffinite-math-only 的 -ffast-math 或 Microsoft VC++ 的 /fp:fast 等选项。
437439 **注释** 不要求浮点格式具有非规格(subnormal) 数表示。但是,实现可能针对规格数和非规格数表示不同的优化算法,以减少处理数值时内部表示的精度损失。
438440 不符合以上预期的实现无法构建,或不保证结果构建正确。
439441 此外,特定操作对不符合预期浮点类型实现格式的值的处理结果的正确性不作保证,且不排除因此具有未定义行为。
Show on old repository browser