• R/O
  • SSH

YSLib: Commit

The YSLib project - main repository


Commit MetaInfo

Revisión30522accfd38ceb1fcff035656ca08694af1b85f (tree)
Tiempo2022-04-05 17:56:05
AutorFrankHB <frankhb1989@gmai...>
CommiterFrankHB

Log Message

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

Cambiar Resumen

Diferencia incremental

diff -r eaddd0235b7a -r 30522accfd38 Tools/Scripts/SHBuild-YSLib-common.txt
--- a/Tools/Scripts/SHBuild-YSLib-common.txt Thu Mar 17 22:05:52 2022 +0800
+++ b/Tools/Scripts/SHBuild-YSLib-common.txt Tue Apr 05 16:56:05 2022 +0800
@@ -407,10 +407,14 @@
407407 $def! prom_C_CXXFLAGS_PIC
408408 $lazy-env-val "C_CXXFLAGS_PIC"
409409 $if (win32? host-os) "" "-fPIC -fno-semantic-interposition";
410+ $def! prom_C_CXXFLAGS_EXT
411+ $lazy-env-val "C_CXXFLAGS_EXT"
412+ $if (win32? host-os) "" "-D_POSIX_C_SOURCE=200809L";
410413 $def! prom_C_CXXFLAGS_COMMON
411414 $lazy-env-val "C_CXXFLAGS_COMMON" cons-cmd "-pipe"
412415 (first (force prom_gcflags_))
413- (safeenv-get "C_CXXFLAGS_ARCH") "-pedantic-errors";
416+ (safeenv-get "C_CXXFLAGS_ARCH") "-pedantic-errors"
417+ (force prom_C_CXXFLAGS_EXT);
414418 $def! prom_C_CXXFLAGS_OPT_LV $lazy-env-val "C_CXXFLAGS_OPT_LV" "-O3";
415419 $def! prom_C_CXXFLAGS_WARNING $lazy-env-val "C_CXXFLAGS_WARNING"
416420 cons-cmd "-Wall" "-Wcast-align" "-Wdeprecated"
diff -r eaddd0235b7a -r 30522accfd38 YBase/include/ystdex/apply.hpp
--- a/YBase/include/ystdex/apply.hpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YBase/include/ystdex/apply.hpp Tue Apr 05 16:56:05 2022 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2013-2016, 2018-2021 FrankHB.
2+ © 2013-2016, 2018-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
@@ -10,14 +10,14 @@
1010
1111 /*! \file apply.hpp
1212 \ingroup YStandardEx
13-\brief 元组应用操作。
14-\version r994
13+\brief 元组和函数应用操作。
14+\version r1102
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 333
1717 \par 创建时间:
1818 2019-01-11 19:43:23 +0800
1919 \par 修改时间:
20- 2021-12-26 12:32 +0800
20+ 2022-03-21 12:08 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -28,9 +28,8 @@
2828 #ifndef YB_INC_ystdex_apply_hpp_
2929 #define YB_INC_ystdex_apply_hpp_ 1
3030
31-#include "invoke.hpp" // for "invoke.hpp", index_sequence, ystdex::invoke,
32-// make_index_sequence, decay_t;
33-#include "integer_sequence.hpp" // for index_sequence;
31+#include "invoke.hpp" // for "invoke.hpp", ystdex::invoke, decay_t;
32+#include "integer_sequence.hpp" // for index_sequence, make_index_sequence;
3433 #include <tuple> // for <tuple>, __cpp_lib_apply, __cpp_lib_tuple_element_t,
3534 // __cpp_lib_make_from_tuple, std::tuple, std::get, std::forward_as_tuple,
3635 // std::tuple_size, std::make_from_tuple;
@@ -195,6 +194,7 @@
195194 \brief 应用函数对象和参数元组。
196195 \tparam _func 函数对象及其引用类型。
197196 \see ISO C++17 [tuple.apply]/1 。
197+\see WG21 P0220R1 。
198198 \since build 547
199199 */
200200 template<typename _func, class _tTuple>
@@ -231,6 +231,120 @@
231231
232232 } // inline namespace cpp2017;
233233
234+//! \since build 594
235+//@{
236+//! \brief 统计函数参数列表中的参数个数。
237+template<typename... _tParams>
238+yconstfn size_t
239+sizeof_params(_tParams&&...) ynothrow
240+{
241+ return sizeof...(_tParams);
242+}
243+
244+
245+//! \since build 412
246+//@{
247+/*!
248+\brief 变长参数操作模板。
249+\warning 非虚析构。
250+*/
251+//@{
252+template<size_t _vN>
253+struct variadic_param
254+{
255+ //! \since build 594
256+ template<typename _type, typename... _tParams>
257+ static yconstfn auto
258+ get(_type&&, _tParams&&... args) ynothrow
259+ -> decltype(variadic_param<_vN - 1>::get(yforward(args)...))
260+ {
261+ static_assert(sizeof...(args) == _vN,
262+ "Wrong variadic arguments number found.");
263+
264+ return variadic_param<_vN - 1>::get(yforward(args)...);
265+ }
266+};
267+
268+template<>
269+struct variadic_param<0U>
270+{
271+ //! \since build 594
272+ template<typename _type>
273+ static yconstfn auto
274+ get(_type&& arg) ynothrow -> decltype(yforward(arg))
275+ {
276+ return yforward(arg);
277+ }
278+};
279+//@}
280+
281+
282+/*!
283+\brief 取指定位置的变长参数。
284+\tparam _vN 表示参数位置的非负数,从左开始计数,第一参数为 0 。
285+*/
286+template<size_t _vN, typename... _tParams>
287+yconstfn auto
288+varg(_tParams&&... args) ynothrow
289+ -> decltype(variadic_param<_vN>::get(yforward(args)...))
290+{
291+ static_assert(_vN < sizeof...(args),
292+ "Out-of-range index of variadic argument found.");
293+
294+ return variadic_param<_vN>::get(yforward(args)...);
295+}
296+//@}
297+
298+
299+//! \see 关于调用参数类型: ISO C++11 30.3.1.2 [thread.thread.constr] 。
300+//@{
301+//! \brief 顺序链式调用。
302+//@{
303+template<typename _func>
304+inline void
305+chain_apply(_func&& f) ynothrow
306+{
307+ return yforward(f);
308+}
309+template<typename _func, typename _type, typename... _tParams>
310+inline void
311+chain_apply(_func&& f, _type&& arg, _tParams&&... args)
312+ ynoexcept_spec(ystdex::chain_apply(
313+ yforward(yforward(f)(yforward(arg))), yforward(args)...))
314+{
315+ return ystdex::chain_apply(yforward(yforward(f)(yforward(arg))),
316+ yforward(args)...);
317+}
318+//@}
319+
320+//! \brief 顺序递归调用。
321+//@{
322+template<typename _func>
323+inline void
324+seq_apply(_func&&) ynothrow
325+{}
326+//! \since build 595
327+template<typename _func, typename _type, typename... _tParams>
328+inline void
329+seq_apply(_func&& f, _type&& arg, _tParams&&... args)
330+ ynoexcept_spec(yimpl(yunseq(0, (void(yforward(f)(yforward(args))), 0)...)))
331+{
332+ yforward(f)(yforward(arg));
333+ ystdex::seq_apply(yforward(f), yforward(args)...);
334+}
335+//@}
336+
337+//! \brief 无序调用。
338+template<typename _func, typename... _tParams>
339+inline void
340+unseq_apply(_func&& f, _tParams&&... args)
341+ ynoexcept_spec(yimpl(yunseq((void(yforward(f)(yforward(args))), 0)...)))
342+{
343+ yunseq((void(yforward(f)(yforward(args))), 0)...);
344+}
345+//@}
346+//@}
347+
234348 } // namespace ystdex;
235349
236350 #endif
diff -r eaddd0235b7a -r 30522accfd38 YBase/include/ystdex/cache.hpp
--- a/YBase/include/ystdex/cache.hpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YBase/include/ystdex/cache.hpp Tue Apr 05 16:56:05 2022 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2013-2016, 2018-2021 FrankHB.
2+ © 2013-2016, 2018-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 cache.hpp
1212 \ingroup YStandardEx
1313 \brief 高速缓冲容器模板。
14-\version r604
14+\version r748
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 521
1717 \par 创建时间:
1818 2013-12-22 20:19:14 +0800
1919 \par 修改时间:
20- 2021-12-29 01:58 +0800
20+ 2022-03-22 18:11 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -28,13 +28,16 @@
2828 #ifndef YB_INC_ystdex_cache_hpp_
2929 #define YB_INC_ystdex_cache_hpp_ 1
3030
31-#include "allocator.hpp" // for std::pair, yassume, allocator_traits, are_same,
32-// rebind_alloc_t, std::hash;
31+#include "allocator.hpp" // for std::pair, yassume, rebind_alloc_t, are_same,
32+// nor_, is_convertible, enable_if_convertible_t, std::piecewise_construct_t,
33+// std::get, std::piecewise_construct, enable_if_t, head_of_t;
3334 #include <list> // for std::list;
35+#include "scope_guard.hpp" // for std::hash, optional_function, std::ref,
36+// ystdex::unique_guard, ystdex::dismiss;
3437 #include <unordered_map> // for std::unordered_map;
3538 #include <map> // for std::map;
36-#include "function.hpp" // for function;
37-#include <stdexcept> // for std::runtime_error;
39+#include "container.hpp" // for ystdex::begin, ystdex::cbegin,
40+// ystdex::search_map_by, ystdex::end, ystdex::cend;
3841
3942 namespace ystdex
4043 {
@@ -44,6 +47,7 @@
4447 /*!
4548 \brief 使用双向链表实现的最近使用列表。
4649 \note 保证移除项时的顺序为最远端先移除。
50+\warning 非虚析构。
4751 */
4852 template<typename _tKey, typename _tMapped,
4953 class _tAlloc = std::allocator<std::pair<const _tKey, _tMapped>>>
@@ -56,13 +60,23 @@
5660 */
5761 using allocator_type = _tAlloc;
5862 using value_type = std::pair<const _tKey, _tMapped>;
59- using list_type = std::list<value_type>;
63+ using list_type = std::list<value_type, allocator_type>;
6064 using size_type = typename list_type::size_type;
6165 using const_iterator = typename list_type::const_iterator;
6266 using iterator = typename list_type::iterator;
6367
68+ //! \since build 942
69+ using list_type::list_type;
70+
6471 using list_type::begin;
6572
73+ //! \since build 942
74+ YB_ATTR_nodiscard YB_PURE friend inline iterator
75+ cast_mutable(recent_used_list& con, const_iterator i) ynothrow
76+ {
77+ return con.erase(i, i);
78+ }
79+
6680 using list_type::cbegin;
6781
6882 using list_type::cend;
@@ -109,8 +123,7 @@
109123
110124 auto& val(list_type::back());
111125
112- if(f)
113- f(val);
126+ f(val);
114127 m.erase(val.first);
115128 list_type::pop_back();
116129 }
@@ -121,22 +134,24 @@
121134 while(max_use < size_type(m.size()))
122135 {
123136 yassume(!m.empty());
124-
125137 shrink(m, yforward(args)...);
126138 }
127139 }
128140
141+ //! \since build 942
129142 void
130- undo_emplace() ynothrow
143+ undo_emplace() ynothrowv
131144 {
132145 yassume(!empty());
133-
134146 list_type::pop_front();
135147 }
136148 };
137149
138150
139-//! \brief 最近刷新策略的缓存特征。
151+/*!
152+\brief 最近刷新策略的缓存特征。
153+\ingroup traits
154+*/
140155 //@{
141156 template<typename _tKey, typename _tMapped, typename _fHash = std::hash<_tKey>,
142157 class _tAlloc = std::allocator<std::pair<const _tKey, _tMapped>>,
@@ -166,6 +181,7 @@
166181 /*!
167182 \brief 按最近使用策略刷新的缓存。
168183 \note 默认策略替换最近最少使用的项,保留其它项。
184+\warning 非虚析构。
169185 \todo 加入异常安全的复制构造函数和复制赋值操作符。
170186 \todo 支持其它刷新策略。
171187 */
@@ -188,8 +204,13 @@
188204 //! \since build 611
189205 static_assert(are_same<size_type, typename used_list_type::size_type,
190206 typename used_cache_type::size_type>(), "Invalid size found.");
207+
208+private:
209+ //! \since build 942
210+ template<typename _tParam>
211+ using not_key_t = nor_<is_convertible<_tParam, const key_type&>,
212+ is_convertible<_tParam, std::piecewise_construct_t>>;
191213
192-private:
193214 /*!
194215 \invariant <tt>std::count(used_list.begin(), used_list.end())
195216 == used_cache.size()</tt> 。
@@ -202,8 +223,13 @@
202223 size_type max_use;
203224
204225 public:
205- //! \since build 870
206- function<void(value_type&)> flush{};
226+ /*!
227+ \brief 刷新函数。
228+ \invariant 为空值或目标调用时不抛出异常。
229+ \warning 若调用抛出异常,使用刷新函数的调用行为可能未定义。
230+ \since build 942
231+ */
232+ optional_function<void(value_type&)> flush{};
207233
208234 explicit
209235 used_list_cache(size_type s = yimpl(15U))
@@ -223,7 +249,7 @@
223249 void
224250 check_max_used() ynothrowv
225251 {
226- used_list.shrink(used_cache, max_use, flush);
252+ used_list.shrink(used_cache, max_use, std::ref(flush));
227253 }
228254
229255 public:
@@ -243,18 +269,25 @@
243269
244270 //! \since build 611
245271 //@{
246- iterator
247- begin()
272+ YB_ATTR_nodiscard YB_PURE iterator
273+ begin() ynothrow
248274 {
249- return used_list.begin();
275+ return ystdex::begin(used_list);
250276 }
251- iterator
252- begin() const
277+ YB_ATTR_nodiscard YB_PURE iterator
278+ begin() const ynothrow
253279 {
254- return used_list.cbegin();
280+ return ystdex::begin(used_list);
255281 }
256282 //@}
257283
284+ //! \since build 942
285+ YB_ATTR_nodiscard YB_PURE friend inline iterator
286+ cast_mutable(used_list_cache& con, const_iterator i) ynothrow
287+ {
288+ return cast_mutable(con.used_list, i);
289+ }
290+
258291 void
259292 clear() ynothrow
260293 {
@@ -263,79 +296,89 @@
263296 }
264297
265298 template<typename... _tParams>
266- std::pair<iterator, bool>
299+ inline std::pair<iterator, bool>
267300 emplace(const key_type& k, _tParams&&... args)
268301 {
269- check_max_used();
270-
271- const auto i_cache(used_cache.find(k));
272-
273- if(i_cache == used_cache.end())
274- {
275- const auto i(used_list.emplace(k, yforward(args)...));
276-
277- try
278- {
279- used_cache.emplace(k, i);
280- }
281- catch(...)
282- {
283- used_list.undo_emplace();
284- throw;
285- }
286- return {i, true};
287- }
288- else
289- return {i_cache->second, false};
302+ return emplace_with_key(k, k, yforward(args)...);
290303 }
304+ //! \since build 942
305+ template<typename _tParam, typename... _tParams>
306+ inline yimpl(enable_if_convertible_t)<_tParam, const key_type&,
307+ std::pair<iterator, bool>>
308+ emplace(std::piecewise_construct_t, std::tuple<_tParam> arg,
309+ std::tuple<_tParams...> args)
310+ {
311+ return emplace_with_key(std::get<0>(arg), std::piecewise_construct,
312+ std::move(arg), std::move(args));
313+ }
314+ //! \since build 942
291315 template<typename... _tParams>
292- std::pair<iterator, bool>
316+ yimpl(enable_if_t)<not_key_t<head_of_t<_tParams...>>::value,
317+ std::pair<iterator, bool>>
293318 emplace(_tParams&&... args)
294319 {
295320 check_max_used();
296321
297- const auto i(used_list.emplace_front(yforward(args)...));
298-
299- try
300- {
301- const auto pr(used_cache.emplace(i->first, i));
322+ const auto i(used_list.emplace(yforward(args)...));
323+ auto gd(ystdex::unique_guard([&]() ynothrowv{
324+ used_list.undo_emplace();
325+ }));
326+ const auto pr(used_cache.emplace(i->first, i));
302327
303- if(!pr.second)
304- {
305- used_list.undo_emplace();
306- return {pr.first, false};
307- }
328+ if(pr.second)
329+ {
330+ ystdex::dismiss(gd);
331+ return {i, true};
308332 }
309- catch(...)
310- {
311- used_list.undo_emplace();
312- throw;
313- }
314- return {i, true};
333+ return {pr.first->second, false};
315334 }
316335
336+private:
337+ //! \since build 942
338+ template<typename... _tParams>
339+ std::pair<iterator, bool>
340+ emplace_with_key(const key_type& k, _tParams&&... args)
341+ {
342+ check_max_used();
343+
344+ const auto pr(ystdex::search_map_by(
345+ [&](typename used_cache_type::const_iterator j){
346+ const auto i(used_list.emplace(yforward(args)...));
347+ auto gd(ystdex::unique_guard([&]() ynothrowv{
348+ used_list.undo_emplace();
349+ }));
350+ const auto r(used_cache.emplace_hint(j, k, i));
351+
352+ ystdex::dismiss(gd);
353+ return r;
354+ }, used_cache, k));
355+
356+ return {pr.first->second, pr.second};
357+ }
358+
359+public:
317360 //! \since build 611
318361 //@{
319- iterator
320- end()
362+ YB_ATTR_nodiscard YB_PURE iterator
363+ end() ynothrow
321364 {
322- return used_list.end();
365+ return ystdex::end(used_list);
323366 }
324- iterator
325- end() const
367+ YB_ATTR_nodiscard YB_PURE iterator
368+ end() const ynothrow
326369 {
327- return used_list.cend();
370+ return ystdex::end(used_list);
328371 }
329372 //@}
330373
331- iterator
374+ YB_ATTR_nodiscard iterator
332375 find(const key_type& k)
333376 {
334377 const auto i(used_cache.find(k));
335378
336379 return i != used_cache.end() ? used_list.refresh(i->second) : end();
337380 }
338- const_iterator
381+ YB_ATTR_nodiscard const_iterator
339382 find(const key_type& k) const
340383 {
341384 const auto i(used_cache.find(k));
@@ -345,13 +388,13 @@
345388
346389 //! \since build 646
347390 //@{
348- const used_cache_type&
391+ YB_ATTR_nodiscard YB_PURE const used_cache_type&
349392 get() const ynothrow
350393 {
351394 return used_cache;
352395 }
353396
354- const used_list_type&
397+ YB_ATTR_nodiscard YB_PURE const used_list_type&
355398 list() const ynothrow
356399 {
357400 return used_list;
@@ -359,7 +402,7 @@
359402 //@}
360403
361404 //! \since build 611
362- size_type
405+ YB_ATTR_nodiscard YB_PURE size_type
363406 size() const ynothrow
364407 {
365408 return used_cache.size();
@@ -369,28 +412,25 @@
369412
370413
371414 /*!
372-\brief 以指定的关键字查找作为缓存的无序关联容器,
373- 若没有找到使用指定的可调用对象和参数初始化内容。
374-\tparam _tMap 映射类型,可以是 std::map 、 std::unordered_map
415+\brief 以指定的关键字查找关联容器访问对应的元素并按需初始化其中的条目。
416+\tparam _tMap 映射类型,可以是 std::map 、std::unordered_map
375417 或 ystdex::used_list_cache 等。
418+\note 若没有找到使用指定的可调用对象和参数初始化内容。
419+\note 使用 ADL emplace_hint_in_place 。
420+\sa emplace_hint_in_place
421+\sa ystdex::try_emplace
376422 \since build 521
377423 */
378424 template<class _tMap, typename _tKey, typename _func, typename... _tParams>
379425 auto
380-cache_lookup(_tMap& cache, const _tKey& key, _func init, _tParams&&... args)
381- -> decltype((cache.begin()->second))
426+cache_lookup(_tMap& m, const _tKey& k, _func init, _tParams&&... args)
427+ -> decltype((m.begin()->second))
382428 {
383- auto i(cache.find(key));
384-
385- if(i == cache.end())
386- {
387- const auto pr(cache.emplace(key, init(yforward(args)...)));
388-
389- if(YB_UNLIKELY(!pr.second))
390- throw std::runtime_error("Cache insertion failed.");
391- i = pr.first;
392- }
393- return i->second;
429+ return ystdex::search_map_by([&](typename _tMap::const_iterator i){
430+ // XXX: Blocked. 'yforward' may cause G++ 5.2 silent crash.
431+ return emplace_hint_in_place(m, i, k,
432+ init(std::forward<_tParams>(args)...));
433+ }, m, k).first->second;
394434 }
395435
396436 } // namespace ystdex;
diff -r eaddd0235b7a -r 30522accfd38 YBase/include/ystdex/container.hpp
--- a/YBase/include/ystdex/container.hpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YBase/include/ystdex/container.hpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file container.hpp
1212 \ingroup YStandardEx
1313 \brief 通用容器操作。
14-\version r2345
14+\version r2610
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 338
1717 \par 创建时间:
1818 2012-09-12 01:36:20 +0800
1919 \par 修改时间:
20- 2022-02-26 22:49 +0800
20+ 2022-04-05 07:04 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -29,13 +29,14 @@
2929 #define YB_INC_ystdex_container_hpp_ 1
3030
3131 #include "iterator.hpp" // for "range.hpp", internal "integreal_sequence.hpp",
32-// std::initializer_list, ystdex::begin, ystdex::end, make_transform,
33-// std::declval, size, is_detected_convertible, std::distance,
34-// std::make_move_iterator, yassume, ystdex::cbegin, ystdex::cend, YAssert,
35-// std::piecewise_construct_t, std::piecewise_construct,
36-// enable_if_convertible_t;
32+// ystdex::begin, ystdex::end, ystdex::make_transform, lref, std::declval,
33+// std::make_move_iterator, is_detected_convertible, yassume, ystdex::cbegin,
34+// ystdex::cend, YAssert, std::distance, std::next, size, ystdex::range_size,
35+// std::piecewise_construct_t, std::tuple, std::piecewise_construct,
36+// std::forward_as_tuple, cond_or_t, is_detected, enable_if_convertible_t,
37+// and_;
3738 #include "algorithm.hpp" // for YB_VerifyIterator, sort_unique;
38-#include "function.hpp" // for ystdex::seq_apply;
39+#include "apply.hpp" // for ystdex::seq_apply;
3940 #include "utility.hpp" // for ystdex::as_const;
4041
4142 namespace ystdex
@@ -72,69 +73,6 @@
7273 //@}
7374
7475
75-//! \since build 546
76-namespace details
77-{
78-
79-//! \since build 663
80-template<class _type>
81-using range_size_t = decltype(size(std::declval<_type>()));
82-
83-//! \since build 663
84-template<class _type>
85-using has_range_size = is_detected_convertible<size_t, range_size_t, _type>;
86-
87-//! \since buld 730
88-template<typename _tRange>
89-yconstfn auto
90-range_size(const _tRange& c, true_) -> decltype(size(c))
91-{
92- return size(c);
93-}
94-//! \since buld 730
95-template<typename _tRange>
96-inline auto
97-range_size(const _tRange& c, false_)
98- -> decltype(std::distance(ystdex::begin(c), ystdex::end(c)))
99-{
100- return std::distance(ystdex::begin(c), ystdex::end(c));
101-}
102-
103-} // namespace details;
104-
105-/*!
106-\ingroup algorithms
107-\pre 参数指定的迭代器范围(若存在)有效。
108-\note 参数 \c first 和 \c last 指定迭代器范围。
109-\note 对不以迭代器指定的范围,使用 ystdex::begin 和 ystdex::end 取迭代器。
110-\note 确定为 const 迭代器时使用 ystdex::cbegin 和 ystdex::cend 代替。
111-*/
112-//@{
113-/*!
114-\brief 取范围大小。
115-\since build 546
116-
117-对 std::initializer_list 的实例直接返回大小,否则:
118-若可调用结果可转换为 size_t 的 ADL 函数 size 则使用 ADL size ;
119-否则使用 std::distance 计算范围迭代器确定范围大小。
120-*/
121-//@{
122-template<typename _tRange>
123-yconstfn auto
124-range_size(const _tRange& c)
125- -> decltype(details::range_size(c, details::has_range_size<_tRange>()))
126-{
127- return details::range_size(c, details::has_range_size<_tRange>());
128-}
129-template<typename _tElem>
130-yconstfn size_t
131-range_size(std::initializer_list<_tElem> il)
132-{
133- return size(il);
134-}
135-//@}
136-
137-
13876 /*!
13977 \brief 插入参数指定的元素到容器。
14078 \since build 274
@@ -248,6 +186,38 @@
248186 }
249187
250188
189+/*!
190+\ingroup functor
191+\brief 擦除容器前缀:移除容器起始至指定迭代器的元素。
192+\warning 非虚析构。
193+\since build 942
194+*/
195+template<class _tCon>
196+struct prefix_eraser
197+{
198+ using container_type = _tCon;
199+ using const_iterator = typename _tCon::const_iterator;
200+
201+ container_type& container;
202+ const_iterator position;
203+
204+ prefix_eraser(container_type& con) ynothrow
205+ : prefix_eraser(con, con.end())
206+ {}
207+ //! \pre 第二参数是第一参数的迭代器。
208+ prefix_eraser(container_type& con, const_iterator i)
209+ ynothrow
210+ : container(con), position(i)
211+ {}
212+
213+ void
214+ operator()() const ynothrow
215+ {
216+ container.erase(container.begin(), position);
217+ }
218+};
219+
220+
251221 //! \since build 488
252222 namespace details
253223 {
@@ -271,28 +241,28 @@
271241
272242 template<class _tCon, typename _tKey>
273243 YB_ATTR_nodiscard YB_PURE bool
274-exists(const _tCon& con, const _tKey& key, false_, false_)
244+exists(const _tCon& con, const _tKey& k, false_, false_)
275245 {
276- return std::find(ystdex::cbegin(con), ystdex::cend(con), key)
246+ return std::find(ystdex::cbegin(con), ystdex::cend(con), k)
277247 != ystdex::cend(con);
278248 }
279249 template<class _tCon, typename _tKey>
280250 YB_ATTR_nodiscard YB_PURE bool
281-exists(const _tCon& con, const _tKey& key, false_, true_)
251+exists(const _tCon& con, const _tKey& k, false_, true_)
282252 {
283- return con.count(key) != 0;
253+ return con.count(k) != 0;
284254 }
285255 template<class _tCon, typename _tKey>
286256 YB_ATTR_nodiscard YB_PURE bool
287-exists(const _tCon& con, const _tKey& key, false_)
257+exists(const _tCon& con, const _tKey& k, false_)
288258 {
289- return details::exists(con, key, false_(), has_mem_count<_tCon, _tKey>());
259+ return details::exists(con, k, false_(), has_mem_count<_tCon, _tKey>());
290260 }
291261 template<class _tCon, typename _tKey>
292262 YB_ATTR_nodiscard YB_PURE bool
293-exists(const _tCon& con, const _tKey& key, true_)
263+exists(const _tCon& con, const _tKey& k, true_)
294264 {
295- return con.find(key) != ystdex::cend(con);
265+ return con.find(k) != ystdex::cend(con);
296266 }
297267 //@}
298268
@@ -454,9 +424,9 @@
454424 */
455425 template<class _tCon, typename _tKey>
456426 YB_ATTR_nodiscard YB_PURE inline bool
457-exists(const _tCon& con, const _tKey& key)
427+exists(const _tCon& con, const _tKey& k)
458428 {
459- return details::exists(con, key, details::has_mem_find<_tCon, _tKey>());
429+ return details::exists(con, k, details::has_mem_find<_tCon, _tKey>());
460430 }
461431
462432 /*!
@@ -611,21 +581,22 @@
611581
612582 /*!
613583 \brief 移除 const_iterator 的 const 限定。
614-\since build 680
584+\since build 942
615585 */
616586 //@{
617587 template<class _tCon>
618-inline typename _tCon::iterator
619-cast_mutable(_tCon& con, typename _tCon::const_iterator i)
588+YB_ATTR_nodiscard YB_PURE inline typename _tCon::iterator
589+cast_mutable(_tCon& con, typename _tCon::const_iterator i) ynothrow
620590 {
621591 return con.erase(i, i);
622592 }
593+//! \note 对迭代器数据成员使用 ADL cast_mutable 。
623594 template<class _tCon, typename _type>
624-inline std::pair<typename _tCon::iterator, _type>
625-cast_mutable(_tCon& con,
626- const std::pair<typename _tCon::const_iterator, _type>& pr)
595+YB_ATTR_nodiscard YB_PURE inline std::pair<typename _tCon::iterator, _type>
596+cast_mutable(_tCon& con, const std::pair<typename _tCon::const_iterator,
597+ _type>& pr) ynoexcept(is_nothrow_constructible<_type>())
627598 {
628- return {ystdex::cast_mutable(con, pr.first), pr.second};
599+ return {cast_mutable(con, pr.first), pr.second};
629600 }
630601 //@}
631602
@@ -776,11 +747,11 @@
776747
777748 /*!
778749 \ingroup metafunctions
779-\brief 判断关联容器、键和参数类型是否可以按 map 的方式无转移地构造。
750+\brief 判断容器、键和参数类型是否可以按 map 的方式无转移地构造。
780751 \since build 681
781752 */
782-template<class _tAssocCon, typename _tKey, typename... _tParams>
783-using is_piecewise_mapped = is_constructible<typename _tAssocCon::value_type,
753+template<class _tCon, typename _tKey, typename... _tParams>
754+using is_piecewise_mapped = is_constructible<typename _tCon::value_type,
784755 std::piecewise_construct_t, std::tuple<_tKey&&>, std::tuple<_tParams&&...>>;
785756
786757
@@ -788,62 +759,102 @@
788759 namespace details
789760 {
790761
762+//! \since build 942
763+//@{
764+template<class _tAssocCon>
765+using mem_key_compare_t = decltype(_tAssocCon::key_compare);
766+
767+template<class _tAssocCon>
768+using key_comp_res_t = decltype(std::declval<_tAssocCon&>().key_comp());
769+
770+template<class _tAssocCon>
771+using has_mem_key_comp_impl
772+ = is_detected_convertible<typename _tAssocCon::key_compare,
773+ details::key_comp_res_t, _tAssocCon>;
774+
775+template<class _tAssocCon, typename _tKey = typename _tAssocCon::key_type>
776+using lower_bound_res_t = decltype(std::declval<_tAssocCon&>().lower_bound(
777+ std::declval<const _tKey&>()));
778+//@}
779+
780+
791781 //! \since build 708
792782 template<class _tAssocCon>
793783 struct assoc_con_traits
794784 {
795- //! \since build 730
785+ //! \since build 942
796786 //@{
787+ // XXX: For general cases, the hint is ignored. This is also true in the
788+ // implementations of the ISO C++ unordered containers in libstdc++ and
789+ // libc++. It is even more unlikely to have some efficient way without
790+ // knowing the internal implementation details of the containers.
797791 template<typename _tKey, typename... _tParams>
798792 static inline typename _tAssocCon::iterator
799- emplace_hint_in_place(false_, _tAssocCon& con,
793+ emplace_hint_in_place(false_, false_, _tAssocCon& con,
794+ typename _tAssocCon::const_iterator, _tKey&&, _tParams&&... args)
795+ {
796+ return con.emplace(yforward(args)...).first;
797+ }
798+ template<typename _tKey, typename... _tParams>
799+ static inline typename _tAssocCon::iterator
800+ emplace_hint_in_place(false_, true_, _tAssocCon& con,
801+ typename _tAssocCon::const_iterator, _tKey&& k, _tParams&&... args)
802+ {
803+ return con.emplace(std::piecewise_construct, std::forward_as_tuple(
804+ yforward(k)), std::forward_as_tuple(yforward(args)...)).first;
805+ }
806+ // XXX: Assume %_tAssocCon is ordered (as %_tOrderCon) in the following 2
807+ // overloads.
808+ template<typename _tKey, typename... _tParams>
809+ static inline typename _tAssocCon::iterator
810+ emplace_hint_in_place(true_, false_, _tAssocCon& con,
800811 typename _tAssocCon::const_iterator hint, _tKey&&, _tParams&&... args)
801812 {
802813 return con.emplace_hint(hint, yforward(args)...);
803814 }
804815 template<typename _tKey, typename... _tParams>
805816 static inline typename _tAssocCon::iterator
806- emplace_hint_in_place(true_, _tAssocCon& con,
817+ emplace_hint_in_place(true_, true_, _tAssocCon& con,
807818 typename _tAssocCon::const_iterator hint, _tKey&& k, _tParams&&... args)
808819 {
809820 return con.emplace_hint(hint, std::piecewise_construct,
810821 std::forward_as_tuple(yforward(k)),
811822 std::forward_as_tuple(yforward(args)...));
812823 }
824+ //@}
813825
826+ //! \since build 942
827+ //@{
814828 static inline const typename _tAssocCon::key_type&
815- extract_key(false_, const typename _tAssocCon::value_type& val)
829+ extract_key(false_, const typename _tAssocCon::value_type& val) ynothrow
816830 {
817831 return val;
818832 }
819833 static inline const typename _tAssocCon::key_type&
820- extract_key(true_, const typename _tAssocCon::value_type& val)
834+ extract_key(true_, const typename _tAssocCon::value_type& val) ynothrow
821835 {
822836 return val.first;
823837 }
824- //@}
825838
826- //! \since build 792
827- //@{
828839 static inline typename _tAssocCon::value_type&
829- extract_mapped(false_, typename _tAssocCon::value_type& val)
840+ extract_mapped(false_, typename _tAssocCon::value_type& val) ynothrow
830841 {
831842 return val;
832843 }
833844 static inline const typename _tAssocCon::value_type&
834- extract_mapped(false_, const typename _tAssocCon::value_type& val)
845+ extract_mapped(false_, const typename _tAssocCon::value_type& val) ynothrow
835846 {
836847 return val;
837848 }
838849 template<typename _tMap = _tAssocCon>
839850 static inline typename _tMap::mapped_type&
840- extract_mapped(true_, typename _tAssocCon::value_type& val)
851+ extract_mapped(true_, typename _tAssocCon::value_type& val) ynothrow
841852 {
842853 return val.second;
843854 }
844855 template<typename _tMap = _tAssocCon>
845856 static inline const typename _tMap::mapped_type&
846- extract_mapped(true_, const typename _tAssocCon::value_type& val)
857+ extract_mapped(true_, const typename _tAssocCon::value_type& val) ynothrow
847858 {
848859 return val.second;
849860 }
@@ -853,7 +864,28 @@
853864 template<class _type>
854865 using mapped_type_t = typename _type::mapped_type;
855866
856-} // unnamed namespace details;
867+} // namespace details;
868+
869+/*!
870+\ingroup type_traits_operations
871+\since build 942
872+*/
873+//@{
874+/*!
875+\brief 判断容器是否可按键类型比较。
876+\note 判断键类型即判断存在 key_compare 成员类型;若不存在,则视为不可比较。
877+\note 按键类型可比较,即具有返回可转换为 key_compare 类型的值的 key_comp 成员函数。
878+*/
879+template<class _tCon>
880+using has_mem_key_comp = cond_or_t<is_detected<details::mem_key_compare_t,
881+ _tCon>, false_, details::has_mem_key_comp_impl, _tCon>;
882+
883+//! \brief 判断容器是否具有接受指定键类型的值返回迭代器的 lower_bound 成员函数。
884+template<class _tCon, typename _tKey = typename _tCon::key_type>
885+using has_mem_lower_bound = is_detected_convertible<typename
886+ _tCon::const_iterator, details::lower_bound_res_t, _tCon, _tKey>;
887+//@}
888+
857889
858890 /*!
859891 \brief 带有提示的原地插入构造。
@@ -865,45 +897,95 @@
865897 _tKey&& k, _tParams&&... args) -> typename _tAssocCon::iterator
866898 {
867899 return details::assoc_con_traits<_tAssocCon>::emplace_hint_in_place(
868- is_piecewise_mapped<_tAssocCon, _tKey, _tParams...>(), con, hint,
869- yforward(k), yforward(args)...);
900+ has_mem_lower_bound<_tAssocCon>(), is_piecewise_mapped<_tAssocCon,
901+ _tKey, _tParams...>(), con, hint, yforward(k), yforward(args)...);
870902 }
871903
872-/*!
873-\brief 从关联容器的值取键。
874-\since build 679
875-*/
904+//! \since build 942
905+//@{
906+//! \brief 从关联容器的值取键。
876907 template<class _tAssocCon, typename _type,
877908 yimpl(typename = enable_if_convertible_t<const _type&,
878909 const typename _tAssocCon::value_type&>)>
879910 inline const typename _tAssocCon::key_type&
880-extract_key(const _type& val)
911+extract_key(const _type& val) ynothrow
881912 {
882913 return details::assoc_con_traits<_tAssocCon>::extract_key(
883914 is_detected<details::mapped_type_t, _tAssocCon>(), val);
884915 }
885916
886-/*!
887-\brief 从关联容器的值取映射的值。
888-\since build 792
889-*/
917+//! \brief 从关联容器的值取映射的值。
890918 template<class _tAssocCon, typename _type,
891919 yimpl(typename = enable_if_convertible_t<_type&,
892920 typename _tAssocCon::value_type&>)>
893921 inline auto
894-extract_mapped(_type& val)
922+extract_mapped(_type& val) ynothrow
895923 -> decltype(details::assoc_con_traits<_tAssocCon>::extract_mapped(
896924 is_detected<details::mapped_type_t, _tAssocCon>(), val))
897925 {
898926 return details::assoc_con_traits<_tAssocCon>::extract_mapped(
899927 is_detected<details::mapped_type_t, _tAssocCon>(), val);
900928 }
929+//@}
901930
902931
903932 //! \since build 792
904933 namespace details
905934 {
906935
936+//! \since build 942
937+//@{
938+template<class _tAssocCon, typename _tKey>
939+YB_ATTR_nodiscard YB_PURE std::pair<typename _tAssocCon::const_iterator, bool>
940+search_map(false_, const _tAssocCon& con, const _tKey& k)
941+{
942+ const auto i(con.find(k));
943+
944+ return {i, i == ystdex::end(con)};
945+}
946+//! \since build 942
947+template<class _tOrdCon, typename _tKey>
948+YB_ATTR_nodiscard YB_PURE std::pair<typename _tOrdCon::const_iterator, bool>
949+search_map(true_, const _tOrdCon& con, const _tKey& k)
950+{
951+ const auto i(con.lower_bound(k));
952+
953+ return {i, i == ystdex::end(con)
954+ || con.key_comp()(k, extract_key<_tOrdCon>(*i))};
955+}
956+//! \since build 942
957+template<class _tAssocCon, typename _tKey>
958+YB_ATTR_nodiscard YB_PURE inline
959+ std::pair<typename _tAssocCon::const_iterator, bool>
960+search_map(false_, const _tAssocCon& con, typename _tAssocCon::const_iterator,
961+ const _tKey& k)
962+{
963+ // XXX: See the comments in %details::assoc_con_traits.
964+ return details::search_map(false_(), con, k);
965+}
966+//! \since build 942
967+template<class _tOrdCon, typename _tKey>
968+YB_ATTR_nodiscard YB_PURE std::pair<typename _tOrdCon::const_iterator, bool>
969+search_map(true_, const _tOrdCon& con, typename _tOrdCon::const_iterator hint,
970+ const _tKey& k)
971+{
972+ if(!con.empty())
973+ {
974+ const auto& comp(con.key_comp());
975+ const bool fit_before(hint == ystdex::cbegin(con)
976+ || bool(comp(extract_key<_tOrdCon>(*std::prev(hint)), k))),
977+ fit_after(hint == ystdex::cend(con)
978+ || bool(comp(k, extract_key<_tOrdCon>(*hint))));
979+
980+ if(fit_before == fit_after)
981+ return {hint, fit_before && fit_after};
982+ return details::search_map(false_(), con, k);
983+ }
984+ yconstraint(hint == ystdex::cend(con));
985+ return {hint, true};
986+}
987+//@}
988+
907989 /*!
908990 \since build 681
909991 \note 使用 ADL extract_mapped 。
@@ -925,31 +1007,35 @@
9251007
9261008
9271009 /*!
928-\return 一个用于表示结果的 std::pair 值,其成员 \c first 为迭代器,
929- \c second 表示是否没有找到。
930-\note 使用 ystdex::cend 和 extract_key 。
1010+\return 一个用于表示结果的 std::pair 实例的值,其成员 \c first 为迭代器,
1011+ \c second 表示未搜索到指定的键。
1012+\note 对非 const 容器使用 ADL cast_mutable 转换迭代器。
1013+\note 对使用提示参数的实现使用 ystdex::cend 和 extract_key 。
9311014 */
9321015 //@{
9331016 /*!
9341017 \brief 按指定键搜索指定关联容器。
935-\since build 677
1018+\since build 942
9361019 */
9371020 //@{
938-template<class _tAssocCon, typename _tKey>
939-YB_ATTR_nodiscard YB_PURE std::pair<typename _tAssocCon::const_iterator, bool>
1021+template<class _tAssocCon, typename _tKey = typename _tAssocCon::key_type>
1022+YB_ATTR_nodiscard YB_PURE inline
1023+ std::pair<typename _tAssocCon::const_iterator, bool>
9401024 search_map(const _tAssocCon& con, const _tKey& k)
9411025 {
942- const auto i(con.lower_bound(k));
943-
944- return {i, i == ystdex::end(con)
945- || con.key_comp()(k, extract_key<_tAssocCon>(*i))};
1026+ // XXX: Sole %has_mem_lower_bound is not sufficient even for standard
1027+ // containers, since (at least) %std::unordered_map in Microsoft VC++
1028+ // has non-standard extensions (actually warned as C4996 by
1029+ // %_DEPRECATE_STDEXT_HASH_LOWER_BOUND in VC++ 2022), leading to
1030+ // ill-formed calls (e.g. in VC++, error C2039).
1031+ return details::search_map(and_<has_mem_lower_bound<_tAssocCon, const _tKey>,
1032+ has_mem_key_comp<_tAssocCon>>(), con, k);
9461033 }
947-template<class _tAssocCon, typename _tKey>
1034+template<class _tAssocCon, typename _tKey = typename _tAssocCon::key_type>
9481035 YB_ATTR_nodiscard YB_PURE inline std::pair<typename _tAssocCon::iterator, bool>
9491036 search_map(_tAssocCon& con, const _tKey& k)
9501037 {
951- return
952- ystdex::cast_mutable(con, ystdex::search_map(ystdex::as_const(con), k));
1038+ return cast_mutable(con, ystdex::search_map(ystdex::as_const(con), k));
9531039 }
9541040 //@}
9551041 //! \since build 680
@@ -958,35 +1044,26 @@
9581044 \brief 按指定键和提示的迭代器位置搜索指定关联容器。
9591045 \pre 容器非空或提示的迭代器位置指向尾部。
9601046 \note 使用 ystdex::cbegin 。
1047+\since build 942
9611048 */
9621049 //@{
963-template<class _tAssocCon, typename _tKey>
964-YB_ATTR_nodiscard YB_PURE std::pair<typename _tAssocCon::const_iterator, bool>
1050+template<class _tAssocCon, typename _tKey = typename _tAssocCon::key_type>
1051+YB_ATTR_nodiscard YB_PURE inline
1052+ std::pair<typename _tAssocCon::const_iterator, bool>
9651053 search_map(const _tAssocCon& con, typename _tAssocCon::const_iterator hint,
9661054 const _tKey& k)
9671055 {
968- if(!con.empty())
969- {
970- const auto& comp(con.key_comp());
971- const bool fit_before(hint == ystdex::cbegin(con)
972- || bool(comp(extract_key<_tAssocCon>(*std::prev(hint)), k))),
973- fit_after(hint == ystdex::cend(con)
974- || bool(comp(k, extract_key<_tAssocCon>(*hint))));
975-
976- if(fit_before == fit_after)
977- return {hint, fit_before && fit_after};
978- return ystdex::search_map(con, k);
979- }
980- yconstraint(hint == ystdex::cend(con));
981- return {hint, true};
1056+ // XXX: Assume %_tAssonCon is an ordered associative container type, so
1057+ // %key_comp is also available.
1058+ return details::search_map(has_mem_lower_bound<_tAssocCon>(), con, hint, k);
9821059 }
983-template<class _tAssocCon, typename _tKey>
1060+template<class _tAssocCon, typename _tKey = typename _tAssocCon::key_type>
9841061 YB_ATTR_nodiscard YB_PURE inline std::pair<typename _tAssocCon::iterator, bool>
9851062 search_map(_tAssocCon& con, typename _tAssocCon::const_iterator hint,
9861063 const _tKey& k)
9871064 {
988- return ystdex::cast_mutable(con,
989- ystdex::search_map(ystdex::as_const(con), hint, k));
1065+ return
1066+ cast_mutable(con, ystdex::search_map(ystdex::as_const(con), hint, k));
9901067 }
9911068 //@}
9921069
@@ -1012,7 +1089,7 @@
10121089 inline std::pair<typename _tAssocCon::iterator, bool>
10131090 search_map_by(_func f, _tAssocCon& con, _tParams&&... args)
10141091 {
1015- return ystdex::cast_mutable(con,
1092+ return cast_mutable(con,
10161093 ystdex::search_map_by(f, ystdex::as_const(con), yforward(args)...));
10171094 }
10181095 //@}
@@ -1032,8 +1109,8 @@
10321109 std::pair<typename _tAssocCon::iterator, bool>
10331110 try_emplace(_tAssocCon& con, _tKey&& k, _tParams&&... args)
10341111 {
1035- // XXX: Blocked. 'yforward' may cause G++ 5.2 silent crash.
10361112 return ystdex::search_map_by([&](typename _tAssocCon::const_iterator i){
1113+ // XXX: Blocked. 'yforward' may cause G++ 5.2 silent crash.
10371114 return emplace_hint_in_place(con, i, yforward(k),
10381115 std::forward<_tParams>(args)...);
10391116 }, con, k);
@@ -1044,8 +1121,8 @@
10441121 try_emplace_hint(_tAssocCon& con, typename _tAssocCon::const_iterator hint,
10451122 _tKey&& k, _tParams&&... args)
10461123 {
1047- // XXX: Blocked. 'yforward' may cause G++ 5.2 silent crash.
10481124 return ystdex::search_map_by([&](typename _tAssocCon::const_iterator i){
1125+ // XXX: Blocked. 'yforward' may cause G++ 5.2 silent crash.
10491126 return emplace_hint_in_place(con, i, yforward(k),
10501127 std::forward<_tParams>(args)...);
10511128 }, con, hint, k);
diff -r eaddd0235b7a -r 30522accfd38 YBase/include/ystdex/function.hpp
--- a/YBase/include/ystdex/function.hpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YBase/include/ystdex/function.hpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file function.hpp
1212 \ingroup YStandardEx
1313 \brief 函数基本操作和调用包装对象。
14-\version r4953
14+\version r5052
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 847
1717 \par 创建时间:
1818 2018-12-13 01:24:06 +0800
1919 \par 修改时间:
20- 2022-02-14 18:05 +0800
20+ 2022-03-21 12:08 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -209,118 +209,6 @@
209209 //@}
210210
211211
212-//! \since build 594
213-//@{
214-//! \brief 统计函数参数列表中的参数个数。
215-template<typename... _tParams>
216-yconstfn size_t
217-sizeof_params(_tParams&&...) ynothrow
218-{
219- return sizeof...(_tParams);
220-}
221-
222-
223-//! \since build 412
224-//@{
225-//! \brief 变长参数操作模板。
226-//@{
227-template<size_t _vN>
228-struct variadic_param
229-{
230- //! \since build 594
231- template<typename _type, typename... _tParams>
232- static yconstfn auto
233- get(_type&&, _tParams&&... args) ynothrow
234- -> decltype(variadic_param<_vN - 1>::get(yforward(args)...))
235- {
236- static_assert(sizeof...(args) == _vN,
237- "Wrong variadic arguments number found.");
238-
239- return variadic_param<_vN - 1>::get(yforward(args)...);
240- }
241-};
242-
243-template<>
244-struct variadic_param<0U>
245-{
246- //! \since build 594
247- template<typename _type>
248- static yconstfn auto
249- get(_type&& arg) ynothrow -> decltype(yforward(arg))
250- {
251- return yforward(arg);
252- }
253-};
254-//@}
255-
256-
257-/*!
258-\brief 取指定位置的变长参数。
259-\tparam _vN 表示参数位置的非负数,从左开始计数,第一参数为 0 。
260-*/
261-template<size_t _vN, typename... _tParams>
262-yconstfn auto
263-varg(_tParams&&... args) ynothrow
264- -> decltype(variadic_param<_vN>::get(yforward(args)...))
265-{
266- static_assert(_vN < sizeof...(args),
267- "Out-of-range index of variadic argument found.");
268-
269- return variadic_param<_vN>::get(yforward(args)...);
270-}
271-//@}
272-
273-
274-//! \see 关于调用参数类型: ISO C++11 30.3.1.2 [thread.thread.constr] 。
275-//@{
276-//! \brief 顺序链式调用。
277-//@{
278-template<typename _func>
279-inline void
280-chain_apply(_func&& f) ynothrow
281-{
282- return yforward(f);
283-}
284-template<typename _func, typename _type, typename... _tParams>
285-inline void
286-chain_apply(_func&& f, _type&& arg, _tParams&&... args)
287- ynoexcept_spec(ystdex::chain_apply(
288- yforward(yforward(f)(yforward(arg))), yforward(args)...))
289-{
290- return ystdex::chain_apply(yforward(yforward(f)(yforward(arg))),
291- yforward(args)...);
292-}
293-//@}
294-
295-//! \brief 顺序递归调用。
296-//@{
297-template<typename _func>
298-inline void
299-seq_apply(_func&&) ynothrow
300-{}
301-//! \since build 595
302-template<typename _func, typename _type, typename... _tParams>
303-inline void
304-seq_apply(_func&& f, _type&& arg, _tParams&&... args)
305- ynoexcept_spec(yimpl(yunseq(0, (void(yforward(f)(yforward(args))), 0)...)))
306-{
307- yforward(f)(yforward(arg));
308- ystdex::seq_apply(yforward(f), yforward(args)...);
309-}
310-//@}
311-
312-//! \brief 无序调用。
313-template<typename _func, typename... _tParams>
314-inline void
315-unseq_apply(_func&& f, _tParams&&... args)
316- ynoexcept_spec(yimpl(yunseq((void(yforward(f)(yforward(args))), 0)...)))
317-{
318- yunseq((void(yforward(f)(yforward(args))), 0)...);
319-}
320-//@}
321-//@}
322-
323-
324212 //! \ingroup transformation_traits
325213 //@{
326214 //! \brief 取参数列表。
diff -r eaddd0235b7a -r 30522accfd38 YBase/include/ystdex/meta.hpp
--- a/YBase/include/ystdex/meta.hpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YBase/include/ystdex/meta.hpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file meta.hpp
1212 \ingroup YStandardEx
1313 \brief 通用元编程设施。
14-\version r1988
14+\version r2002
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 832
1717 \par 创建时间:
1818 2018-07-23 17:22:28 +0800
1919 \par 修改时间:
20- 2022-02-28 21:08 +0800
20+ 2022-03-18 03:20 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -1077,7 +1077,22 @@
10771077 //@}
10781078
10791079
1080-//! \since build 843
1080+/*!
1081+\ingroup transformation_traits
1082+\since build 942
1083+*/
1084+//@{
1085+template<typename... _types>
1086+using empty_pack_t = bool_<sizeof...(_types) == 0>;
1087+
1088+template<typename... _types>
1089+using sizeof_pack_t = size_t_<sizeof...(_types)>;
1090+//@}
1091+
1092+/*!
1093+\ingroup unary_type_traits
1094+\since build 843
1095+*/
10811096 template<typename _type>
10821097 using sizeof_t = size_t_<sizeof(_type)>;
10831098
diff -r eaddd0235b7a -r 30522accfd38 YBase/include/ystdex/placement.hpp
--- a/YBase/include/ystdex/placement.hpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YBase/include/ystdex/placement.hpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file placement.hpp
1212 \ingroup YStandardEx
1313 \brief 放置对象管理操作。
14-\version r1117
14+\version r1127
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 715
1717 \par 创建时间:
1818 2016-08-03 18:56:31 +0800
1919 \par 修改时间:
20- 2022-03-11 22:38 +0800
20+ 2022-03-21 12:08 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -89,8 +89,14 @@
8989 #endif
9090 //@}
9191
92+/*! \defgroup guards Guards
93+\brief 守卫:销毁时确保外部不变量有效的类型或实例是这些类型的模板。
94+\warning 一般非虚析构。
95+\since build 942
96+*/
97+
9298 /*! \defgroup allocators Allcators
93-\brief 分配器。
99+\brief 分配器:满足分配器要求的类型或实例是这些类型的模板。
94100 \warning 一般非虚析构。
95101 \see WG21 N4606 17.6.3.5 [allocator.requirements] 。
96102 \since build 746
@@ -925,7 +931,10 @@
925931 };
926932
927933
928-//! \brief 独占放置存储的对象所有权的指针。
934+/*!
935+\ingroup guards
936+\brief 独占放置存储的对象所有权的指针。
937+*/
929938 template<typename _type, typename _tPointer = _type*>
930939 using placement_ptr
931940 = std::unique_ptr<_type, placement_delete<_type, _tPointer>>;
diff -r eaddd0235b7a -r 30522accfd38 YBase/include/ystdex/range.hpp
--- a/YBase/include/ystdex/range.hpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YBase/include/ystdex/range.hpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file range.hpp
1212 \ingroup YStandardEx
1313 \brief 范围操作。
14-\version r1167
14+\version r1225
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 624
1717 \par 创建时间:
1818 2015-08-18 22:33:54 +0800
1919 \par 修改时间:
20- 2022-02-05 09:01 +0800
20+ 2022-03-21 12:08 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -28,10 +28,11 @@
2828 #ifndef YB_INC_ystdex_range_hpp_
2929 #define YB_INC_ystdex_range_hpp_ 1
3030
31-#include "addressof.hpp" // for <initializer_list>, ystdex::addressof,
32-// enable_if_t;
31+#include "addressof.hpp" // for "addressof.hpp", <initializer_list>,
32+// ystdex::addressof, enable_if_t, std::initializer_list;
3333 #include <iterator> // for <iteartor>, std::reverse_iterator, std::begin,
3434 // std::end;
35+#include <algorithm> // for internal <algorithm>, std::distance;
3536 // XXX: For efficiency, <valarray> is not supported here. Use %std names
3637 // instead.
3738
@@ -681,6 +682,68 @@
681682 = decltype(ystdex::cbegin(std::declval<const _tRange&>()));
682683 //@}
683684
685+//! \since build 546
686+namespace details
687+{
688+
689+//! \since build 663
690+template<class _type>
691+using range_size_t = decltype(size(std::declval<_type>()));
692+
693+//! \since build 663
694+template<class _type>
695+using has_range_size = is_detected_convertible<size_t, range_size_t, _type>;
696+
697+//! \since buld 730
698+template<typename _tRange>
699+yconstfn auto
700+range_size(const _tRange& c, true_) -> decltype(size(c))
701+{
702+ return size(c);
703+}
704+//! \since buld 730
705+template<typename _tRange>
706+inline auto
707+range_size(const _tRange& c, false_)
708+ -> decltype(std::distance(ystdex::begin(c), ystdex::end(c)))
709+{
710+ return std::distance(ystdex::begin(c), ystdex::end(c));
711+}
712+
713+} // namespace details;
714+
715+/*!
716+\ingroup algorithms
717+\pre 参数指定的迭代器范围(若存在)有效。
718+\note 参数 \c first 和 \c last 指定迭代器范围。
719+\note 对不以迭代器指定的范围,使用 ystdex::begin 和 ystdex::end 取迭代器。
720+\note 确定为 const 迭代器时使用 ystdex::cbegin 和 ystdex::cend 代替。
721+*/
722+//@{
723+/*!
724+\brief 取范围大小。
725+\since build 546
726+
727+对 std::initializer_list 的实例直接返回大小,否则:
728+若可调用结果可转换为 size_t 的 ADL 函数 size 则使用 ADL size ;
729+否则使用 std::distance 计算范围迭代器确定范围大小。
730+*/
731+//@{
732+template<typename _tRange>
733+yconstfn auto
734+range_size(const _tRange& c)
735+ -> decltype(details::range_size(c, details::has_range_size<_tRange>()))
736+{
737+ return details::range_size(c, details::has_range_size<_tRange>());
738+}
739+template<typename _tElem>
740+yconstfn size_t
741+range_size(std::initializer_list<_tElem> il)
742+{
743+ return size(il);
744+}
745+//@}
746+
684747 } // namespace ystdex;
685748
686749 #endif
diff -r eaddd0235b7a -r 30522accfd38 YBase/include/ystdex/type_op.hpp
--- a/YBase/include/ystdex/type_op.hpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YBase/include/ystdex/type_op.hpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file type_op.hpp
1212 \ingroup YStandardEx
1313 \brief C++ 类型操作。
14-\version r2922
14+\version r2944
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 201
1717 \par 创建时间:
1818 2011-04-14 08:54:25 +0800
1919 \par 修改时间:
20- 2022-02-26 22:51 +0800
20+ 2022-03-18 03:23 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -32,9 +32,9 @@
3232 #define YB_INC_ystdex_type_op_hpp_ 1
3333
3434 #include "variadic.hpp" // for "variadic.hpp", is_class, std::declval,
35-// is_detected, vseq::apply, _t, bool_, is_void, equal_t, is_same,
36-// detected_or_t, remove_reference_t, and_, cond_t, is_enum, vdefer,
37-// underlying_type_t, common_type_t;
35+// vseq::ctor_of_t, is_detected, vseq::apply, _t, bool_, is_void, equal_t,
36+// is_same, detected_or_t, remove_reference_t, and_, cond_t, is_enum, vdefer,
37+// underlying_type_t, empty_pack_t, vseq::front_t, common_type_t;
3838
3939 namespace ystdex
4040 {
@@ -272,6 +272,27 @@
272272 using cond_or_t = _t<cond_or<_tCond, _tDefault, _gOp, _tParams...>>;
273273 //@}
274274
275+//! \since build 942
276+//@{
277+/*!
278+\brief 参数包判断,若为空参数包使用默认类型。
279+\sa cond_or_t
280+\sa empty_pack_t
281+*/
282+template<typename _tDefault, template<typename...> class _gOp,
283+ typename... _tParams>
284+using nonempty_pack_or_t = cond_or_t<not_<empty_pack_t<_tParams...>>, _tDefault,
285+ _gOp, _tParams...>;
286+
287+/*!
288+\brief 取参数包的第一个类型。
289+\note 若类型不存在,则视为 void 。
290+*/
291+template<typename... _tParams>
292+using head_of_t
293+ = nonempty_pack_or_t<void, vseq::front_t, empty_base<_tParams...>>;
294+//@}
295+
275296 /*!
276297 \brief 取公共非空类型:若第一参数为非空类型则为第一参数,否则从其余参数推断。
277298 \since build 562
diff -r eaddd0235b7a -r 30522accfd38 YBase/source/ystdex/memory_resource.cpp
--- a/YBase/source/ystdex/memory_resource.cpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YBase/source/ystdex/memory_resource.cpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file memory_resource.cpp
1212 \ingroup YStandardEx
1313 \brief 存储资源。
14-\version r1798
14+\version r1805
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 14:37 +0800
20+ 2022-03-30 12:10 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -34,11 +34,10 @@
3434 #if YB_Has_memory_resource != 1
3535 # include <atomic> // for std::atomic;
3636 #endif
37-#include "ystdex/map.hpp" // for map, greater;
3837 #include "ystdex/pointer.hpp" // for tidy_ptr;
39-#include "ystdex/algorithm.hpp" // for std::max, std::min,
38+#include "ystdex/scope_guard.hpp" // for unique_guard, ystdex::dismiss;
39+#include "ystdex/algorithm.hpp" // for std::min, std::max,
4040 // ystdex::lower_bound_n;
41-#include "ystdex/scope_guard.hpp" // for unique_guard, ystdex::dismiss;
4241
4342 namespace ystdex
4443 {
@@ -268,7 +267,7 @@
268267 if(alignment > 1)
269268 {
270269 // TODO: Record 'sizeof' value for debugging?
271- // TODO: Extract as %::operator new with extended alignment?
270+ // TODO: Provide as an additonal %::operator new with extended alignment?
272271 // NOTE: The checks are necessary to prevent wrapping of the
273272 // results of '+'. See also https://gcc.gnu.org/bugzilla/show_bug.cgi?id=19351.
274273 if(bytes + alignment > bytes)
@@ -369,7 +368,7 @@
369368 static_assert(is_power_of_2_positive(largest_required_pool_block_limit),
370369 "Invalid limit value found.");
371370
372- // TODO: Use interval arithmetic libraries.
371+ // TODO: Add and use interval arithmetic library calls.
373372 if(opts.max_blocks_per_chunk - 1 >= max_blocks_per_chunk_limit)
374373 opts.max_blocks_per_chunk = max_blocks_per_chunk_limit;
375374 if(opts.largest_required_pool_block - 1
@@ -764,7 +763,8 @@
764763 : upstream_rsrc(upstream), next_buffer_size([](size_t size) ynothrow{
765764 // NOTE: Since 'mono_alloc_max == -yalignof(monobuf_header)',
766765 // 'size < mono_alloc_max' implies that
767- // 'size + yalignof(monobuf_header) - 1' does not overflow.
766+ // 'size + yalignof(monobuf_header) - 1' will always representable in
767+ // the range of %size_t.
768768 return size < mono_alloc_min ? mono_alloc_min : (size >= mono_alloc_max
769769 ? mono_alloc_max : ((size + yalignof(monobuf_header) - 1)
770770 & mono_alloc_max));
diff -r eaddd0235b7a -r 30522accfd38 YFramework/include/NPL/Dependency.h
--- a/YFramework/include/NPL/Dependency.h Thu Mar 17 22:05:52 2022 +0800
+++ b/YFramework/include/NPL/Dependency.h Tue Apr 05 16:56:05 2022 +0800
@@ -1,5 +1,5 @@
11 /*
2- © 2015-2021 FrankHB.
2+ © 2015-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 Dependency.h
1212 \ingroup NPL
1313 \brief 依赖管理。
14-\version r500
14+\version r549
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 623
1717 \par 创建时间:
1818 2015-08-09 22:12:37 +0800
1919 \par 修改时间:
20- 2021-11-04 02:51 +0800
20+ 2022-03-26 09:25 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -119,65 +119,65 @@
119119 namespace Forms
120120 {
121121
122-/*!
123-\brief 加载代码调用。
124-\since build 889
125-*/
126-template<typename _fCallable>
122+//! \since build 942
123+//@{
124+//! \brief 加载代码调用。
125+template<typename _fCallable, typename... _tParams>
127126 void
128-InvokeIn(ContextNode& ctx, _fCallable&& f)
127+InvokeIn(ContextNode& ctx, _fCallable&& f, _tParams&&... args)
129128 {
130129 EnvironmentGuard gd(ctx,
131130 NPL::SwitchToFreshEnvironment(ctx, ValueObject(ctx.WeakenRecord())));
132131
133- ystdex::invoke(f);
132+ ystdex::invoke(yforward(f), yforward(args)...);
134133 }
135134
136135 /*!
137136 \brief 加载代码作为模块。
138137 \return 作为环境模块的环境对象强引用。
139138 \post 返回值非空,指定冻结的环境。
140-\since build 838
141139 */
142-template<typename _fCallable>
140+template<typename _fCallable, typename... _tParams>
143141 shared_ptr<Environment>
144-GetModuleFor(ContextNode& ctx, _fCallable&& f)
142+GetModuleFor(ContextNode& ctx, _fCallable&& f, _tParams&&... args)
145143 {
146144 EnvironmentGuard gd(ctx,
147145 NPL::SwitchToFreshEnvironment(ctx, ValueObject(ctx.WeakenRecord())));
148146
149- ystdex::invoke(f);
147+ ystdex::invoke(yforward(f), yforward(args)...);
150148 ctx.GetRecordRef().Frozen = true;
151149 return ctx.ShareRecord();
152150 }
153151
154-//! \pre 间接断言:模块名称字符串的数据指针非空。
155-//@{
156152 /*!
157-\brief 加载模块为变量,若已存在则忽略。
158-\since build 871
153+\pre 间接断言:模块名称字符串的数据指针非空。
154+\sa Forms::GetModuleFor
159155 */
160-template<typename _fCallable>
156+//@{
157+//! \brief 加载模块为变量,若已存在则忽略。
158+template<typename _fCallable, typename... _tParams>
161159 inline void
162-LoadModule(ContextNode& ctx, string_view module_name, _fCallable&& f)
160+LoadModule(ContextNode& ctx, string_view module_name, _fCallable&& f,
161+ _tParams&&... args)
163162 {
164163 ctx.GetRecordRef().Define(module_name,
165- Forms::GetModuleFor(ctx, yforward(f)));
164+ Forms::GetModuleFor(ctx, yforward(f), yforward(args)...));
166165 }
167166
168167 /*!
169168 \brief 加载模块为变量,若已存在抛出异常。
170169 \exception BadIdentifier 变量绑定已存在。
171-\since build 867
172170 */
173-template<typename _fCallable>
171+template<typename _fCallable, typename... _tParams>
174172 inline void
175-LoadModuleChecked(ContextNode& ctx, string_view module_name, _fCallable&& f)
173+LoadModuleChecked(ContextNode& ctx, string_view module_name, _fCallable&& f,
174+ _tParams&&... args)
176175 {
177176 ctx.GetRecordRef().DefineChecked(module_name,
178- Forms::GetModuleFor(ctx, yforward(f)));
177+ Forms::GetModuleFor(ctx, yforward(f), yforward(args)...));
179178 }
180179 //@}
180+//@}
181181
182182
183183 /*!
@@ -192,6 +192,7 @@
192192 支持的语法形式包括预定义对象、基本操作和定义在基础环境中的派生操作。
193193 其中,派生操作包括基本派生操作和核心库函数。
194194 加载的基础环境被冻结。
195+当前派生实现:求值合并子调用前已加载字符串模块或等价方式初始化为模块 std.strings 。
195196 */
196197 YF_API void
197198 LoadGroundContext(REPLContext&);
@@ -230,37 +231,50 @@
230231 LoadModule_std_strings(REPLContext&);
231232
232233 /*!
234+\pre 当前派生实现:已加载和初始化依赖的模块,在当前环境可访问的指定的模块名称。
235+\exception NPLException 违反加载模块的前置条件而无法成功初始化。
236+*/
237+//@{
238+/*!
233239 \brief 加载输入/输出模块。
240+\pre 断言:第二参数非空。
241+\note 第二参数指定基础环境。
242+\since build 942
234243
235244 加载输入/输出库操作。
245+派生实现依赖模块:
246+字符串模块 std.strings 。
236247 */
237248 YF_API void
238-LoadModule_std_io(REPLContext&);
249+LoadModule_std_io(REPLContext&, const shared_ptr<Environment>&);
239250
240251 /*!
241252 \brief 加载系统模块。
242253 \sa LoadModule_std_strings
243254
244255 加载系统库操作。
245-当前实现:求值合并子调用前已加载字符串模块或等价方式初始化为模块 std.strings 。
256+派生实现依赖模块:
257+字符串模块 std.strings 。
246258 */
247259 YF_API void
248260 LoadModule_std_system(REPLContext&);
249261
250262 /*!
251263 \brief 加载模块管理模块。
252-\pre 当前实现:已加载初始化依赖的模块。
253-\since build 923
264+\pre 断言:第二参数非空。
265+\note 第二参数指定基础环境。
266+\since build 942
254267
255268 加载模块管理操作。
256-当前实现:已加载初始化以下模块:
269+派生实现依赖模块:
257270 字符串模块 std.strings ;
258271 输入/输出模块 std.io ;
259272 代理求值模块 std.promises ;
260273 系统模块 std.system 。
261274 */
262275 YF_API void
263-LoadModule_std_modules(REPLContext&);
276+LoadModule_std_modules(REPLContext&, const shared_ptr<Environment>&);
277+//@}
264278
265279 /*!
266280 \brief 加载 SHBuild 使用的内部模块。
diff -r eaddd0235b7a -r 30522accfd38 YFramework/include/NPL/NPLA.h
--- a/YFramework/include/NPL/NPLA.h Thu Mar 17 22:05:52 2022 +0800
+++ b/YFramework/include/NPL/NPLA.h Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA.h
1212 \ingroup NPL
1313 \brief NPLA 公共接口。
14-\version r9401
14+\version r9422
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 663
1717 \par 创建时间:
1818 2016-01-07 10:32:34 +0800
1919 \par 修改时间:
20- 2022-03-13 01:56 +0800
20+ 2022-04-05 15:16 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -737,8 +737,8 @@
737737 //! \since build 926
738738 PDefHOp(EnvironmentReference&, =, const EnvironmentReference& env_ref)
739739 ImplRet(ystdex::copy_and_swap(*this, env_ref))
740- //! \since build 926
741- PDefHOp(EnvironmentReference&, =, EnvironmentReference&& env_ref)
740+ //! \since build 942
741+ PDefHOp(EnvironmentReference&, =, EnvironmentReference&& env_ref) ynothrow
742742 ImplRet(swap(*this, env_ref), *this)
743743 #else
744744 //! \since build 894
@@ -2616,17 +2616,34 @@
26162616
26172617 /*!
26182618 \brief 访问指定类型的当前动作目标。
2619+ \todo 添加 const 重载。
2620+ */
2621+ //@{
2622+ /*!
2623+ \sa AccessCurrentAsUnchecked
26192624 \since build 892
2620- \todo 添加 const 重载。
26212625 */
26222626 template<typename _type>
26232627 YB_ATTR_nodiscard YB_PURE _type*
26242628 AccessCurrentAs()
26252629 {
2626- return IsAlive() ? current.front().template target<_type>() : nullptr;
2630+ return IsAlive() ? AccessCurrentAsUnchecked<_type>() : nullptr;
26272631 }
26282632
26292633 /*!
2634+ \pre 断言:\c IsAlive() 。
2635+ \since build 942
2636+ */
2637+ template<typename _type>
2638+ YB_ATTR_nodiscard YB_PURE _type*
2639+ AccessCurrentAsUnchecked()
2640+ {
2641+ YAssert(IsAlive(), "No tail action found.");
2642+ return current.front().template target<_type>();
2643+ }
2644+ //@}
2645+
2646+ /*!
26302647 \brief 转移并应用作为尾动作的当前动作,并设置 LastStatus 。
26312648 \note 非强异常安全:当动作调用抛出异常,不恢复已转移的动作。
26322649 \note 不无效化作序列中第一个动作以外的元素。
@@ -2877,6 +2894,7 @@
28772894
28782895 /*!
28792896 \brief 分配环境。
2897+\return 新创建环境的非空指针。
28802898 \relates Environment
28812899 \since build 847
28822900 */
diff -r eaddd0235b7a -r 30522accfd38 YFramework/include/NPL/NPLA1.h
--- a/YFramework/include/NPL/NPLA1.h Thu Mar 17 22:05:52 2022 +0800
+++ b/YFramework/include/NPL/NPLA1.h Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1.h
1212 \ingroup NPL
1313 \brief NPLA1 公共接口。
14-\version r9347
14+\version r9423
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 472
1717 \par 创建时间:
1818 2014-02-02 17:58:24 +0800
1919 \par 修改时间:
20- 2022-03-07 02:49 +0800
20+ 2022-03-29 18:07 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -31,18 +31,18 @@
3131 #include "YModules.h"
3232 #include YFM_NPL_NPLA // for NPLATag, TermNode, ContextNode,
3333 // ystdex::equality_comparable, std::declval, ystdex::exclude_self_t,
34-// trivial_swap_t, trivial_swap, ystdex::ref_eq, CombineReductionResult,
35-// pmr::memory_resource, NPL::make_observer, TNIter, LiftOtherValue, ValueNode,
36-// NPL::Deref, NPL::AsTermNode, std::make_move_iterator, IsBranch, std::next,
37-// ystdex::retry_on_cond, std::find_if, string_view,
38-// ystdex::exclude_self_params_t, YSLib::AreEqualHeld,
34+// trivial_swap_t, trivial_swap, ystdex::ref_eq, string_view,
35+// CombineReductionResult, pmr::memory_resource, NPL::make_observer,
36+// AssertMatchedAllocators, TNIter, LiftOtherValue, ValueNode, NPL::Deref,
37+// NPL::AsTermNode, std::make_move_iterator, std::next, ystdex::retry_on_cond,
38+// std::find_if, ystdex::exclude_self_params_t, YSLib::AreEqualHeld,
3939 // ystdex::make_parameter_list_t, ystdex::make_function_type_t, ystdex::true_,
4040 // ystdex::decay_t, ystdex::expanded_caller, std::is_constructible,
4141 // ystdex::or_, ArityMismatch, TermTags, RegularizeTerm, type_id, TokenValue,
4242 // Environment, ParseResultOf, ByteParser, SourcedByteParser, type_info,
43-// SourceInformation, std::integral_constant, SourceName, NPL::tuple, NPL::get,
44-// NPL::forward_as_tuple, ReaderState, YSLib::allocate_shared,
45-// ThrowTypeErrorForInvalidType, ystdex::is_bitwise_swappable;
43+// SourceInformation, std::bind, std::placeholders::_1, std::integral_constant,
44+// SourceName, NPL::tuple, NPL::get, NPL::forward_as_tuple, ReaderState,
45+// YSLib::allocate_shared, ystdex::is_bitwise_swappable;
4646 #include YFM_YSLib_Core_YEvent // for YSLib::GHEvent, YSLib::GCombinerInvoker,
4747 // YSLib::GDefaultLastValueInvoker;
4848 #include <ystdex/algorithm.hpp> // for ystdex::fast_any_of, ystdex::split;
@@ -1145,11 +1145,36 @@
11451145 //@}
11461146
11471147
1148-//! \since build 871
1148+/*!
1149+\brief 注册一般形式上下文处理器。
1150+\pre 间接断言:第二参数的数据指针非空。
1151+\since build 942
1152+*/
11491153 //@{
1154+template<class _tTarget>
1155+YB_ATTR_always_inline inline void
1156+RegisterFormHandler(_tTarget& target, string_view name, FormContextHandler fm)
1157+{
1158+ // XXX: %ContextHandler is specialized enough without %trivial_swap.
1159+ NPL::EmplaceLeaf<ContextHandler>(target, name,
1160+ std::allocator_arg, ToBindingsAllocator(target), std::move(fm));
1161+}
1162+//! \note 使用 ADL ToBindingsAllocator 。
1163+template<class _tTarget, typename... _tParams, yimpl(typename
1164+ = ystdex::exclude_self_params_t<FormContextHandler, _tParams...>)>
1165+YB_ATTR_always_inline inline void
1166+RegisterFormHandler(_tTarget& target, string_view name, _tParams&&... args)
1167+{
1168+ // XXX: %FormContextHandler is specialized enough without %trivial_swap.
1169+ A1::RegisterFormHandler(target, name,
1170+ FormContextHandler(yforward(args)...));
1171+}
1172+//@}
1173+
11501174 /*!
11511175 \brief 包装数种类枚举。
11521176 \note 用于指定创建上下文处理器的种类。
1177+\since build 871
11531178 */
11541179 enum WrappingKind : decltype(FormContextHandler::Wrapping)
11551180 {
@@ -1159,24 +1184,6 @@
11591184 Strict = 1
11601185 };
11611186
1162-
1163-/*!
1164-\brief 注册一般形式上下文处理器。
1165-\pre 间接断言:第二参数的数据指针非空。
1166-\note 使用 ADL ToBindingsAllocator 。
1167-*/
1168-template<size_t _vWrapping = Strict, class _tTarget, typename... _tParams>
1169-inline void
1170-RegisterHandler(_tTarget& target, string_view name, _tParams&&... args)
1171-{
1172- // XXX: Both %ContextHandler and %FormContexthandler are specialized enough
1173- // without %trivial_swap.
1174- NPL::EmplaceLeaf<ContextHandler>(target, name,
1175- std::allocator_arg, ToBindingsAllocator(target),
1176- FormContextHandler(yforward(args)..., _vWrapping));
1177-}
1178-//@}
1179-
11801187 /*!
11811188 \pre 间接断言:第二参数的数据指针非空。
11821189 \since build 838
@@ -1184,18 +1191,18 @@
11841191 //@{
11851192 //! \brief 注册一般形式上下文处理器。
11861193 template<class _tTarget, typename... _tParams>
1187-inline void
1194+YB_ATTR_always_inline inline void
11881195 RegisterForm(_tTarget& target, string_view name, _tParams&&... args)
11891196 {
1190- A1::RegisterHandler<Form>(target, name, yforward(args)...);
1197+ A1::RegisterFormHandler(target, name, yforward(args)..., Form);
11911198 }
11921199
11931200 //! \brief 注册严格上下文处理器。
11941201 template<class _tTarget, typename... _tParams>
1195-inline void
1202+YB_ATTR_always_inline inline void
11961203 RegisterStrict(_tTarget& target, string_view name, _tParams&&... args)
11971204 {
1198- A1::RegisterHandler<>(target, name, yforward(args)...);
1205+ A1::RegisterFormHandler(target, name, yforward(args)..., Strict);
11991206 }
12001207 //@}
12011208
@@ -1421,6 +1428,7 @@
14211428 //! \since build 876
14221429 //@{
14231430 /*!
1431+\ingroup guards
14241432 \brief 求值环境守卫。
14251433
14261434 作用域退出时调用环境切换器恢复保存的环境的作用域守卫。
@@ -1818,6 +1826,40 @@
18181826 //@}
18191827
18201828
1829+//! \since build 942
1830+//@{
1831+//! \brief 保持环境守卫。
1832+template<class _tGuard>
1833+inline ReductionStatus
1834+KeepGuard(_tGuard&, ContextNode& ctx) ynothrow
1835+{
1836+ return ctx.LastStatus;
1837+}
1838+
1839+//! \brief 环境守卫类型。
1840+template<class _tGuard>
1841+using GKeptGuardAction = decltype(std::bind(KeepGuard<_tGuard>,
1842+ std::declval<_tGuard&>(), std::placeholders::_1));
1843+
1844+//! \brief 创建保持环境守卫。
1845+//@{
1846+template<class _tGuard>
1847+YB_ATTR_nodiscard inline GKeptGuardAction<_tGuard>
1848+MakeKeptGuard(_tGuard& gd)
1849+{
1850+ return A1::NameTypedReducerHandler(std::bind(KeepGuard<_tGuard>,
1851+ std::move(gd), std::placeholders::_1), "eval-guard");
1852+}
1853+template<class _tGuard>
1854+YB_ATTR_nodiscard inline GKeptGuardAction<_tGuard>
1855+MakeKeptGuard(_tGuard&& gd)
1856+{
1857+ return A1::MakeKeptGuard(gd);
1858+}
1859+//@}
1860+//@}
1861+
1862+
18211863 /*
18221864 \brief REPL 上下文。
18231865 \warning 非虚析构。
diff -r eaddd0235b7a -r 30522accfd38 YFramework/include/NPL/NPLA1Forms.h
--- a/YFramework/include/NPL/NPLA1Forms.h Thu Mar 17 22:05:52 2022 +0800
+++ b/YFramework/include/NPL/NPLA1Forms.h Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Forms.h
1212 \ingroup NPL
1313 \brief NPLA1 语法形式。
14-\version r8641
14+\version r8664
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 882
1717 \par 创建时间:
1818 2020-02-15 11:19:21 +0800
1919 \par 修改时间:
20- 2022-02-14 07:39 +0800
20+ 2022-03-26 05:26 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -231,7 +231,10 @@
231231 \pre 设置为处理器调用的操作在进入调用前应确保设置尾上下文等内部状态。
232232 \pre 作为操作数的项的子项不包含引用或非正规表示引入的对操作数内的子项的循环引用。
233233 \pre 作为 NPLA1 规约函数的函数的参数符合规约函数实现约定。
234+\pre 若参数指定被规约项,参数是分支列表节点。
235+\pre 若参数指定被规约项的容器非空,参数非空(对应枝节点)。
234236 \pre 间接断言:作为规约函数第一参数指定的项是枝节点,以符合语法形式的实现要求。
237+\post 第一参数指定的被规约项在规约函数调用完成可表示一等对象。
235238 \sa ContextState
236239 \see %Documentation::NPL.
237240 \since build 732
@@ -239,14 +242,12 @@
239242 提供支持 NPLA1 对象语言文法的操作的接口。
240243 提供的操作用于实现操作子或应用子底层的操作子。
241244 除非另行指定,操作子的参数被通过直接转移项的形式转发。
242-对其中符合规约函数的 API ,在 NPLA1 规约函数约定的基础上,除非另行指定:
245+对其中符合规约函数的 API ,满足各项前置和后置条件,且除非另行指定:
243246 可能使用同步(不依赖上下文)或异步(依赖上下文)实现;
244247 没有依赖上下文参数的规约函数使用同步实现;
245248 异步实现依赖的上下文是当前上下文;
246249 在异步实现中都要求下一项项和参数指定的项相同;
247- 对规约函数的约定可隐含使用间接的断言检查;
248- 参数指定的被规约项是分支列表节点;
249- 参数指定的被规约项的容器非空(对应枝节点)。
250+ 对规约函数的上述约定(及各项前置和后置条件)可隐含使用间接的断言检查。
250251 */
251252 namespace Forms
252253 {
@@ -602,24 +603,24 @@
602603 inline void
603604 RegisterUnary(_tTarget& target, string_view name, _func f)
604605 {
605- A1::RegisterHandler<_vWrapping>(target, name,
606- UnaryExpansion<_func>(std::move(f)));
606+ A1::RegisterFormHandler(target, name,
607+ UnaryExpansion<_func>(std::move(f)), _vWrapping);
607608 }
608609 //! \since build 927
609610 template<size_t _vWrapping = Strict, typename _func, class _tTarget>
610611 inline void
611612 RegisterUnary(_tTarget& target, string_view name, trivial_swap_t, _func f)
612613 {
613- A1::RegisterHandler<_vWrapping>(target, name,
614- trivial_swap, UnaryExpansion<_func>(std::move(f)));
614+ A1::RegisterFormHandler(target, name, trivial_swap,
615+ UnaryExpansion<_func>(std::move(f)), _vWrapping);
615616 }
616617 template<size_t _vWrapping = Strict, typename _type, typename _func,
617618 class _tTarget>
618619 inline void
619620 RegisterUnary(_tTarget& target, string_view name, _func f)
620621 {
621- A1::RegisterHandler<_vWrapping>(target, name,
622- UnaryAsExpansion<_type, _func>(std::move(f)));
622+ A1::RegisterFormHandler(target, name,
623+ UnaryAsExpansion<_type, _func>(std::move(f)), _vWrapping);
623624 }
624625 //! \since build 927
625626 template<size_t _vWrapping = Strict, typename _type, typename _func,
@@ -627,8 +628,8 @@
627628 inline void
628629 RegisterUnary(_tTarget& target, string_view name, trivial_swap_t, _func f)
629630 {
630- A1::RegisterHandler<_vWrapping>(target, name,
631- trivial_swap, UnaryAsExpansion<_type, _func>(std::move(f)));
631+ A1::RegisterFormHandler(target, name, trivial_swap,
632+ UnaryAsExpansion<_type, _func>(std::move(f)), _vWrapping);
632633 }
633634 //@}
634635
@@ -638,24 +639,24 @@
638639 inline void
639640 RegisterBinary(_tTarget& target, string_view name, _func f)
640641 {
641- A1::RegisterHandler<_vWrapping>(target, name,
642- BinaryExpansion<_func>(std::move(f)));
642+ A1::RegisterFormHandler(target, name,
643+ BinaryExpansion<_func>(std::move(f)), _vWrapping);
643644 }
644645 //! \since build 927
645646 template<size_t _vWrapping = Strict, typename _func, class _tTarget>
646647 inline void
647648 RegisterBinary(_tTarget& target, string_view name, trivial_swap_t, _func f)
648649 {
649- A1::RegisterHandler<_vWrapping>(target, name,
650- trivial_swap, BinaryExpansion<_func>(std::move(f)));
650+ A1::RegisterFormHandler(target, name, trivial_swap,
651+ BinaryExpansion<_func>(std::move(f)), _vWrapping);
651652 }
652653 template<size_t _vWrapping = Strict, typename _type, typename _type2,
653654 typename _func, class _tTarget>
654655 inline void
655656 RegisterBinary(_tTarget& target, string_view name, _func f)
656657 {
657- A1::RegisterHandler<_vWrapping>(target, name,
658- BinaryAsExpansion<_type, _type2, _func>(std::move(f)));
658+ A1::RegisterFormHandler(target, name,
659+ BinaryAsExpansion<_type, _type2, _func>(std::move(f)), _vWrapping);
659660 }
660661 //! \since build 927
661662 template<size_t _vWrapping = Strict, typename _type, typename _type2,
@@ -663,8 +664,8 @@
663664 inline void
664665 RegisterBinary(_tTarget& target, string_view name, trivial_swap_t, _func f)
665666 {
666- A1::RegisterHandler<_vWrapping>(target, name, trivial_swap,
667- BinaryAsExpansion<_type, _type2, _func>(std::move(f)));
667+ A1::RegisterFormHandler(target, name, trivial_swap,
668+ BinaryAsExpansion<_type, _type2, _func>(std::move(f)), _vWrapping);
668669 }
669670 //@}
670671 //@}
diff -r eaddd0235b7a -r 30522accfd38 YFramework/include/NPL/SContext.h
--- a/YFramework/include/NPL/SContext.h Thu Mar 17 22:05:52 2022 +0800
+++ b/YFramework/include/NPL/SContext.h Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file SContext.h
1212 \ingroup NPL
1313 \brief S 表达式上下文。
14-\version r4178
14+\version r4186
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 304
1717 \par 创建时间:
1818 2012-08-03 19:55:41 +0800
1919 \par 修改时间:
20- 2022-03-14 18:23 +0800
20+ 2022-03-18 03:12 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -40,9 +40,8 @@
4040 // YSLib::ListContainerTag, std::initializer_list,
4141 // ystdex::enable_if_same_param_t, ystdex::exclude_self_t,
4242 // ystdex::enable_if_inconvertible_t, ystdex::forward_like, ystdex::invoke,
43-// YSLib::AccessPtr, ystdex::not_, ystdex::cond_or_t, ystdex::bool_,
44-// ystdex::false_, std::is_convertible, ystdex::decay_t, ystdex::addrof,
45-// ystdex::compose, pair, std::is_lvalue_reference, YSLib::Alert, YSLib::stack;
43+// YSLib::AccessPtr, ystdex::head_of_t, ystdex::addrof, ystdex::compose, pair,
44+// std::is_lvalue_reference, YSLib::Alert, YSLib::stack;
4645 #include YFM_YSLib_Core_YException // for YSLib::LoggedEvent;
4746 #include <ystdex/deref_op.hpp> // for ystdex::call_value_or;
4847 #include <ystdex/range.hpp> // for std::iterator_traits,
@@ -418,7 +417,7 @@
418417 yimpl(ystdex::enable_if_t<sizeof...(_tParams) != 0
419418 || !ystdex::is_same_param<ValueObject, _tParam>::value, int> = 0,
420419 ystdex::exclude_self_t<std::allocator_arg_t, _tParam, int> = 0)>
421- inline yimpl(ystdex::enable_if_inconvertible_t)<ystdex::decay_t<_tParam>,
420+ inline yimpl(ystdex::enable_if_inconvertible_t)<_tParam,
422421 TermNode::allocator_type, void>
423422 SetValue(_tParam&& arg, _tParams&&... args) ynoexcept_spec(Value.assign(
424423 std::allocator_arg, std::declval<TermNode::allocator_type&>(),
@@ -804,12 +803,11 @@
804803
805804 //! \brief 创建项节点。
806805 //@{
807-//! \since build 853
806+//! \since build 942
808807 template<typename... _tParam, typename... _tParams>
809808 YB_ATTR_nodiscard YB_PURE inline
810-ystdex::enable_if_t<ystdex::not_<ystdex::cond_or_t<ystdex::bool_<
811- (sizeof...(_tParams) >= 1)>, ystdex::false_, std::is_convertible,
812- ystdex::decay_t<_tParams>..., TermNode::allocator_type>>::value, TermNode>
809+yimpl(ystdex::enable_if_inconvertible_t)<ystdex::head_of_t<_tParams...>,
810+ TermNode::allocator_type, TermNode>
813811 AsTermNode(_tParams&&... args)
814812 {
815813 return TermNode(NoContainer, yforward(args)...);
diff -r eaddd0235b7a -r 30522accfd38 YFramework/source/NPL/Dependency.cpp
--- a/YFramework/source/NPL/Dependency.cpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YFramework/source/NPL/Dependency.cpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file Dependency.cpp
1212 \ingroup NPL
1313 \brief 依赖管理。
14-\version r6634
14+\version r6880
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 623
1717 \par 创建时间:
1818 2015-08-09 22:14:45 +0800
1919 \par 修改时间:
20- 2022-03-07 02:50 +0800
20+ 2022-03-31 02:43 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -32,8 +32,9 @@
3232 // RetainN, NPL::ResolveRegular, NPL::Deref, RelaySwitched, trivial_swap,
3333 // A1::NameTypedReducerHandler, std::bind, std::ref, Forms::CallResolvedUnary,
3434 // EnsureValueTags, ResolvedTermReferencePtr, NPL::TryAccessLeaf,
35-// TermReference, LiftTerm, MoveResolved, NPL::AllocateEnvironment, function,
36-// ValueObject, EnvironmentReference, std::piecewise_construct,
35+// TermReference, LiftTerm, MoveResolved, Environment,
36+// NPL::AllocateEnvironment, function, ValueObject, NPL::AccessPtr,
37+// EnvironmentReference, shared_ptr, std::piecewise_construct,
3738 // NPL::forward_as_tuple, LiftOther, ThrowNonmodifiableErrorForAssignee,
3839 // ThrowValueCategoryError, ValueToken, ResolveTerm, TokenValue,
3940 // CheckVariadicArity, A1::AsForm, ystdex::bind1, std::placeholders,
@@ -48,9 +49,11 @@
4849 // ReferenceTerm, ResolveName, ystdex::fast_any_of, Ensigil, YSLib::ufexists,
4950 // YSLib::to_std_string, EmplaceCallResultOrReturn, NPL::TryAccessTerm,
5051 // ystdex::plus, ystdex::tolower, YSLib::OwnershipTag, YSLib::IO::StreamPut,
51-// YSLib::FetchEnvironmentVariable, YSLib::SetEnvironmentVariable,
52+// YSLib::FetchEnvironmentVariable, YSLib::SetEnvironmentVariable, NPL::tuple,
5253 // YSLib::IO::UniqueFile, YSLib::uremove, YSLib::allocate_shared,
5354 // ReduceReturnUnspecified, ystdex::throw_error;
55+#include <ystdex/container.hpp> // for ystdex::exists, ystdex::search_map,
56+// ystdex::emplace_hint_in_place;
5457 #include YFM_NPL_NPLA1Forms // for EncapsulateValue, Encapsulate, Encapsulated,
5558 // Decapsulate, NPL::Forms functions, StringToSymbol, SymbolToString;
5659 #include YFM_NPL_NPLAMath // for NumerLeaf, NumberNode, NPL math functions;
@@ -60,11 +63,10 @@
6063 // ystdex::make_transform;
6164 #include YFM_YSLib_Service_TextFile // for
6265 // YSLib::IO::SharedInputMappedFileStream, YSLib::Text;
63-#include <cerrno> // for errno, ERANGE;
6466 #include <regex> // for std::regex, std::regex_replace, std::regex_match;
6567 #include <ostream> // for std::endl;
6668 #include "NPLA1Internals.h" // for NPL_Impl_NPLA1_Enable_Thunked,
67-// ReduceSubsequent;
69+// ReduceSubsequent, A1::RelayCurrentNext, MakeKeptGuard;
6870 #include YFM_YSLib_Core_YCoreUtilities // for YSLib::LockCommandArguments,
6971 // YSLib::FetchCommandOutput, YSLib::RandomizeTemplatedString;
7072 #include <ystdex/string.hpp> // for ystdex::begins_with;
@@ -231,7 +233,7 @@
231233 ReduceToLoadFile(TermNode& term, ContextNode& ctx, REPLContext& context,
232234 string filename)
233235 {
234- term = context.Load(context, ctx, filename);
236+ term = context.Load(context, ctx, std::move(filename));
235237 // NOTE: This is explicitly not same to klisp. This is also friendly to PTC.
236238 // XXX: Same to %A1::ReduceOnce, without setup the next term.
237239 return ContextState::Access(ctx).ReduceOnce.Handler(term, ctx);
@@ -243,8 +245,9 @@
243245 ReduceToLoadExternal(TermNode& term, ContextNode& ctx, REPLContext& context)
244246 {
245247 RetainN(term);
246- return ReduceToLoadFile(term, ctx, context,
247- NPL::ResolveRegular<const string>(NPL::Deref(std::next(term.begin()))));
248+ return ReduceToLoadFile(term, ctx, context, string(
249+ NPL::ResolveRegular<const string>(NPL::Deref(std::next(term.begin()))),
250+ term.get_allocator()));
248251 }
249252
250253 ReductionStatus
@@ -468,7 +471,7 @@
468471 NPL::AddToken(t2, "body");
469472 NPL::AddToken(term, "d");
470473 Reduce(term, ctx);
471-#if false
474+# if false
472475 // NOTE: Usage:
473476 AddDefineFunction(rctx, "$defv!", {"&$f", "&formals", "&ef"},
474477 {"$f", "$vau", "formals", "ef"});
@@ -494,10 +497,27 @@
494497 }, {"f", "$lambda/e", "p", "formals"});
495498 AddDefineFunction(rctx, "$defl/e%!", {"&f", "&p", "&formals",
496499 }, {"f", "$lambda/e%", "p", "formals"});
497-#endif
500+# endif
498501 }
499502 #endif
500503 //@}
504+
505+/*!
506+\brief 创建环境弱引用作为单一父环境的新环境。
507+\return 新创建环境的非空指针。
508+\sa NPL::AllocateEnvironment
509+\since build 942
510+*/
511+YB_ATTR_nodiscard YB_FLATTEN shared_ptr<Environment>
512+CreateEnvironmentWithParent(const Environment::allocator_type& a,
513+ const EnvironmentReference& r_env)
514+{
515+ // XXX: Simlar to %MakeEnvironment.
516+ ValueObject parent;
517+
518+ parent.emplace<EnvironmentReference>(r_env);
519+ return NPL::AllocateEnvironment(a, std::move(parent));
520+}
501521 #endif
502522
503523 //! \since build 834
@@ -619,8 +639,8 @@
619639 // basic than vau.
620640 RegisterStrict(ctx, "copy-environment", CopyEnvironment);
621641 RegisterUnary<Strict, const EnvironmentReference>(ctx, "lock-environment",
622- [](const EnvironmentReference& wenv) ynothrow{
623- return wenv.Lock();
642+ [](const EnvironmentReference& r_env) ynothrow{
643+ return r_env.Lock();
624644 });
625645 RegisterUnary(ctx, "freeze-environment!", [](TermNode& x){
626646 Environment::EnsureValid(ResolveEnvironment(x).first).Frozen = true;
@@ -893,16 +913,9 @@
893913 // shall not be moved after being called.
894914 // TODO: Blocked. Use C++14 lambda initializers to simplify the
895915 // implementation.
896- ystdex::bind1([] YB_LAMBDA_ANNOTATE(
897- (TermNode& term, const EnvironmentReference& ce), , flatten){
916+ ystdex::bind1([](TermNode& term, const EnvironmentReference& ce){
898917 RetainN(term, 0);
899-
900- // XXX: Simlar to %MakeEnvironment.
901- ValueObject parent;
902-
903- parent.emplace<EnvironmentReference>(ce);
904- term.Value
905- = NPL::AllocateEnvironment(term.get_allocator(), std::move(parent));
918+ term.Value = CreateEnvironmentWithParent(term.get_allocator(), ce);
906919 }, context.Root.WeakenRecord()));
907920 RegisterStrict(renv, "derive-current-environment",
908921 [] YB_LAMBDA_ANNOTATE((TermNode& term, ContextNode& ctx), , flatten){
@@ -1146,14 +1159,14 @@
11461159 $cond
11471160 ((null? x) #t)
11481161 ((null? (rest& x)) eval% (first (forward! x)) d)
1149- ((eval% (first& x) d) apply (wrap $and) (rest% (forward! x)) d)
1162+ ((eval% (first& x) d) eval% (cons% $and (rest% (forward! x))) d)
11501163 (#t #f);
11511164 $defv%! $or &x d
11521165 $cond
11531166 ((null? x) #f)
11541167 ((null? (rest& x)) eval% (first (forward! x)) d)
1155- (#t ($lambda% (&r) $if r (forward! r) (apply (wrap $or)
1156- (rest% (forward! x)) d)) (eval% (move! (first& x)) d));
1168+ (#t ($lambda% (&r) $if r (forward! r) (eval% (cons% $or (rest% (forward! x)))
1169+ d)) (eval% (move! (first& x)) d));
11571170 $defw%! accl (&l &pred? &base &head &tail &sum) d
11581171 $if (apply pred? (list% l) d) (forward! base)
11591172 (apply accl (list% (apply tail (list% l) d) pred?
@@ -1400,9 +1413,9 @@
14001413 throw NPLException("Empty requirement name found.");
14011414 }
14021415
1403-//! \since build 923
1416+//! \since build 941
14041417 YB_ATTR_nodiscard string
1405-FindValidRequirementIn(const vector<string>& specs, const string req)
1418+FindValidRequirementIn(const vector<string>& specs, const string& req)
14061419 {
14071420 YAssert(!req.empty(), "Invalid requirement found.");
14081421 const std::regex re("\\?");
@@ -1601,8 +1614,8 @@
16011614 ystdex::bind1([&, lift](TermNode& t, ContextNode& c,
16021615 shared_ptr<Environment>& p_env){
16031616 return NonTailCall::RelayNextGuardedProbe(c, t,
1604- EnvironmentGuard(c, c.SwitchEnvironment(std::move(p_env))),
1605- lift, std::ref(ContextState::Access(c).ReduceOnce));
1617+ EnvironmentGuard(c, c.SwitchEnvironment(std::move(p_env))), lift,
1618+ std::ref(ContextState::Access(c).ReduceOnce));
16061619 }, std::placeholders::_2, std::move(p_env_saved)),
16071620 trivial_swap, A1::NameTypedReducerHandler([&, p_ref]{
16081621 // NOTE: Different to [RnRK], the promise object may be assigned and
@@ -1635,6 +1648,26 @@
16351648 }, "promise-handle-result"));
16361649 }
16371650 //@}
1651+
1652+
1653+//! \since build 942
1654+template<typename _func>
1655+ReductionStatus
1656+ReduceToLoadGuarded(TermNode& term, ContextNode& ctx, REPLContext& context,
1657+ shared_ptr<Environment> p_env, _func reduce)
1658+{
1659+# if NPL_Impl_NPLA1_Enable_Thunked
1660+ return A1::RelayCurrentNext(ctx, term, trivial_swap, std::bind(
1661+ std::move(reduce), std::ref(term), std::ref(ctx), std::ref(context)),
1662+ trivial_swap, MakeKeptGuard(EnvironmentGuard(ctx,
1663+ ctx.SwitchEnvironmentUnchecked(std::move(p_env)))));
1664+# else
1665+ const EnvironmentGuard gd(ctx,
1666+ ctx.SwitchEnvironmentUnchecked(p_env));
1667+
1668+ return reduce(term, ctx, context);
1669+# endif
1670+}
16381671 #endif
16391672
16401673
@@ -1911,8 +1944,11 @@
19111944 }
19121945
19131946 void
1914-LoadModule_std_io(REPLContext& context)
1947+LoadModule_std_io(REPLContext& context,
1948+ const shared_ptr<Environment>& p_ground)
19151949 {
1950+ YAssertNonnull(p_ground);
1951+
19161952 auto& renv(context.Root.GetRecordRef());
19171953
19181954 RegisterUnary<Strict, const string>(renv, "readable-file?",
@@ -1966,6 +2002,66 @@
19662002 return ValueToken::Unspecified;
19672003 });
19682004 #endif
2005+#if NPL_Impl_NPLA1_Native_Forms
2006+ RegisterStrict(renv, "get-module", trivial_swap,
2007+ ystdex::bind1([&](TermNode& term, ContextNode& ctx,
2008+ const EnvironmentReference& r_ground){
2009+ CheckVariadicArity(term, 0);
2010+
2011+ const auto size(term.size());
2012+
2013+ if(size <= 3)
2014+ {
2015+ // XXX: As 'make-standard-environment'.
2016+ auto p_env(
2017+ CreateEnvironmentWithParent(term.get_allocator(), r_ground));
2018+
2019+ switch(size)
2020+ {
2021+ case 3:
2022+ {
2023+ auto& con(term.GetContainerRef());
2024+
2025+ p_env->Bind("module-parameters", NPL::AsTermNode(
2026+ ResolveEnvironment(std::move(*con.rbegin())).first));
2027+ // XXX: This is needed for the call to %ReduceToLoadExternal
2028+ // later.
2029+ term.GetContainerRef().pop_back();
2030+ }
2031+ YB_ATTR_fallthrough;
2032+ case 2:
2033+# if NPL_Impl_NPLA1_Enable_Thunked
2034+ RelaySwitched(ctx, trivial_swap, A1::NameTypedReducerHandler(
2035+ std::bind([&](shared_ptr<Environment> p_res){
2036+ term.Value = std::move(p_res);
2037+ return ReductionStatus::Clean;
2038+ }, p_env), "get-module-return"));
2039+ return ReduceToLoadGuarded(term, ctx, context, std::move(p_env),
2040+ ReduceToLoadExternal);
2041+# else
2042+ ReduceToLoadGuarded(term, ctx, context, std::move(p_env),
2043+ ReduceToLoadExternal);
2044+ term.Value = std::move(p_env);
2045+ return ReductionStatus::Clean;
2046+# endif
2047+ }
2048+ YAssert(false, "Invalid state found.");
2049+ }
2050+ ThrowInvalidSyntaxError("Syntax error in get-module.");
2051+ }, std::placeholders::_2, EnvironmentReference(p_ground)));
2052+#else
2053+ yunused(p_ground);
2054+ context.ShareCurrentSource("<lib:std.io-1>");
2055+ context.Perform(R"NPL(
2056+$defl! get-module (&filename .&opt)
2057+ $let ((env $if (null? opt) (() make-standard-environment)
2058+ ($let (((&e .&eopt) opt)) $if (null? eopt)
2059+ ($let ((env () make-standard-environment)) $sequence
2060+ ($set! env module-parameters (check-environemnt e)) env)
2061+ (raise-invalid-syntax-error "Syntax error in get-module."))))
2062+ $sequence (eval% (list load filename) env) env;
2063+ )NPL");
2064+#endif
19692065 }
19702066
19712067 void
@@ -2005,8 +2101,8 @@
20052101 });
20062102 context.ShareCurrentSource("<lib:std.system>");
20072103 context.Perform(R"NPL(
2008-$defl/e! env-empty? (derive-current-environment std.strings) (&n) string-empty?
2009- (env-get n);
2104+$defl/e! env-empty? (derive-current-environment std.strings) (&n)
2105+ string-empty? (env-get n);
20102106 )NPL");
20112107 RegisterStrict(renv, "system", CallSystem);
20122108 RegisterStrict(renv, "system-get", [](TermNode& term){
@@ -2034,13 +2130,18 @@
20342130 }
20352131
20362132 void
2037-LoadModule_std_modules(REPLContext& context)
2133+LoadModule_std_modules(REPLContext& context,
2134+ const shared_ptr<Environment>& p_ground)
20382135 {
2136+ YAssertNonnull(p_ground);
20392137 #if NPL_Impl_NPLA1_Native_Forms
2138+
2139+ using namespace std::placeholders;
20402140 using YSLib::to_std_string;
20412141 auto& renv(context.Root.GetRecordRef());
20422142 const auto a(renv.Bindings.get_allocator());
2043- const auto p_registry(YSLib::allocate_shared<set<string>>(a));
2143+ const auto p_registry(YSLib::allocate_shared<YSLib::map<string,
2144+ pair<TermNode, shared_ptr<Environment>>>>(a));
20442145 auto& registry(*p_registry);
20452146 const auto p_specs([&]{
20462147 auto p_vec(YSLib::allocate_shared<vector<string>>(a));
@@ -2077,16 +2178,28 @@
20772178 throw NPLException("Empty requirement name found.");
20782179 });
20792180 RegisterStrict(renv, "register-requirement!", trivial_swap,
2080- [&](TermNode& term){
2181+ ystdex::bind1([&](TermNode& term, const EnvironmentReference& r_ground){
20812182 auto i(term.begin());
20822183 const auto& req(NPL::ResolveRegular<const string>(NPL::Deref(++i)));
20832184
20842185 CheckRequirement(req);
2085- if(registry.insert(req).second)
2086- return ReduceReturnUnspecified(term);
2186+
2187+ const auto pr(ystdex::search_map(registry, req));
2188+
2189+ if(pr.second)
2190+ {
2191+ term.Value.emplace<EnvironmentReference>(
2192+ ystdex::emplace_hint_in_place(registry, pr.first, req,
2193+ std::piecewise_construct, NPL::tuple<>(),
2194+ NPL::forward_as_tuple([&]{
2195+ return CreateEnvironmentWithParent(term.get_allocator(),
2196+ r_ground);
2197+ }()))->second.second);
2198+ return ReductionStatus::Clean;
2199+ }
20872200 throw NPLException("Requirement '" + to_std_string(req)
20882201 + "' is already registered.");
2089- });
2202+ }, EnvironmentReference(p_ground)));
20902203 RegisterUnary<Strict, const string>(renv, "unregister-requirement!",
20912204 trivial_swap, [&](const string& req){
20922205 CheckRequirement(req);
@@ -2103,26 +2216,83 @@
21032216 return FindValidRequirementIn(NPL::Deref(p_specs), req);
21042217 });
21052218 RegisterStrict(renv, "require", trivial_swap,
2106- [&](TermNode& term, ContextNode& ctx) -> ReductionStatus{
2219+ ystdex::bind1([&](TermNode& term, ContextNode& ctx,
2220+ const EnvironmentReference& r_ground) -> ReductionStatus{
2221+ CheckVariadicArity(term, 0);
2222+
21072223 auto i(term.begin());
21082224 const auto& req(NPL::ResolveRegular<const string>(NPL::Deref(++i)));
21092225
21102226 CheckRequirement(req);
2111- if(!ystdex::exists(registry, req))
2227+
2228+ auto pr(ystdex::search_map(registry, req));
2229+ const auto reduce_to_res([&](TermNode& tm){
2230+ // XXX: As %ReduceToReference for modifiable lvalues (with default
2231+ // tags).
2232+ AssertValueTags(tm);
2233+ if(const auto p = NPL::TryAccessLeaf<const TermReference>(tm))
2234+ {
2235+ // XXX: Since %tm is not shared with %term, it is safe to use
2236+ // %TermNode::SetContent instead of %TermNode::CopyContent.
2237+ term.SetContent(tm);
2238+ return ReductionStatus::Retained;
2239+ }
2240+ term.Value
2241+ = TermReference(TermTags::Unqualified, tm, ctx.WeakenRecord());
2242+ return ReductionStatus::Clean;
2243+ });
2244+
2245+ if(pr.second)
21122246 {
21132247 auto filename(FindValidRequirementIn(specs, req));
21142248
2115- registry.insert(req);
2116- return ReduceToLoadFile(term, ctx, context, std::move(filename));
2249+ pr.first = ystdex::emplace_hint_in_place(registry, pr.first, req,
2250+ std::piecewise_construct, NPL::tuple<>(),
2251+ NPL::forward_as_tuple([&]{
2252+ return CreateEnvironmentWithParent(term.get_allocator(),
2253+ r_ground);
2254+ }()));
2255+
2256+ auto& val(pr.first->second);
2257+ auto& con(term.GetContainerRef());
2258+
2259+ // XXX: As 'get-module'.
2260+ switch(con.size())
2261+ {
2262+ case 3:
2263+ val.second->Bind("module-parameters", NPL::AsTermNode(
2264+ ResolveEnvironment(std::move(*con.rbegin())).first));
2265+ con.pop_back();
2266+ YB_ATTR_fallthrough;
2267+ case 2:
2268+ // TODO: Blocked. Use C++14 lambda initializers to optimize
2269+ // the implementation.
2270+ return A1::RelayCurrentNext(ctx, term, trivial_swap,
2271+ ystdex::bind1([&](TermNode& t, ContextNode& c,
2272+ string& fname, shared_ptr<Environment>& p_env){
2273+ return ReduceToLoadGuarded(t, c, context, std::move(p_env),
2274+ ystdex::bind1([&](TermNode& t_0, ContextNode& c_0,
2275+ REPLContext& rc, string& fname_0){
2276+ return
2277+ ReduceToLoadFile(t_0, c_0, rc, std::move(fname_0));
2278+ }, _2, _3, std::move(fname)));
2279+ }, _2, std::move(filename), val.second),
2280+ A1::NameTypedReducerHandler([&, reduce_to_res]{
2281+ MoveCollapsed(val.first, term);
2282+ return reduce_to_res(val.first);
2283+ }, "require-return"));
2284+ }
2285+ ThrowInvalidSyntaxError("Syntax error in require.");
21172286 }
2118- return ReduceReturnUnspecified(term);
2119- });
2287+ return reduce_to_res(pr.first->second.first);
2288+ }, _2, EnvironmentReference(p_ground)));
21202289 #else
2290+ yunused(p_ground);
21212291 context.ShareCurrentSource("<lib:std.modules>");
21222292 // XXX: Thread-safety is not respected currently.
21232293 context.Perform(R"NPL(
21242294 $provide/let! (registered-requirement? register-requirement!
2125- unregister-requirement! find-requirement-filename)
2295+ unregister-requirement! find-requirement-filename require)
21262296 ((mods $as-environment (
21272297 $import! std.strings &string-empty? &++ &string->symbol;
21282298
@@ -2132,9 +2302,10 @@
21322302 $def! registry () make-environment;
21332303 $defl! bound-name? (&req)
21342304 $and (eval (list bound? req) registry)
2135- (not? (string-empty? (eval (string->symbol req) registry))),
2305+ (not? (null? (eval (string->symbol req) registry))),
2306+ $defl%! get-cell% (&req) first& (eval% (string->symbol req) registry),
21362307 $defl! set-value! (&req &v)
2137- eval (list $def! (string->symbol req) v) registry
2308+ eval (list $def! (string->symbol req) $quote (forward! v)) registry
21382309 ),
21392310 $def! prom_pathspecs ($remote-eval% $lazy std.promises)
21402311 $let ((spec ($remote-eval% env-get std.system) "NPLA1_PATH"))
@@ -2159,21 +2330,32 @@
21592330 $if (string-empty? req) (() requirement-error) (bound-name? req),
21602331 $defl/e! &register-requirement! mods (&req)
21612332 $if (string-empty? req) (() requirement-error)
2162- ($if (bound-name? req) (raise-error (++ "Requirement '" req
2163- "' is already registered.")) (set-value! req req)),
2333+ ($if (bound-name? req) (raise-error
2334+ (++ "Requirement '" req "' is already registered."))
2335+ ($let ((env () make-standard-environment))
2336+ $sequence (set-value! req (list () env))
2337+ (weaken-environment (move! env)))),
21642338 $defl/e! &unregister-requirement! mods (&req)
21652339 $if (string-empty? req) (() requirement-error)
2166- ($if (bound-name? req) (set-value! req "") (raise-error
2340+ ($if (bound-name? req) (set-value! req ()) (raise-error
21672341 (++ "Requirement '" req "' is not registered."))),
21682342 $defl/e! &find-requirement-filename mods (&req)
21692343 get-requirement-filename
2170- (($remote-eval% force std.promises) prom_pathspecs) req
2344+ (($remote-eval% force std.promises) prom_pathspecs) req;
2345+ $defl/e%! require mods (&req .&opt)
2346+ $if (registered-requirement? req) (get-cell% (move! req))
2347+ ($let*% ((filename find-requirement-filename req)
2348+ (env register-requirement! req) (&res get-cell% (move! req)))
2349+ $sequence
2350+ ($unless (null? opt)
2351+ ($set! env module-parameters $let (((&e .&eopt) opt))
2352+ $if (null? eopt) (check-environemnt e)
2353+ (raise-invalid-syntax-error
2354+ "Syntax error in require.")))
2355+ (assign%! res (eval% (list ($remote-eval% load std.io)
2356+ (move! filename)) (move! env)))
2357+ res);
21712358 );
2172-$defl%! require (&req)
2173- $if (registered-requirement? req) #inert
2174- ($let ((filename find-requirement-filename req))
2175- $sequence (register-requirement! (move! req))
2176- (($remote-eval% load std.io) filename));
21772359 )NPL");
21782360 #endif
21792361 }
@@ -2384,10 +2566,13 @@
23842566 continue;
23852567 #endif
23862568 }
2569+
23872570 // NOTE: This is nearly fatal. It is not intended to be handled in
23882571 // the object language in general. Thus, currently it is not
23892572 // wrapped in %NPLException.
2390- ystdex::throw_error(errno, "Failed opening temporary file with the"
2573+ int err(errno);
2574+
2575+ ystdex::throw_error(err, "Failed opening temporary file with the"
23912576 " prefix '" + to_std_string(pfx) + '\'');
23922577 });
23932578 }
@@ -2397,19 +2582,26 @@
23972582 {
23982583 LoadGroundContext(context);
23992584
2585+ const auto pfx("std.");
2586+ string mod(pfx, context.Allocator);
24002587 auto& rctx(context.Root);
2401- const auto load_std_module([&](string_view module_name,
2402- void(&load_module)(REPLContext&)){
2403- LoadModuleChecked(rctx, "std." + string(module_name),
2404- std::bind(load_module, std::ref(context)));
2588+ const auto load_std_module(
2589+ [&](string_view module_name, void(&load_module)(REPLContext&)){
2590+ // XXX: Using %context.Allocator or %std::string are both a bit less
2591+ // efficient.
2592+ mod += module_name;
2593+ LoadModuleChecked(rctx, mod, load_module, context);
2594+ mod.resize(ystdex::string_length(pfx));
24052595 });
2596+ const auto p_ground(rctx.ShareRecord());
24062597
24072598 load_std_module("promises", LoadModule_std_promises);
24082599 load_std_module("math", LoadModule_std_math),
2409- load_std_module("strings", LoadModule_std_strings),
2410- load_std_module("io", LoadModule_std_io),
2600+ load_std_module("strings", LoadModule_std_strings);
2601+ LoadModuleChecked(rctx, "std.io", LoadModule_std_io, context, p_ground);
24112602 load_std_module("system", LoadModule_std_system);
2412- load_std_module("modules", LoadModule_std_modules);
2603+ LoadModuleChecked(rctx, "std.modules", LoadModule_std_modules, context,
2604+ p_ground);
24132605 }
24142606
24152607 } // namespace Forms;
diff -r eaddd0235b7a -r 30522accfd38 YFramework/source/NPL/NPLA1.cpp
--- a/YFramework/source/NPL/NPLA1.cpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YFramework/source/NPL/NPLA1.cpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1.cpp
1212 \ingroup NPL
1313 \brief NPLA1 公共接口。
14-\version r22291
14+\version r22297
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 472
1717 \par 创建时间:
1818 2014-02-02 18:02:47 +0800
1919 \par 修改时间:
20- 2022-03-12 21:58 +0800
20+ 2022-04-03 02:07 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -661,13 +661,10 @@
661661 }
662662
663663 template<typename _func>
664- static void
664+ static inline void
665665 HandleLeaf(_func f, const TermNode& t, bool t_has_ref)
666666 {
667- if(const auto p = TermToNamePtr(t))
668- f(*p);
669- else if(!IsIgnore(t))
670- ThrowFormalParameterTypeError(t, t_has_ref);
667+ HandleOrIgnore(std::ref(f), t, t_has_ref);
671668 }
672669
673670 template<typename _func>
@@ -697,7 +694,7 @@
697694 }
698695
699696 template<typename _func>
700- static void
697+ static inline void
701698 HandleLeaf(_func f, const TermNode& t)
702699 {
703700 if(!IsIgnore(t))
diff -r eaddd0235b7a -r 30522accfd38 YFramework/source/NPL/NPLA1Forms.cpp
--- a/YFramework/source/NPL/NPLA1Forms.cpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YFramework/source/NPL/NPLA1Forms.cpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Forms.cpp
1212 \ingroup NPL
1313 \brief NPLA1 语法形式。
14-\version r26358
14+\version r26431
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 882
1717 \par 创建时间:
1818 2014-02-15 11:19:51 +0800
1919 \par 修改时间:
20- 2022-03-14 18:22 +0800
20+ 2022-03-29 02:31 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -53,6 +53,8 @@
5353 // LiftCollapsed, YSLib::usystem;
5454 #include "NPLA1Internals.h" // for A1::Internals API;
5555 #include YFM_NPL_SContext // for Session;
56+#include <ystdex/scope_guard.hpp> // for ystdex::unique_guard, ystdex::dismiss;
57+#include <ystdex/container.hpp> // for ystdex::prefix_eraser;
5658
5759 namespace NPL
5860 {
@@ -1855,6 +1857,45 @@
18551857 LiftOther(term, head);
18561858 }
18571859
1860+//! \since build 942
1861+//@{
1862+using TermPrefixGuard = decltype(ystdex::unique_guard(ystdex::prefix_eraser<
1863+ TermNode::Container>(std::declval<TermNode::Container&>())));
1864+
1865+TermPrefixGuard
1866+GuardTermPrefix(TermNode::Container& con) ynothrow
1867+{
1868+ return
1869+ ystdex::unique_guard(ystdex::prefix_eraser<TermNode::Container>(con));
1870+}
1871+
1872+void
1873+RemoveTermPostfix(TermPrefixGuard& gd) ynothrow
1874+{
1875+ auto& eraser(gd.func.func);
1876+ auto& tcon(eraser.container);
1877+
1878+ tcon.erase(eraser.position, tcon.end());
1879+ ystdex::dismiss(gd);
1880+}
1881+
1882+template<class _tTerm>
1883+void
1884+AccSetTerm(TermNode& term, _tTerm&& lv_l, TermNode& tail,
1885+ const shared_ptr<Environment>& d)
1886+{
1887+ {
1888+ auto& con(term.GetContainerRef());
1889+ auto gd(GuardTermPrefix(con));
1890+
1891+ con.insert(con.insert(con.begin(), yforward(lv_l)),
1892+ EvaluateBoundLValueUnwrapped(tail, d));
1893+ RemoveTermPostfix(gd);
1894+ }
1895+ term.Value.Clear();
1896+}
1897+//@}
1898+
18581899 /*!
18591900 \pre 第一参数的类型可平凡交换。
18601901 \since build 917
@@ -1871,12 +1912,7 @@
18711912 auto& con(term.GetContainerRef());
18721913 auto& lv_l(con.back());
18731914 const auto nterm_cons_combine([&, d](TermNode& tm){
1874- TermNode::Container tcon(nterm.get_allocator());
1875-
1876- tcon.push_back(EvaluateBoundLValueUnwrapped(tm, d));
1877- tcon.push_back(lv_l);
1878- tcon.swap(nterm.GetContainerRef());
1879- nterm.Value.Clear();
1915+ AccSetTerm(nterm, lv_l, tm, d);
18801916 });
18811917
18821918 // NOTE: This shall be stored separatedly to %l because %l is abstract,
@@ -1898,7 +1934,7 @@
18981934 return Combine<NonTailCall>::ReduceCallSubsequent(nterm, ctx, d,
18991935 A1::NameTypedReducerHandler(
19001936 std::bind([&, d, f](TNIter& i_1){
1901- return f(l, base, lv_l, nterm, d, i_1);
1937+ return f(l, base, lv_l, nterm, d, ++i_1);
19021938 }, std::move(i_0)), "eval-acc-head-next"));
19031939 }
19041940 LiftOther(term, base);
@@ -1917,7 +1953,7 @@
19171953 return Acc([&] YB_LAMBDA_ANNOTATE(
19181954 (TermNode& l, TermNode& base, TermNode& lv_l, TermNode& nterm,
19191955 const shared_ptr<Environment>& d, TNIter& i) , , flatten){
1920- auto& tail(*++i);
1956+ auto& tail(*i);
19211957
19221958 {
19231959 TermNode::Container tcon(base.get_allocator());
@@ -1932,14 +1968,7 @@
19321968 A1::NameTypedReducerHandler(
19331969 // XXX: Capture of %d by copy is a slightly more efficient.
19341970 [&, d] YB_LAMBDA_ANNOTATE(() , , flatten){
1935- {
1936- TermNode::Container tcon(nterm.get_allocator());
1937-
1938- tcon.push_back(EvaluateBoundLValueUnwrapped(tail, d));
1939- tcon.push_back(std::move(lv_l));
1940- tcon.swap(nterm.GetContainerRef());
1941- }
1942- nterm.Value.Clear();
1971+ AccSetTerm(nterm, std::move(lv_l), tail, d);
19431972 return Combine<NonTailCall>::ReduceCallSubsequent(nterm, ctx, d,
19441973 A1::NameTypedReducerHandler(
19451974 [&] YB_LAMBDA_ANNOTATE(() , , flatten){
@@ -1959,23 +1988,18 @@
19591988 return Acc([&] YB_LAMBDA_ANNOTATE(
19601989 (TermNode& l, TermNode&, TermNode& lv_l, TermNode& nterm,
19611990 const shared_ptr<Environment>& d, TNIter& i) , , flatten){
1991+ auto& tail(*i);
19621992 auto& n2term(*std::next(term.rbegin()));
1963- auto& tail(*++i);
19641993 const auto& lv_sum_op(*++i);
19651994
1966- {
1967- TermNode::Container tcon(n2term.get_allocator());
1968-
1969- tcon.push_back(EvaluateBoundLValueUnwrapped(tail, d));
1970- tcon.push_back(std::move(lv_l));
1971- tcon.swap(n2term.GetContainerRef());
1972- }
1973- n2term.Value.Clear();
1995+ AccSetTerm(n2term, std::move(lv_l), tail, d);
19741996 return Combine<NonTailCall>::ReduceCallSubsequent(n2term, ctx, d,
19751997 // XXX: Capture of %d by copy is a slightly more efficient.
19761998 A1::NameTypedReducerHandler(
19771999 [&, d] YB_LAMBDA_ANNOTATE(() , , flatten){
19782000 l = std::move(n2term);
2001+ // XXX: %A1::ReduceCurrentNext would not prevent direct recursive
2002+ // calls to %ReduceAccR on %NPL_Impl_NPLA1_Enable_InlineDirect.
19792003 return A1::ReduceCurrentNextThunked(
19802004 *term.emplace(ystdex::exchange(term.GetContainerRef(), [&]{
19812005 TermNode::Container tcon(term.get_allocator());
@@ -1984,9 +2008,9 @@
19842008 tcon.push_back(std::move(nterm));
19852009 return tcon;
19862010 }())), ctx,
1987- // XXX: The function after decayed is specialized enough without
1988- // %trivial_swap.
1989- ReduceAccR, trivial_swap, A1::NameTypedReducerHandler(
2011+ // XXX: The function after decayed is specialized enough without
2012+ // %trivial_swap.
2013+ ReduceAccR, trivial_swap, A1::NameTypedReducerHandler(
19902014 [&, d] YB_LAMBDA_ANNOTATE(() , , flatten){
19912015 return Combine<NonTailCall>::ReduceEnvSwitch(term, ctx, d);
19922016 }, "eval-accr-sum"));
@@ -2021,7 +2045,7 @@
20212045 \brief 准备递归调用使用的列表对象:绑定对象为列表临时对象范围。
20222046 \since build 913
20232047 */
2024-YB_FLATTEN void
2048+void
20252049 PrepareFoldRList(TermNode& term)
20262050 {
20272051 // NOTE: Conceptually, the 1st call to the list element can be applied to
@@ -2095,6 +2119,8 @@
20952119 auto i(term.begin());
20962120
20972121 if(!tr.empty())
2122+ // XXX: %A1::ReduceCurrentNext would not prevent direct recursive calls
2123+ // to %ReduceFoldR1 on %NPL_Impl_NPLA1_Enable_InlineDirect.
20982124 return A1::ReduceCurrentNextThunked(
20992125 *term.emplace(ystdex::exchange(con, [&]{
21002126 TermNode::Container tcon(term.get_allocator());
@@ -2188,14 +2214,15 @@
21882214 {
21892215 auto& con(term.GetContainerRef());
21902216
2191- con.pop_front();
21922217 // NOTE: Bind the last argument as the local reference to list temporary
21932218 // object.
21942219 PrepareFoldRList(con.back());
21952220
2196- auto& rterm(*term.emplace(ystdex::exchange(con,
2197- TermNode::Container(term.get_allocator()))));
2198-
2221+ auto i(con.begin());
2222+ auto& rterm(*i);
2223+
2224+ rterm.Clear();
2225+ rterm.GetContainerRef().splice(rterm.end(), con, ++i, con.end());
21992226 // NOTE: Save 'kons' (for %FoldR1) or 'appv' (for %Map1).
22002227 PrepareTailOp(term, ctx, *rterm.begin());
22012228 return ReduceLiftSum(term, ctx, rterm, f);
@@ -2719,8 +2746,8 @@
27192746 }
27202747 else if(IsBranchedList(extracted))
27212748 {
2722- // XXX: As %BranchFirstReferenced, with 'NPL::IsMovable(tags)'
2723- // always 'true'.
2749+ // XXX: As %BranchFirstReferenced, with 'NPL::IsMovable(tags)' always
2750+ // 'true'.
27242751 auto& nterm(*con.emplace_front().emplace());
27252752 auto& tm(AccessFirstSubterm(extracted));
27262753
diff -r eaddd0235b7a -r 30522accfd38 YFramework/source/NPL/NPLA1Internals.cpp
--- a/YFramework/source/NPL/NPLA1Internals.cpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YFramework/source/NPL/NPLA1Internals.cpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Internals.cpp
1212 \ingroup NPL
1313 \brief NPLA1 内部接口。
14-\version r20595
14+\version r20599
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 473
1717 \par 创建时间:
1818 2020-02-15 13:20:08 +0800
1919 \par 修改时间:
20- 2022-03-09 02:27 +0800
20+ 2022-04-05 15:19 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 非公开模块名称:
@@ -27,8 +27,8 @@
2727
2828 #include "NPL/YModules.h"
2929 #include "NPLA1Internals.h" // for NPL::Deref, Environment, ystdex::dismiss,
30-// shared_ptr, NPL::get, std::throw_with_nested, InvalidSyntax,
31-// std::make_move_iterator, NPL::AsTermNode;
30+// shared_ptr, std::make_move_iterator, NPL::get, std::throw_with_nested,
31+// ParameterMismatch, std::allocator_arg, NPL::AsTermNode;
3232
3333 namespace NPL
3434 {
@@ -211,10 +211,10 @@
211211 {
212212 auto p_act(AccessTCOAction(ctx));
213213
214- if(!p_act)
214+ if(YB_UNLIKELY(!p_act))
215215 {
216216 SetupTailTCOAction(ctx, term, {});
217- p_act = AccessTCOAction(ctx);
217+ p_act = AccessTCOActionUnchecked(ctx);
218218 }
219219 return NPL::Deref(p_act);
220220 }
diff -r eaddd0235b7a -r 30522accfd38 YFramework/source/NPL/NPLA1Internals.h
--- a/YFramework/source/NPL/NPLA1Internals.h Thu Mar 17 22:05:52 2022 +0800
+++ b/YFramework/source/NPL/NPLA1Internals.h Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLA1Internals.h
1212 \ingroup NPL
1313 \brief NPLA1 内部接口。
14-\version r21955
14+\version r22014
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 882
1717 \par 创建时间:
1818 2020-02-15 13:20:08 +0800
1919 \par 修改时间:
20- 2022-03-08 00:46 +0800
20+ 2022-04-05 13:40 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 非公开模块名称:
@@ -33,18 +33,17 @@
3333 // ReductionStatus, Reducer, YSLib::map, lref, Environment,
3434 // set, NPL::Deref, IsTyped, EnvironmentList, EnvironmentReference, tuple,
3535 // YSLib::get, YSLib::forward_list, size_t, list, std::declval,
36-// EnvironmentGuard, NPL::make_observer, std::allocator_arg,
37-// A1::NameTypedReducerHandler, A1::NameTypedContextHandler, TermReference,
38-// ThrowTypeErrorForInvalidType, type_id, ystdex::exclude_self_t,
39-// ParameterMismatch, NPL::TryAccessLeaf, IsIgnore;
36+// EnvironmentGuard, MakeKeptGuard, A1::NameTypedContextHandler, TermReference,
37+// ThrowTypeErrorForInvalidType, NPL::TryAccessLeaf, type_id,
38+// TermToNamePtr, IsIgnore, ystdex::exclude_self_t, ParameterMismatch;
4039 #include <ystdex/compose.hpp> // for ystdex::get_less;
4140 #include <ystdex/scope_guard.hpp> // for ystdex::unique_guard;
4241 #include <ystdex/utility.hpp> // for ystdex::exchange;
42+#include <ystdex/ref.hpp> // for std::reference_wrapper, std::ref,
43+// ystdex::unref;
44+#include <ystdex/bind.hpp> // for ystdex::bind1, std::placeholders::_2;
45+#include <ystdex/function_adaptor.hpp> // for ystdex::update_thunk;
4346 #include <iterator> // for std::next;
44-#include <ystdex/ref.hpp> // for ystdex::unref;
45-#include <ystdex/bind.hpp> // for std::bind, std::placeholders::_1, std::ref,
46-// ystdex::bind1, std::placeholders::_2;
47-#include <ystdex/function_adaptor.hpp> // for ystdex::update_thunk;
4847
4948 namespace NPL
5049 {
@@ -85,7 +84,7 @@
8584 "Invalid combination of build options found.");
8685
8786 //! \since build 842
88-YB_FLATTEN inline PDefH(void, SetupNextTerm, ContextNode& ctx, TermNode& term)
87+inline PDefH(void, SetupNextTerm, ContextNode& ctx, TermNode& term)
8988 ImplExpr(ContextState::Access(ctx).SetNextTermRef(term))
9089
9190 // NOTE: See $2018-09 @ %Documentation::Workflow for rationale of the
@@ -382,8 +381,8 @@
382381 // NOTE: If there is no environment set in %act.EnvGuard yet, there is
383382 // ideally no need to save the components to the frame record list
384383 // for recursive calls. In such case, each operation making
385- // potentionally overwriting of %act.attached_owned will always get into
386- // this call and that time %act.EnvGuard should be set.
384+ // potentionally calling to %Attach will always get into this call and
385+ // that time %EnvGuard should be set.
387386 if(EnvGuard.func.SavedPtr)
388387 {
389388 // NOTE: Operand saving is performed whether the frame compression
@@ -444,6 +443,12 @@
444443 YB_ATTR_nodiscard YB_PURE inline
445444 PDefH(TCOAction*, AccessTCOAction, ContextNode& ctx) ynothrow
446445 ImplRet(ctx.AccessCurrentAs<TCOAction>())
446+
447+//! \since build 942
448+YB_ATTR_nodiscard YB_PURE inline
449+ PDefH(TCOAction*, AccessTCOActionUnchecked, ContextNode& ctx) ynothrowv
450+ ImplRet(ctx.AccessCurrentAsUnchecked<TCOAction>())
451+
447452 // NOTE: There is no need to check term like
448453 // 'if(&p->GetTermPtr().get() == &term)'. It should be same to saved enclosing
449454 // term unless a nested TCO action is needed explicitly (by following
@@ -491,25 +496,6 @@
491496 return act;
492497 }
493498 # endif
494-
495-//! \since build 879
496-inline ReductionStatus
497-MoveGuard(EnvironmentGuard& gd, ContextNode& ctx) ynothrow
498-{
499- const auto egd(std::move(gd));
500-
501- return ctx.LastStatus;
502-}
503-
504-//! \since build 898
505-using MoveGuardAction = decltype(std::bind(MoveGuard,
506- std::declval<EnvironmentGuard>(), std::placeholders::_1));
507-
508-//! \since build 898
509-YB_ATTR_nodiscard inline
510- PDefH(MoveGuardAction, MakeMoveGuard, EnvironmentGuard& gd)
511- ImplRet(A1::NameTypedReducerHandler(std::bind(MoveGuard, std::move(gd),
512- std::placeholders::_1), "eval-guard"))
513499 #endif
514500
515501
@@ -633,7 +619,7 @@
633619
634620 //! \since build 910
635621 template<typename _fCurrent, typename _fNext>
636-YB_FLATTEN inline ReductionStatus
622+inline ReductionStatus
637623 RelayCurrentNext(ContextNode& ctx, TermNode& term, _fCurrent&& cur,
638624 _fNext&& next)
639625 {
@@ -648,7 +634,7 @@
648634 }
649635 //! \since build 926
650636 template<typename _fCurrent, typename _fNext>
651-YB_FLATTEN inline ReductionStatus
637+inline ReductionStatus
652638 RelayCurrentNext(ContextNode& ctx, TermNode& term, _fCurrent&& cur,
653639 trivial_swap_t, _fNext&& next)
654640 {
@@ -662,7 +648,7 @@
662648 }
663649 //! \since build 926
664650 template<typename _fCurrent, typename _fNext>
665-YB_FLATTEN inline ReductionStatus
651+inline ReductionStatus
666652 RelayCurrentNext(ContextNode& ctx, TermNode& term, trivial_swap_t,
667653 _fCurrent&& cur, _fNext&& next)
668654 {
@@ -676,7 +662,7 @@
676662 }
677663 //! \since build 926
678664 template<typename _fCurrent, typename _fNext>
679-YB_FLATTEN inline ReductionStatus
665+inline ReductionStatus
680666 RelayCurrentNext(ContextNode& ctx, TermNode& term, trivial_swap_t,
681667 _fCurrent&& cur, trivial_swap_t, _fNext&& next)
682668 {
@@ -691,7 +677,7 @@
691677
692678 //! \since build 916
693679 template<typename _fCurrent, typename _fNext>
694-YB_FLATTEN inline ReductionStatus
680+inline ReductionStatus
695681 RelayCurrentNextThunked(ContextNode& ctx, TermNode& term, _fCurrent&& cur,
696682 _fNext&& next)
697683 {
@@ -705,7 +691,7 @@
705691 }
706692 //! \since build 926
707693 template<typename _fCurrent, typename _fNext>
708-YB_FLATTEN inline ReductionStatus
694+inline ReductionStatus
709695 RelayCurrentNextThunked(ContextNode& ctx, TermNode& term, _fCurrent&& cur,
710696 trivial_swap_t, _fNext&& next)
711697 {
@@ -719,7 +705,7 @@
719705 }
720706 //! \since build 926
721707 template<typename _fCurrent, typename _fNext>
722-YB_FLATTEN inline ReductionStatus
708+inline ReductionStatus
723709 RelayCurrentNextThunked(ContextNode& ctx, TermNode& term, trivial_swap_t,
724710 _fCurrent&& cur, _fNext&& next)
725711 {
@@ -733,7 +719,7 @@
733719 }
734720 //! \since build 926
735721 template<typename _fCurrent, typename _fNext>
736-YB_FLATTEN inline ReductionStatus
722+inline ReductionStatus
737723 RelayCurrentNextThunked(ContextNode& ctx, TermNode& term, trivial_swap_t,
738724 _fCurrent&& cur, trivial_swap_t, _fNext&& next)
739725 {
@@ -817,13 +803,13 @@
817803 // being captured and it is not capturable here. No %SetupNextTerm
818804 // needs to be called here. Otherwise, %cur is not a %Contiuation and
819805 // it shall still handle the capture of the term by itself. The %term
820- // is optinonally used in direct calls instead of the next term setup,
806+ // is optionally used in direct calls instead of the next term setup,
821807 // while they shall be equivalent.
822808 #if NPL_Impl_NPLA1_Enable_Thunked
823809 // TODO: Blocked. Use C++14 lambda initializers to simplify the
824810 // implementation.
825811 return A1::RelayCurrentNext(ctx, term, yforward(cur), trivial_swap,
826- MakeMoveGuard(gd));
812+ MakeKeptGuard(gd));
827813 #else
828814 yunused(gd);
829815 return A1::RelayDirect(ctx, cur, term);
@@ -837,7 +823,7 @@
837823 {
838824 // XXX: See %RelayNextGuarded.
839825 #if NPL_Impl_NPLA1_Enable_Thunked
840- auto act(MakeMoveGuard(gd));
826+ auto act(MakeKeptGuard(gd));
841827 // TODO: Blocked. Use C++14 lambda initializers to simplify the
842828 // implementation.
843829 // XXX: Term reused. Call of %SetupNextTerm is not needed as the next
@@ -868,7 +854,7 @@
868854 #if NPL_Impl_NPLA1_Enable_Thunked
869855 // TODO: Blocked. Use C++14 lambda initializers to simplify the
870856 // implementation.
871- auto act(MakeMoveGuard(gd));
857+ auto act(MakeKeptGuard(gd));
872858
873859 if(lift)
874860 {
@@ -941,7 +927,7 @@
941927
942928 //! \pre TCO 实现:当前动作是 TCO 动作,且其中的当前项和被规约的项相同。
943929 template<typename _fCurrent>
944- YB_FLATTEN static inline ReductionStatus
930+ static inline ReductionStatus
945931 RelayNextGuardedLifted(ContextNode& ctx, TermNode& term,
946932 EnvironmentGuard&& gd, _fCurrent&& cur)
947933 {
@@ -1079,7 +1065,7 @@
10791065 bound.GetContainer(), EnsureLValueReference(TermReference(ref))))
10801066
10811067 //! \since build 920
1082-YB_FLATTEN inline PDefH(void, SetEvaluatedValue, TermNode& term,
1068+inline PDefH(void, SetEvaluatedValue, TermNode& term,
10831069 TermNode& bound, shared_ptr<Environment>& p_env)
10841070 ImplExpr(term.Value = TermReference(NPL::Deref(p_env).MakeTermTags(bound)
10851071 & ~TermTags::Unique, bound, std::move(p_env)))
@@ -1098,6 +1084,17 @@
10981084 char
10991085 ExtractSigil(string_view&);
11001086
1087+//! \since build 942
1088+template<typename _func>
1089+YB_FLATTEN inline void
1090+HandleOrIgnore(_func f, const TermNode& t, bool t_has_ref)
1091+{
1092+ if(const auto p = TermToNamePtr(t))
1093+ f(*p);
1094+ else if(!IsIgnore(t))
1095+ ThrowFormalParameterTypeError(t, t_has_ref);
1096+}
1097+
11011098
11021099 //! \since build 917
11031100 //@{
@@ -1155,10 +1152,8 @@
11551152 Match(nd, true);
11561153 });
11571154 }
1158- else if(const auto p = TermToNamePtr(t))
1159- BindValue(*p);
1160- else if(!IsIgnore(t))
1161- ThrowFormalParameterTypeError(t, t_has_ref);
1155+ else
1156+ HandleOrIgnore(std::ref(BindValue), t, t_has_ref);
11621157 }
11631158
11641159 void
diff -r eaddd0235b7a -r 30522accfd38 YFramework/source/NPL/NPLAMath.cpp
--- a/YFramework/source/NPL/NPLAMath.cpp Thu Mar 17 22:05:52 2022 +0800
+++ b/YFramework/source/NPL/NPLAMath.cpp Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPLAMath.cpp
1212 \ingroup NPL
1313 \brief NPLA 数学功能。
14-\version r28310
14+\version r28330
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 930
1717 \par 创建时间:
1818 2021-11-03 12:50:49 +0800
1919 \par 修改时间:
20- 2022-02-08 22:27 +0800
20+ 2022-02-08 19:39 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -38,8 +38,9 @@
3838 #include <ystdex/exception.h> // for ystdex::unsupported, std::domain_error;
3939 #include <ystdex/cstdint.hpp> // for std::numeric_limits,
4040 // ystdex::make_widen_int;
41-#include <ystdex/functional.hpp> // for ystdex::retry_on_cond, ystdex::equal_to,
42-// ystdex::less, ystdex::greater, ystdex::less_equal, ystdex::greater_equal;
41+#include <ystdex/expanded_function.hpp> // for ystdex::retry_on_cond;
42+#include <ystdex/functor.hpp> // for ystdex::equal_to, ystdex::less,
43+// ystdex::greater, ystdex::less_equal, ystdex::greater_equal;
4344 // XXX: The type 'long double' may be also IEC 60559 binary64 format, e.g. in
4445 // Microsoft VC++.
4546 #include <cfloat> // for FLT_*, DBL_*, LDBL_*;
@@ -129,11 +130,11 @@
129130 using has_special_value_t = ystdex::bool_<std::numeric_limits<
130131 _type>::has_infinity && std::numeric_limits<_type>::has_quiet_NaN>;
131132
132-static_assert(has_special_value_t<float>::value,
133+static_assert(has_special_value_t<float>(),
133134 "Unsupported implementation found.");
134-static_assert(has_special_value_t<double>::value,
135+static_assert(has_special_value_t<double>(),
135136 "Unsupported implementation found.");
136-static_assert(has_special_value_t<long double>::value,
137+static_assert(has_special_value_t<long double>(),
137138 "Unsupported implementation found.");
138139 //@}
139140
@@ -579,7 +580,12 @@
579580 YB_ATTR_nodiscard yconstfn
580581 yimpl(ystdex::exclude_self_t)<ValueObject, _tParam, _tRet>
581582 operator()(_tParam&& x) const
583+ // XXX: See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52869.
584+#if !YB_IMPL_GNUCPP || YB_IMPL_GNUCPP >= 90000
582585 ynoexcept_spec(_tBase::operator()(yforward(x)))
586+#else
587+ ynoexcept_spec(std::declval<GUOp>()._tBase::operator()(yforward(x)))
588+#endif
583589 {
584590 return _tBase::operator()(yforward(x));
585591 }
@@ -598,7 +604,13 @@
598604 YB_ATTR_nodiscard yconstfn
599605 yimpl(ystdex::exclude_self_t)<ValueObject, _tParam2, _tRet>
600606 operator()(_tParam1&& x, _tParam2&& y) const
607+ // XXX: Ditto.
608+#if !YB_IMPL_GNUCPP || YB_IMPL_GNUCPP >= 90000
601609 ynoexcept_spec(_tBase::operator()(yforward(x), yforward(y)))
610+#else
611+ ynoexcept_spec(
612+ std::declval<GBOp>()._tBase::operator()(yforward(x), yforward(y)))
613+#endif
602614 {
603615 return _tBase::operator()(yforward(x), yforward(y));
604616 }
@@ -2963,8 +2975,8 @@
29632975 int exp_bin, typename fp_traits<_type>::carrier_type& sig_dec, int& exp_dec)
29642976 ynothrow
29652977 {
2966- using traits = fp_traits<_type>;
2967- using carrier_type = typename traits::carrier_type;
2978+ using traits_type = fp_traits<_type>;
2979+ using carrier_type = typename traits_type::carrier_type;
29682980 // NOTE: This algorithm requires extra 2 bits in %sig_bin to prevent
29692981 // overflow. Every conforming floating-point format shall meet the
29702982 // requirement, since the sign bit and the exponent bits shall be no less
@@ -2974,7 +2986,7 @@
29742986 "Invalid carrier type found.");
29752987
29762988 // NOTE: Ditto.
2977- YAssert(sig_bin >> (traits::value_bits - 2) == carrier_type(),
2989+ YAssert(sig_bin >> (traits_type::value_bits - 2) == carrier_type(),
29782990 "Invalid significand value found.");
29792991
29802992 // NOTE: %shorter indicates the lower bound is closer, i.e. the "shorter
diff -r eaddd0235b7a -r 30522accfd38 doc/ChangeLog.V0.9.txt
--- a/doc/ChangeLog.V0.9.txt Thu Mar 17 22:05:52 2022 +0800
+++ b/doc/ChangeLog.V0.9.txt Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file ChangeLog.V0.9.txt
1212 \ingroup Documentation
1313 \brief 版本更新历史记录 - V0.9 。
14-\version r8636
14+\version r8973
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 800
1717 \par 创建时间:
1818 2020-10-12 17:19:23 +0800
1919 \par 修改时间:
20- 2022-03-17 21:08 +0800
20+ 2022-04-05 16:32 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -34,6 +34,341 @@
3434 (
3535 / %YBase.YStandardEx $=
3636 (
37+ + "transformation_traits %(empty_pack_t, sizeof_pack_t)" @ %Meta,
38+ / %TypeOperation $=
39+ (
40+ + "metafunction %nonempty_pack_or_t"
41+ ^ $dep_from ("%empty_pack_t" @ %Meta);
42+ + "metafunction %head_of_t"
43+ ),
44+ / DLDI "all '_tKey' parameter 'key'" => 'k'
45+ $effective ("%exists" @ %Container, "%cache_lookup" @ %Cache),
46+ // To be consistent to most other container routines.
47+ / DD %Placement $=
48+ (
49+ + "Doxygen group %guards";
50+ + '\ingroup grauds' @ "Doxygen comment"
51+ @ "alias template %placement_ptr"
52+ ),
53+ / ("class template %variadic_param", "function templates \
54+ %(sizeof_params, varg, chain_apply, unseq_apply)") @ %Function
55+ >> %Apply,
56+ / %Container $=
57+ (
58+ / DLI "%is_piecewise_mapped" ^ '_tCon' ~ '_tAssocCon',
59+ + 'ynothrow'
60+ @ "function templates %(extract_key, extract_mapped)",
61+ (
62+ + "type traits %(has_mem_key_comp, has_mem_lower_bound)";
63+ * "missing support of unordered containers"
64+ @ ("function templates %search_map" $since b680,
65+ "function template %emplace_hint_in_place" $since b708;
66+ $comp "function templates %search_map_by" $since b734,
67+ $comp "function templates %(try_emplace, try_emplace_hint, \
68+ insert_or_assign, insert_or_assign_hint)" $since b680)
69+ ),
70+ / "function templates %(cast_mutable#2, search_map, search_map_by)"
71+ ^ "ADL %cast_mutable" ~ "%ystdex::cast_mutable",
72+ / @ "all 2 function templates %cast_mutable" $=
73+ (
74+ + $dev $lib 'YB_ATTR_nodiscard YB_PURE'
75+ + "exception specification"
76+ ),
77+ / "all 2 function templates %range_size" >> %Range,
78+ / DLDI "inclusion %Function" -> "%Apply"
79+ $dep_from ("%seq_apply" @ %Function),
80+ + "functor %prefix_eraser",
81+ + "support of default key template argument"
82+ @ "all 2 function templates %search_map"
83+ ),
84+ / %Cache $=
85+ (
86+ + $re_add(b937) DD "'\warning' command for missing virtual \
87+ destruction" @ "Doxygen comment" @ "class templates \
88+ %(recent_used_list, used_list_cache)",
89+ / @ "class template %recent_used_list" $=
90+ (
91+ / "enabled allocator" $=
92+ (
93+ / "type %list_type" ^ "%allocator_type",
94+ + 'using list_type::list_type;'
95+ ),
96+ - "check on the function" @ "function template %shrink#2",
97+ / 'ynothrow' @ "function %undo_emplace" -> 'ynothrowv'
98+ ),
99+ / @ "class template %used_list_cache" $=
100+ (
101+ + $dev $lib 'YB_ATTR_nodiscard YB_PURE' @ ("all 2 functions \
102+ %(begin, end)", "functions %(get, list, size)")
103+ + 'ynothrow' @ "all 2 functions %(begin, end)",
104+ / $forced DLDI "removed internal checks"
105+ @ "%recent_used_list::shrink",
106+ / @ "data member %flush" $=
107+ (
108+ / "supported empty state" ^ "%optional_function"
109+ ~ "%function" $dep_to "optional flush",
110+ + DD "commands" @ "Doxygen comment"
111+ ),
112+ / "internal maximum elements checks" $=
113+ (
114+ / $comp $forced DLDI $dep_from "optional flush"
115+ $dep_from "%recent_used_list::shrink",
116+ / DLI "prevended copy of %flush" ^ "%std::ref"
117+ ),
118+ / @ "function template %emplace with parameter of \
119+ 'const key_type&'" $=
120+ (
121+ * "missing excluding key type at the front of the \
122+ parameter type" $since b611
123+ $= (/ $impl
124+ ^ $dep_from ("%head_of_t" @ %TypeOperation)),
125+ * "wrong used list access" $since b611
126+ $= (/ $impl ^ 'emplace' ~ 'emplace_front'),
127+ * "ill-formed iterator component returned" $since b611,
128+ / DLDI "simplified" ^ "%ystdex::make_guard"
129+ ),
130+ + "function template %emplace with parameter of \
131+ %std::piecewise_construct_t",
132+ / @ "function template %emplace with parameter of '_tParams&&'"
133+ $=
134+ (
135+ / "optimized for associative containers"
136+ ^ ($dep_from ("%ystdex::search_map_by" @ %Container),
137+ "%used_cache_type::emplace_hint")
138+ ~ "%used_cache_type::emplace",
139+ / DLDI "simplified" ^ "%ystdex::unique_guard"
140+ ),
141+ * "wrong return value for const-qualified %(begin, end)"
142+ $since b611
143+ $= (/ ^ ('begin', 'end') ~ ('cbegin' ~ 'cend')),
144+ // The results are from the mutable data member.
145+ / $lib "functions %(begin, end)" ^ "%ystdex::(begin, end)"
146+ ~ "member functions"
147+ ),
148+ + "friend function %cast_mutable" @ "class templates \
149+ %(recent_used_list; used_list_cache)";
150+ / @ "function template %cache_lookup" $=
151+ (
152+ / DLDI "parameter 'cache'" => 'm',
153+ / ^ $dep_from ("%ystdex::search_map_by",
154+ "ADL %emplace_hint_in_place") @ %Container
155+ ~ "check to throwing exception on insertion failure"
156+ $dep_from ("%(cast_mutable, emplace)" @ "%used_list_cache",
157+ "%cast_mutable" @ %Container)
158+ // The check is needed because no support of concurrent \
159+ accesses is provided.
160+ )
161+ ),
162+ - DLDI "redundant header inclusion" @ %MemoryResource
163+ ),
164+ / %YFramework $=
165+ (
166+ / %NPL $=
167+ (
168+ / %SContext $=
169+ (
170+ / DLDI !^ "%ystdex::decay_t",
171+ / @ $impl "return type" @ "function template %AsTermNode#1" $=
172+ (
173+ / "allowed not excluding allocator type other than at the \
174+ front of the parameter pack"
175+ ^ $dep_from ("%ystdex::head_of_t"
176+ @ %YBase.YStandardEx.TypeOperation)
177+ ~ "%ystdex::(bool_, cond_or_t_)";
178+ / DLDI "simplified" ^ "%enable_if_convertible_t"
179+ ~ ("%ystdex::(enable_if_t, not_, false_)",
180+ "%std::isconvertible", 'sizeof...')
181+ )
182+ ),
183+ / DLI %NPLA1Forms $=
184+ (
185+ - "several internal 'YB_FLATTEN'",
186+ / "reused subterm on preparation" @ "functions %(FoldR1, Map1)",
187+ / $forced $design "function templates %(RegisterUnary, \
188+ RegisterBinary)" $dep_from ("%RegisterHandler" @ %NPLA1)
189+ ),
190+ / %NPLA1 $=
191+ (
192+ + '\ingroup grauds' @ "Doxygen comment"
193+ @ "alias %EnvironmentGuard",
194+ / "function template %RegisterHandler"
195+ -> "2 function templates %RegisterFormHandler",
196+ / DLI @ "functions %(RegisterForm, RegisterStrict)" $=
197+ (
198+ / $forced $design $dep_from "%RegisterHandler"
199+ + 'YB_ATTR_always_inline'
200+ ),
201+ / DLDI "leaf handling" @ "functions %(MatchParameter, \
202+ BindParameter, BindParameterWellFormed)" $=
203+ (
204+ "simplified the check"
205+ ^ $dep_from ("%HandleOrIgnore" @ %NPLA1Interals),
206+ + 'inline' @ "internal function templates"
207+ )
208+ ),
209+ / DLI %Dependency $=
210+ (
211+ / DLI "avoided redundant %string copy" @ ("function \
212+ %ReduceToLoadExternal", "native implementation"
213+ @ "applicative %require"
214+ @ "function %LoadModule_std_modules"),
215+ / DLI "function %ReduceToLoadExternal" ^ "allocator",
216+ // The copy constructor would not propagate the allocator \
217+ by default.
218+ / DLI "module name concatenations" @ "function \
219+ %LoadStandardContext" ^ ("allocator", "%string::resize"),
220+ * DLI "redundant %string argument copying"
221+ @ "native implementation"
222+ @ "applicatives ('find-requirement-filename', %require)"
223+ @ "function %LoadModule_std_modules" $since b923,
224+ / DLI "simplified %(AccL, AccR)" ^ $dep_from
225+ ("%ystdex::prefix_eraser" @ %YBase.YStandardEx.Container),
226+ / DLDI "newline" @ "alternative derivation" @ "appliative \
227+ 'env-empty?'" @ "function %LoadModule_std_system",
228+ + DD ("'\pre' command", "'\exception' command") @ "Doxygen comment"
229+ @ "functions %(LoadModule_std_io, LoadModule_std_system, \
230+ LoadModule_std_modules)",
231+ / "forwareded function parameter"
232+ @ "function templates %(InvokeIn, GetModuleFor)",
233+ + "pamameter pack as invocation arguments" @ "function templates \
234+ %(InvokeIn, (GetModuleFor; LoadModule; LoadModuleChecked))",
235+ / DLI @ "function %LoadGroundContext" $=
236+ (
237+ / $design "parameter name 'wenv'" @ "native implementation"
238+ @ "applicative 'lock-environment'" -> 'r_env',
239+ // To be consistent to other uses.
240+ / "simplified native implementation"
241+ @ "applicative 'make-standard-environment'"
242+ !^ ('YB_LAMBDA_ANNOTATE', 'flatten'),
243+ / DLDI "alternative derivation" @ "operatives ('$and', \
244+ '$or')" ^ ('eval%', 'cons') ~ ("%apply", 'wrap')
245+ // See also the update in the rationale in \
246+ %Documentation.NPL (zh-CN).
247+ ),
248+ / @ "function %LoadModule_std_io" $=
249+ (
250+ + "2nd parameter of 'const shared_ptr<Environment>&' to \
251+ reperesent the ground environment"
252+ $dep_to "requiring ground for std.io";
253+ + "applicative 'get-module'"
254+ $dep_from ("%MakeKeptGuard" @ %NPLA1)
255+ ),
256+ / @ "function %LoadModule_std_modules" $=
257+ (
258+ + "2nd parameter of 'const shared_ptr<Environment>&' to \
259+ reperesent the ground environment"
260+ $dep_to "requiring ground for std.modules",
261+ (
262+ + $impl "interna registry saved fresh standard environment";
263+ / "returned the fresh standard environment"
264+ @ "appliative 'register-requirement!'"
265+ ),
266+ / @ "applicative %require" $=
267+ (
268+ * "missing parameter arity check"
269+ @ "native implementation" $since b923,
270+ * "wrongly moved arguments if modifiable when loaded at \
271+ 1st time" $since b923,
272+ * "missing switching to the fresh standard environment \
273+ before loading" $since b923
274+ $dep_from "appliative 'register-requirement!'",
275+ (
276+ * "missing preserving reference results"
277+ @ "alternative derivation" $since b923;
278+ / "saved cached result and took it as the call result \
279+ after the 1st loading"
280+ // This is an explicit change on the interface \
281+ different to [RnRK] to make it easier to use.
282+ ),
283+ + "support of optional 'module-parameters'",
284+ // As 'get-module'.
285+ / DLDI "simplified guard"
286+ ^ $dep_from ("%MakeKeptGuard" @ %NPLA1)
287+ ),
288+ * "missing saving %errno before throwing the exception"
289+ @ "applicative %SHBuild_MakeTempFilename"
290+ @ "function %LoadModule_SHBuild" $since b904
291+ // The evaluation of the other argument may overwrite \
292+ %errno.
293+ ),
294+ / DLDI "function %LoadStandardContext" $=
295+ (
296+ / "simplified"
297+ $dep_from "%LoadModuleChecked" !^ "%std::(bind, ref)",
298+ / $forced $dep_from ("requiring ground for std.io",
299+ "requiring ground for std.modules")
300+ )
301+ ),
302+ / %NPLAMath $=
303+ (
304+ / DLDI "simplified internal static assertions" ^ '()'
305+ ~ '::value',
306+ / $re_add(b941) DLDI "all member type %traits"
307+ => "%traits_type",
308+ + DLI "workaround on exception specifications"
309+ @ !'!YB_IMPL_GNUCPP || YB_IMPL_GNUCPP >= 90000'
310+ ),
311+ / %NPLA $=
312+ (
313+ + DD "'\return' command" @ "Doxygen comment"
314+ @ "function templates %AllocateEnvironment"
315+ + 'ynothrow' @ "move %operator="
316+ @ 'NPL_NPLA_CheckEnvironmentReferenceCount'
317+ @ "class %EnvironmentReference",
318+ // Also to eliminate Microsoft VC++ 2022 warning: C26439.
319+ / @ "class %ContextNode" $=
320+ (
321+ + "function template %AccessCurrentAsUnchecked";
322+ / DLDI "simplified function template %AccessCurrentAs"
323+ ^ "%AccessCurrentAsUnchecked"
324+ )
325+ ),
326+ / %NPLA1Internals $=
327+ (
328+ / ("functions %(MoveGuard, MakeMoveGuard)",
329+ "alias %MoveGuardAction") @ 'NPL_Impl_NPLA1_Enable_Thunked'
330+ -> ("function templates %(KeepGuard, MakeKeptGuard)",
331+ ("alias tempalte %GKeptGuardAction") @ %NPLA1),
332+ // Since the guard is sequenced in %ReducerSequence, it \
333+ is guaranteed to be released in order in both the \
334+ normal and exceptional paths, so there is no need to \
335+ explicit move and release the guard (except in \
336+ %TCOAction::operator() needing to be ordered with \
337+ other resource releases in the same action, which is \
338+ not handled here).
339+ / $forced DLI "simplified guard" $effective
340+ @ "class %NonTailCall",
341+ (
342+ + "function template %HandleOrIgnore";
343+ / DLDI "simplified matching" @ "class tmeplate \
344+ %GParameterValueMatcher" ^ "%HandleOrIgnore"
345+ ),
346+ - DLDI 'YB_FLATTEN' @ ("function %SetupNextTerm",
347+ @ "all 4 function templates %RelayCurrentNextThunked",
348+ "function template %RelayNextGuardedLifted"
349+ @ "struct %TailCall"),
350+ - DLI 'YB_FLATTEN' @ ("all 4 function templates \
351+ %RelayCurrentNext", "function %SetEvaluatedValue"),
352+ // This now improves the performance.
353+ (
354+ + "function %AccessTCOActionUnchecked" ^ $dep_from
355+ ("%ContextNode::AccessCurrentAsUnchecked" @ %NPLA);
356+ / DLI "optimized %EnsureTCOAction"
357+ ^ ('YB_UNLIKELY', "%AccessTCOActionUnchecked")
358+ )
359+ )
360+ )
361+ ),
362+ + "support of varaible %C_CXXFLAGS_EXT" @ "%SHBuild-YSLib-common.txt"
363+ @ %Tools.Scripts
364+ // This allows to configure %_POSIX_C_SOURCE automatically when it is \
365+ not available by default (e.g. native Termux).
366+),
367+
368+b941
369+(
370+ / %YBase.YStandardEx $=
371+ (
37372 / %Placement $=
38373 (
39374 / DLDI "header inclusion %IteratorOperation"
@@ -2199,7 +2534,7 @@
21992534 (
22002535 - 'YB_ATTR(always_inline)' @ "function template %destruct_in",
22012536 // Ditto.
2202- + "workaround to avoid removing the inlineing attribute"
2537+ + DLI "workaround to avoid removing the inlining attribute"
22032538 @ '(YB_IMPL_GNUCPP && !YB_IMPL_CLANGPP) && \
22042539 defined(NDEBUG) && __OPTIMIZE__'
22052540 @ "function template %construct_within"
@@ -3414,7 +3749,7 @@
34143749 / DLDI "simplified function %CheckEnvironmentFormal"
34153750 !^ 'YB_UNLIKELY',
34163751 / DLI "optimized member function %FormContextHandler::operator()"
3417- !^ "%NPL::ToReducer",
3752+ ^ "%any_ops::trivial_swap" ~ "%NPL::ToReducer",
34183753 / @ "function %DefaultEvaluateLeaf" $=
34193754 (
34203755 - "'#n' and '#null' handling",
@@ -6753,7 +7088,7 @@
67537088 // See $2020-10 @ %Documentation::Workflow.
67547089 - "variables %(LDFLAGS_DYN; LDFLAGS_DYN_BASE, \
67557090 LDFLAGS_DYN_EXTRA)";
6756- - "variables %(LIBS_RPATH, LIBPFX, DSOSFX)"
7091+ - "variables %(LIBS_RPATH, LIBPFX, DSOSFX)",
67577092 - "variable %EXESFX"
67587093 $dep_from ("build command line" @ %Test)
67597094 ),
diff -r eaddd0235b7a -r 30522accfd38 doc/NPL.txt
--- a/doc/NPL.txt Thu Mar 17 22:05:52 2022 +0800
+++ b/doc/NPL.txt Tue Apr 05 16:56:05 2022 +0800
@@ -11,13 +11,13 @@
1111 /*! \file NPL.txt
1212 \ingroup Documentation
1313 \brief NPL 规范和实现规格说明。
14-\version r26784
14+\version r27151
1515 \author FrankHB <frankhb1989@gmail.com>
1616 \since build 304
1717 \par 创建时间:
1818 2012-04-25 10:34:20 +0800
1919 \par 修改时间:
20- 2022-03-07 02:52 +0800
20+ 2022-03-31 08:09 +0800
2121 \par 文本编码:
2222 UTF-8
2323 \par 模块名称:
@@ -238,7 +238,7 @@
238238 正确性应蕴含完整性。
239239 由 @1.5.3.1 的推论:设计应包含完整的需求响应。
240240 **注释**
241-对通用编程语言的一个完整性要求是支持计算上的可表达性。
241+对通用编程语言的一个完整性要求是支持计算上的可表达性(expresiveness) 。
242242 这种性质被称为可有效计算性(effective computability) ,或 Turing 完备性(Turing completeness) 。在可物理实现的计算普遍遵循 Church-Turing 论题(Church-Turing thesis) 的情形下,这同时是可计算性(computability) 。以上性质一般不加分辨。
243243 特定的场合要求更弱的性质,例如类型检查等情形需要全(total) 计算而确保实现总是可终止。这种要求在完整的实现中可通过附加的设施(用户提供的标注或证明)保证,而不应通过系统设计的规则静态地排除,否则实现是不完整的。仅在作为领域特定语言时,通过从需求中排除可计算性,静态规则作为优化是被允许的。
244244
@@ -252,7 +252,7 @@
252252
253253 @1.5.3.3 可修改性(modifiablity) :
254254 在满足需求的前提下,修改应尽可能少地有碍于其它的接口。
255-这是 @1.5.2.1 的推论。
255+这是变化的自由(@1.5.2.1) 的推论。
256256
257257 @1.5.3.4 避免抽象泄漏(abstraction leak) :
258258 泄漏的抽象(leaky abstraction) 指抽象的底层复杂性没有被抽象合理地隐藏(@1.5.6.3) ,而在一定程度上构成了利用抽象时的不必要的依赖。
@@ -270,8 +270,12 @@
270270 @1.5.3.6 开放性(openness) :
271271 除非另行指定,不假定实体不存在。
272272 这个原则主要用于建模(modeling) 的依据。对一般的模型,这个原则称为开放世界假设(open-world assumption) 。
273-与之相对,封闭世界假设(closed-world assumption) 需要提前设置一个全集以保持合规。
274-封闭世界假设表面上可能简化实现,但在一般的模型中是不必要的,因为保持问题合规性的论域应已由清晰的需求描述规范,不应为此阻碍 @1.5.2.1 的实现。
273+与之相对,封闭世界假设(closed-world assumption) 需要提前设置一个全集(universe) 以保持合规。
274+封闭世界假设表面上可能简化实现,但在一般的模型中是不必要的,因为保持问题合规性的论域应已由清晰的需求描述规范,不应为此阻碍实现变化的自由(@1.5.2.1) 。
275+换言之,开放世界的元素的全集是模型的结构化规则推断得到的,而非名义上的定义决定。这同时称为模型的语言的论域(universe of disclosure) 。
276+**注释**
277+开放世界包含的元素的外延及其语言的论域伴随随语言规则的修改而改变。
278+开放世界不限制论域中的某个子集是封闭的。例如,论域中可能存在某个子集的所有元素通过一定方式被枚举。
275279
276280 @1.5.4 结构和依赖原则:
277281
@@ -820,7 +824,7 @@
820824 通过特定的等价关系可定义具体的不可变状态(@1.2.1.2) 的集合。
821825 这些集合可用于定义以这些状态为值的实体的不可变性,进而定义不保持可变性的改变(mutation) 操作和具体的其中可能影响可观察行为(@4.1.3) 的修改(@4.1) 操作。
822826 通过限定不同的修改操作,定义不同的可修改性(modifiability) 和对立的不可修改性(nonmodifiability) 。
823-通过明确不可修改性拒绝支持修改操作(例如通过通过实体的类型检查(@4.7.3) 拒绝特定的修改操作),或通过不提供修改操作(例如关于 ISO C++ 的非类且非数组类型的纯右值,尽管要求非纯右值可被视为是一种类型检查),语义规则保证实体不被修改操作改变状态。
827+通过明确不可修改性拒绝支持修改操作(例如通过通过实体的类型检查(@4.7.4) 拒绝特定的修改操作),或通过不提供修改操作(例如关于 ISO C++ 的非类且非数组类型的纯右值,尽管要求非纯右值可被视为是一种类型检查),语义规则保证实体不被修改操作改变状态。
824828 (不依赖和影响实体同一性(@4.1) 的)同一个实体上的修改操作是改变操作。只有具有可变状态(@1.2.1.2) 的实体可能支持这些操作。
825829 一般地,一个实体不一定保证可区分是否具有不可变性以及具有何种不可变性(也蕴含一般不可区分可修改性),因为不可变性依赖实体的表示(@1.2.4) 进行约定。
826830 潜在引起实体的一些内部状态的变化的操作可不被视为影响不可变性而不被视为实体的(整体意义上的)改变操作。这种实体具有内部可变性(interior mutability) 。
@@ -904,7 +908,7 @@
904908 @4.2.1.3 Kernel 中的一等对象:
905909 尽管没有显式指出一等实体和一等对象的区别,在 [RnRK] 中的一等对象和此处的一等实体在目的上一致。因为 Kernel 不直接支持区分对象同一性,一等实体退化为一等对象。
906910 并不需要修改一等对象的判定准则(@4.1) 限定为后者并使前者依赖后者的定义,因为作为抽象,前者通常并非是后者的操作上进行限制得到(正相反,一般是通过补充约定假设得到,如 @4.1 的定义)。
907-类似的一个例子是不可修改对象(nonmodifiable object) 可以但不必要是对应的可修改对象(modifiable object) 的子类型(@4.7) 。
911+类似的一个例子是不可修改对象(nonmodifiable object) 可以但不必要是对应的可修改对象(modifiable object) 的子类型(@4.7.2) 。
908912 作为 @1.5.5.1 的实例,一等实体避免特殊规则,和 [RnRK] 设计原则 G1a 一致。
909913 除非另行指定,本节中的以下描述提供允许派生实现提供保证或假设的机制,并非要求。派生实现可附加规则改变此处对一等对象的保证或性质。
910914
@@ -1085,7 +1089,7 @@
10851089 这同样违反了简单性;
10861090 这使基于等价关系定义不同的修改(@4.1.4.2) 更困难且难以实现统一性(@1.5.5.1)(例如,需要更多的特设的基本修改操作(@4.1.4.2) ,或者如 SRFI-17 这样的为修改操作特设的上下文),乃至缺乏实际意义;
10871091 若支持 SRFI-111 的自动装箱(autoboxing) (实际包含自动拆箱(autounboxing) 和幂等(@4.1) 的拆箱操作),则进一步违反简单性,也更难以实现统一性。
1088-基于 [RnRK] 的封装类型(encapsulation type) 可实现装箱,但装箱实际上依赖的是共享的引用,并非封装类型自身的名义类型(@4.7.1) 特性,不符合 @1.5.2.4 。并且,在扩展针对个别对象的属性(如不可变性(@4.1.4.2) )时它仍存在一些不容易扩展的较复杂的问题,如:
1092+基于 [RnRK] 的封装类型(encapsulation type) 可实现装箱,但装箱实际上依赖的是共享的引用,并非封装类型自身的名义类型(@4.7.2) 特性,不符合 @1.5.2.4 。并且,在扩展针对个别对象的属性(如不可变性(@4.1.4.2) )时它仍存在一些不容易扩展的较复杂的问题,如:
10891093 https://groups.google.com/forum/?fromgroups#!topic/klisp/pLz-uqJ0WfE 。
10901094 使用 [RnRK] 的设计而在此之后引入一等引用(而不仅是一等被引用的对象)可能是对语言特性的实现的复杂的改动,但直接消除隐式的共享(@4.2.3.4) 而使用一等引用的设计并不需要这样的复杂性。
10911095 封装类型作为装箱以外仍有意义。封装类型仍可用于实现箱,但这只是实现细节,更一般的情形两者是无关的——这也符合 @1.5.2.3 和 @1.5.2.4 的要求。
@@ -1157,7 +1161,7 @@
11571161 NPL 不要求预设具体的实体及对象类型的设计,因此不要求用户使用语言体现整体上的可扩展性(@1.5.5.1) 。这允许由派生实现指定类型的外延而满足 @1.5.2.1 。
11581162 特别地,NPL 不要求表达式具有预设的不同类型(@4.7) 。
11591163 除不必涉及引用(@4.2.3) 外,[RnRK] 中定义的封装的(encapsulated) 类型的概念及类型封装性( [RnRK] 原则 G4 )仍然适用,且一般仍然需要满足;差异是派生实现因为扩展不满足的情形也不影响此实现的一致性(@2.4) (尽管使用扩展的程序可能不可移植)。
1160-表达式中的对象的类型和值类别(@4.2.2.1) 应分别讨论,因为两者正交:两者的确定(@4.7) 和检查(@4.7.3) 机制都相互独立。
1164+表达式中的对象的类型和值类别(@4.2.2.1) 应分别讨论,因为两者正交:两者的确定(@4.7) 和检查(@4.7.4) 机制都相互独立。
11611165
11621166 @4.2.7 例子:
11631167 存在一些判定准则不符合这些要求,如 [RnRK] 中引用的 [Gu91] :
@@ -1287,7 +1291,7 @@
12871291 表达式在得到规范形式后规约终止,且蕴含求值终止。
12881292 得到范式的规约步骤称为规范化(normalization) 。
12891293 若表达式规约总是能得到规范形式(求值总是能在有限规约步骤后终止),则具有强规范化(strong normalization) 性质。
1290-实现应避免引起无法保证强规范化性质的操作(如直接无条件的递归规约调用)。
1294+实现应避免引起对象语言的语义表达以外的无法保证强规范化性质的操作(如直接无条件的递归规约调用)。
12911295 除非派生实现另行指定,不保证强规范化性质。
12921296 保证得到范式的规约是规范化规约。
12931297 具体求值得到的范式若可作为表达式,其求值结果(@4.1) 和被求值的项等价,即仅允许恒等求值(@4.4.4) 而仍是范式;这样的项称为自求值项(self-evaluating term) 。
@@ -1298,7 +1302,7 @@
12981302 第一个子表达式(头表达式)是范式的表达式是 HNF(Head Normal Form ,头范式)。
12991303 头表达式是可直接求值为范式的表达式是 WHNF(Weak HNF,弱头范式)。
13001304 约定求值到 WHNF 提供保证强规范化性质的一般手段,可用于非严格求值(@4.4.6.5) 。
1301-WHNF 的头表达式是操作符(operator) ,存在对应 HNF 的头表达式的最终求值结果(final evaluation result) ,详见 @4.5.3.2 。
1305+WHNF 的头表达式是操作符(operator) ,存在对应 HNF 的头表达式的最终求值结果(final evaluation result) ,详见合并子(@4.5.3.2) 。
13021306 WHNF 中除了操作符以外的子表达式是操作数(operand) 。
13031307 操作数以具有限定顺序或不限定顺序的数据结构表示,典型代表分别是操作数列表(operand list) 和操作数树(operand tree) 。
13041308 操作数树是有限的树形数据结构的 DAG(@4.2.4) (和 Kernel 类似),其具体构造和表示由派生实现定义。
@@ -1311,10 +1315,10 @@
13111315 表达式和子表达式之间的求值满足以下递归蕴含规则:
13121316 求值依赖规则:除非另行指定,表达式被求值实质蕴含子表达式(@3.4.2) 被求值。
13131317 顺序依赖规则:子表达式值的计算先序(@4.4.3) 所在的表达式值的计算。
1314-平凡求值规则:指定一个表达式是平凡求值(@4.4.4) 实质蕴含其子表达式的求值被指定为平凡求值。
1318+平凡求值规则:指定一个表达式是纯求值或空求值(@4.4.4) 对应实质蕴含其子表达式的求值被指定为纯求值或空求值。
13151319 **注释**
1320+一般地,一些求值策略(@4.4.6.5) 可以不遵循求值依赖规则。一个具体的典型例外是 WHNF 可约定只求值部分子表达式实现这些求值策略;参见 vau 抽象(@4.5.2.3) 。
13161321 顺序依赖规则是因果性(@4.4.3) 的具体表现之一。对不被求值的表达式,此规则不生效。构造不同的表达式进行计算可实现和直接违反此规则等效的作用,但因为是不同的表达式,实际上不违反此规则。
1317-一般地,一些求值策略(@4.4.6.5) 可以不遵循求值依赖规则。一个具体的典型例外是 WHNF 可约定只求值部分子表达式实现这些求值策略;参见 vau 抽象(@4.5.2.3) 。
13181322 附加的顺序依赖规则可由特定的实体构成的表达式的求值隐含指定,如过程调用(@4.5.2.1) 可利用不同的求值策略(@4.4.6.5) 。
13191323
13201324 @4.4.6.2 严格性(strictness) :
@@ -1462,6 +1466,9 @@
14621466 典型实现的函数指称过程(@4.5.2.1) ,函数调用为过程调用(procedure call) 。
14631467 若一个函数的调用仍待返回,则该函数调用是活动的(active) 。
14641468 一般地,被调用的函数及函数调用的作用的等价性通常不能被确定。一个重要的子类是不能确定具体表示的合并子的情形(@4.4.5.1) ,参见 @4.5.3.2 。其它函数一般也有类似限制。
1469+调用总是不蕴含非纯求值(@4.4.4) 的函数是纯函数(pure function) 。
1470+**注释**
1471+另见函数调用的终止保证(@4.8.2) 。
14651472
14661473 @4.5.3.2 合并子:
14671474 除非另行指定,NPL 假定函数合并满足以下典型情形,即函数合并的操作符(@4.4.5.1) 求值为以下类型的合并子(combiner) 之一:
@@ -1520,11 +1527,13 @@
15201527 @4.6.1 求值环境:
15211528 求值环境(evaluation environment) 是在求值(@4.1) 时可访问的隐式上下文,提供可通过名称解析(@4.3.3) 访问的变量的绑定(@4.1) 。
15221529 能被通过名称解析成功访问变量的绑定是可见的(visible) 。
1523-同 [RnRK] ,环境包含(contain) 局部绑定(local binding) ,即不通过环境以外的对象即保证可见的被绑定实体(@5.7.1) 。
1530+环境包含(contain) 局部绑定(local binding) ,即不通过其它环境即保证可见的被绑定实体(@5.7.1) 。
15241531 按绑定的定义,求值环境的局部绑定集合即变量的名称和通过声明引入的被变量表示的实体构成的映射。
1525-同 [RnRK] ,环境展示(exhibit) 可见的绑定。
1532+环境展示(exhibit) 可见的绑定。
15261533 不和实现环境(@1.2.3) 相混淆的情况下,求值环境简称(变量或对应的本地绑定所在的)为环境(environment) 。
15271534 一个环境是空环境(empty environment) ,当且仅当其中包含的局部绑定集合是空集。
1535+**注释**
1536+包含和展示的定义同 [RnRK] 。
15281537
15291538 @4.6.1.1 实现环境提供的求值环境:
15301539 实现环境可能在实现以外提供附加的求值环境(@4.6.1) 作为任务通信的机制,如环境变量。
@@ -1543,23 +1552,44 @@
15431552 典型的实例为由 ISA 约定的通用架构寄存器的状态,可能需要在函数调用(@4.5.3.1) 或任务切换过程中保存和重置。
15441553 除非派生实现另行指定,语言不提供访问互操作上下文的公开接口。
15451554
1546-@4.7 类型(type) :
1547-上下文中和特定的实体直接关联或间接关联的元素,满足某个执行阶段的不变量约束(@2.4.3) 。
1555+@4.7 类型:
1556+类型(type) 是上下文中和特定的实体直接关联或间接关联的元素,满足某个执行阶段的不变量约束(@2.4.3) 。
1557+实体关联的类型可能被显式地指定,或通过隐式的限定规则推断确定。符合指定和限定要求的类型可有任意多个。
1558+实体的类型是被显式指定的实体关联的类型。实体具有实体的类型以及通过其它规则限定的类型。实体是类型的实例(instance) 。
1559+类型可用集合表示(@4.1.1) 。集合的元素是具有其表示的类型的实体。
15481560 和表达式直接关联的类型满足起始阶段不变量约束,称为静态类型(static type) 。
15491561 和表达式的值(@4.1) 关联的类型满足运行阶段(@2.4.1) 的不变量约束,称为动态类型(dynamic type) 。
15501562 其它可能存在类型或实现执行阶段的扩展由派生实现定义。
15511563 除非另行指定,对象的类型是对象的值(@4.1) 的类型。
15521564 NPL 对象类型和存储的值(@4.1.1) 的类型之间的关联未指定。
1565+
1566+@4.7.1 类型系统和类型机制:
15531567 称为类型的具体实体和之间的关联由派生实现的类型系统(type system) 规则指定。
15541568 默认类型系统不附加约束,所有表达式或关联的项都没有指定类型(untyped) ,为退化的平凡类型系统(trivial type system) 或单一类型系统(unityped system) ,实质上是动态类型。
15551569 对类型系统的分类中,类型也指确定类型的过程称为类型机制(typing discipline) ,其中确定类型的过程称为定型(typing) 。
1556-被定型的类型的实体可完全地满足其它类型的约束。前者具有后者的子类型(subtype) 。
1557-
1558-@4.7.1 名义类型(nominal typing) 和结构化类型(structrual typing) :
1559-通过显式指定标识(如名称)的方式定义类型的方法是名义类型,否则是结构化类型。
1570+在静态类型之后阶段确定的类型机制是动态定型(dynamic typing) 。
1571+除非另行指定,静态类型的阶段是翻译时阶段;动态类型的阶段是翻译时之后,即运行时(@2.4.1) 。
1572+语言可提供定型规则,指定项(@4.1) 作为实体在特定的上下文(@4.6)(称为类型环境(typing environment) )中的类型。项是类型在这个上下文中的居留(inhabitant) 。
1573+不违反类型系统规则下的良定义(well-defined) 的程序构造是良型的(well-typed) 。
1574+**注释**
1575+实体的类型可被指定为未指定类型,以明确类型的存在性,但不明确具体的类型的构造和表示。
1576+在数理逻辑中,使用结构主义数学方法,集合可以作为描述类型规则的理论(句子集合)的模型,和理论支持描述的类型一一对应。
1577+
1578+@4.7.2 类型等价性:
1579+通过显式指定标识(如名称)的方式定义类型的方法是名义类型(nominal typing) ,否则是结构化类型(structrual typing) 。
15601580 名义类型之间没有隐含的等价关系。结构化类型之间的等价关系由实现定义。
1561-
1562-@4.7.2 类型标注(type annotation) :
1581+类型的相等关系是一种类型之间的等价关系。两个类型相等,当且仅当它们的实例作为元素的两个集合对应相等。
1582+除非另行指定,相等的类型不在语言中区分,且元语言(描述对象语言的规则)中类型作为实体的同一性(@4.1) 即类型相等性。
1583+推论:除非另行指定,不同的类型不等价。
1584+除非另行指定,对象语言使用类型相等性实现类型等价性。
1585+**原理**
1586+对象语言中的类型实质上是类型的一种间接的表示,作为实体仍然可以具有不同的同一性。
1587+这避免程序可能需要枚举类型的外延(即精确实现出表示它的集合)才能确保确切表示出这个类型这样的计算上不可行的困难。
1588+因为可支持的表示的类型全集(@4.7.5) 不同,类型相等是相对的,依赖类型系统的具体实现。一个类型系统可能支持无法在另一个类型系统中精确表示的类型。
1589+**注释**
1590+本节的主要例外参见公共子类型(@4.7.7) 。
1591+
1592+@4.7.3 类型标注(type annotation) :
15631593 根据是否需要特定的文法元素指定和项关联的类型即类型标注,对确定类型的机制可进行分类。
15641594 类型系统可使用显式类型(explicit typing) ,即要求类型标注。
15651595 不使用类型标注确定的类型是隐式类型(implicit typing) 。
@@ -1567,31 +1597,48 @@
15671597 不使用清单类型而使隐式引入的实体(如值(@4.1) )关联具体类型的机制称为潜在类型(latent typing) 。
15681598 清单类型是显式类型的实例;除此之外,显式类型还包括铸型(casting) ,即显式指定表达式求值的结果应具有的类型。
15691599 潜在类型是隐式类型的实例;除此之外,隐式类型还包括类型推断(type interferece) ,即通过隐含的上下文信息判断表达式关联的类型。
1570-若类型机制可保证在某个执行阶段(@2.4.1) 内有确定强规范性质(@4.4.5) (即保证终止)的算法确定类型,则类型机制在该阶段是静态类型(static typing) 。
1571-在静态类型之后阶段确定的类型机制是动态类型(dynamic typing) 。
1572-除非另行指定,静态类型的阶段是翻译时阶段;动态类型的阶段是翻译时之后,即运行时(@2.4.1) 。
1600+若类型机制可保证在某个执行阶段(@2.4.1) 内有确定强规范性质(@4.4.5)(即保证终止)的算法确定类型,则类型机制在该阶段是静态类型(static typing) 。
15731601 语言可能个别指定引入这些类型相关的规则,在保持逻辑相容的前提下可混合使用。
1574-和 [RnRK] 类似,NPL 不要求使用清单类型,以避免过于积极地(非预期地)排除危险但对程序有用的使用,而违反 @1.5.5.2.1 。
1575-同时,避免具体的清单类型允许派生语言定义其它不容易冲突的类型标注规则,而使语言具有更好的可扩展性(@1.5.2.1) 。
1576-派生语言仍可限定使用清单类型。
1577-
1578-@4.7.3 类型检查(typechecking) :
1602+**原理**
1603+历史上,表达式的类型和变量的类型在简单类型 λ 演算(@4.5.2) 中同时被引入。后者修饰 λ 抽象中的自由变量(@4.1) ,而前者限定剩余的所有项。
1604+即便从项重写系统(@4.1) 中两者是形式上统一的,在实际语用中具有很不同的差异。这集中体现在后者是名义的(@4.7.2) ,除非附加其它不同的语法设施,并不具有结构化推导的性质,原则上只适合描述接口;而前者能兼容化结构类型(@4.7.2) ,同时适合描述接口及其实现。
1605+作为接口的名义类型在作为自由变量以外的上下文中重新复用为不关心其类型(并消除依赖这些信息的其它机制(@4.7.4) )的其它程序构造(一般意义上的表达式),通常需要类型擦除等更复杂的机制和支持的类型系统规则,以消去不再预期和其它类型系统规则交互的类型。
1606+和 [RnRK] 类似,NPL 不要求使用清单类型,以避免一些一般意义上的全局设计缺陷。这些缺陷包括:
1607+ 过于积极地(非预期地)排除危险但对程序有用的使用,而违反易预测性(@1.5.5.2.1) 。
1608+ 因为移除类型标注需要上述的复杂机制和类型系统规则,具体的清单类型阻碍派生语言定义其它不容易冲突的类型标注规则而使语言具有更好的可扩展性(@1.5.2.1) 。
1609+ 因为名义类型的相关规则更容易直接拒绝一些和类型规则不兼容的程序构造而难以简单地变通,往往对程序构造的组合具有更多直接的表达性限制(@1.5.3.1.1) 而破坏通用计算意义上的正确性(@1.5.3.1) 。
1610+ 例如,许多类型系统不允许表达 Y 组合子(@4.1) 的构造良型(@4.7.1) 。
1611+若有必要,派生语言仍可限定使用清单类型。一般仍然建议仅在局部引入而避免全局复杂性和因此带来的限制。
1612+
1613+@4.7.4 类型检查(typechecking) :
15791614 类型检查解答程序是否满足类型规则的判定性问题。
15801615 使用语义分析或运行时(@2.4.1) 的类型检查分别为静态类型检查和动态类型检查。
15811616 静态类型检查规则是可诊断语义规则(@2.5.1) 。
15821617 语言可能个别指定引入类型检查相关的规则,在保持逻辑相容的前提下可混合使用。
1583-注意静态类型检查和静态类型(@4.7.2) 以及动态类型检查和动态类型(@4.7.2) 的区别。类型检查和类型机制是不同的规则,不必然包含蕴含关系。
1618+注意静态类型检查和静态类型(@4.7.3) 以及动态类型检查和动态类型(@4.7.3) 的区别。类型检查和类型机制是不同的规则,不必然包含蕴含关系。
15841619 类型检查失败引起的错误(@2.5.2) 称为类型错误(type error) 。
15851620
1586-@4.7.4 类型全集(type universe) :
1587-类型全集是语言规则中允许表达的类型的总称。
1588-NPL 避免限定类型全集以更好地符合 @1.5.3.6 。
1621+@4.7.5 类型全集:
1622+作为论域(@1.5.3.6) 的实例,类型全集(type universe) 是语言规则中允许表达的类型的总称。
1623+**注释** 表达类型的规则构成的模型的语言是语言规则的子集。
1624+NPL 避免限定类型全集以符合开放世界假设(@1.5.3.6) 。派生语言可指定不同的规则。
15891625 除非派生实现另行指定,程序的用户不能依赖语言规则的限定枚举类型全集中的所有类型。
15901626
1591-@4.7.5 类型谓词(type predicate) :
1627+@4.7.6 类型谓词(type predicate) :
15921628 判断值是否满足类型居留(inhabitant) 的谓词是类型谓词。
15931629 和 [RnRK] 的基本类型谓词不同,类型谓词定义为只接受一个参数。
15941630
1631+@4.7.7 子类型:
1632+被定型(@4.7.1) 的类型的实体可完全地满足其它类型的约束。前者具有后者的子类型(subtype) 。
1633+子类型关系是一种预序(preorder) 关系,即自反的、反对称的二元关系。
1634+相等的类型符合子类型关系,是平凡的(trivial) 。排除平凡的子类型关系是严格子类型关系。
1635+严格子类型是严格预序关系,即反自反、反对称的二元关系。
1636+子类型和严格子类型对应的逆关系是超类型(super type) 和严格超类型(strict supertype) 关系。
1637+多个类型可具有公共的(严格)超类型。这些类型同为一个类型的子类型而等价(@4.7.2) 。
1638+除非另行指定,在程序的行为不依赖其中特定的个别不相等的类型而具有差异时,具有相等超类型的等价的子类型视为相同的类型。
1639+**原理**
1640+相同类型涵盖不相等的子类型允许使不同的子类型实现公共严格超类型公开的接口行为,而有效地实现封装性(@1.5.6.2) 。
1641+
15951642 @4.8 程序的控制执行条件(execution condition) :
15961643 和 @4.5.2.1 类似,程序的控制状态(@4.1) 决定求值使用的续延。
15971644 更一般地,规约规则指定语言的实现决定程序行为时使用的(对程序不保证可见的)续延,这种在实现中对应的控制状态称为控制执行条件。
@@ -1599,7 +1646,7 @@
15991646 除非另行指定,仅由默认规约规则决定的执行条件是正常(normal) 的。
16001647 一个主要实例:以当前续延返回的合并子调用返回(@4.5.3.3) 是正常执行的。
16011648 改变程序的正常的控制要求存在控制作用(@4.1) ,此时,控制执行条件是非正常(abnormal) 的。
1602-一个主要实例:除非另行指定,隐含在默认规约规则确定的函数应用外的续延调用(@4.5.3.3) 是非正常的。
1649+一个主要实例:除非另行指定,隐含在默认规约规则确定的函数应用(@4.5.3) 外的续延调用(@4.5.3.3) 是非正常的。
16031650 具有规约语义的语言总是支持正常控制条件。NPL 中,非正常的控制条件的支持是可选的。
16041651
16051652 @4.8.1 异常(exception) :
@@ -1689,17 +1736,17 @@
16891736 名称解析失败(@4.3.3) 可被忽略而不终止(@4.4.5) 实现演绎;
16901737 保证名称表达式求值的强规范化(@4.4.5)。
16911738 不要求提供命名空间(@4.3.4) 实现的可变实体。
1692-不保证求值都是纯求值;非特殊形式使用热情求值;其它情形使用热情求值或惰性求值(@2.5.2) 由具体特殊形式约定。
1739+不保证求值都是纯求值(@4.4.4) ;非特殊形式使用热情求值;其它情形使用热情求值或惰性求值(@2.5.2) 由具体特殊形式约定。
16931740 对象语言的函数(@4.5.2) 默认为过程(@4.5.2.1) 。过程默认实现为子例程(@4.5.2.1) 。过程指定的计算结果和函数表达式最终返回结果的关联(@4.5.2.1) 是过程调用(@4.5.3.1) 结果的一一映射。
16941741 除非另行指定,实现函数(@4.5.2) 的宿主数据结构生存期要求默认同宿主语言;循环引用(@4.2.4) 可能行为未定义(@5.4)(另见内存泄漏(@5.6.4) )。
16951742 除非另行指定,按值传递(@4.4.6.5) 支持复制初始化(@5.8.2) 对象的一等作用(@4.2.2.2) 。这和宿主语言的对应语义也保持兼容。
16961743 派生实现使用的项不满足特定的表示(@6.10.11) 。
16971744
16981745 @5.2.6 类型系统:
1699-NPLA 默认使用隐式类型而非显式类型(@4.7.2) 。
1700-NPLA 默认使用潜在类型(@4.7.2) :值(@4.1) 具有类型;不指定动态类型以外的类型。
1701-显式类型(如清单类型)(@4.7.3) 的机制可由派生实现指定可选地引入。除非派生实现另行指定,引入的静态类型应同动态类型。
1702-NPLA 使用和宿主语言相容的动态类型检查(@4.7.3) 。除非派生实现另行指定及类型映射(@5.5) 的需要,使用的类型检查规则和宿主语言一致。
1746+NPLA 默认使用隐式类型而非显式类型(@4.7.3) 。
1747+NPLA 默认使用潜在类型(@4.7.3) :值(@4.1) 具有类型;不指定动态类型以外的类型。
1748+显式类型(如清单类型)(@4.7.4) 的机制可由派生实现指定可选地引入。除非派生实现另行指定,引入的静态类型应同动态类型。
1749+NPLA 使用和宿主语言相容的动态类型检查(@4.7.4) 。除非派生实现另行指定及类型映射(@5.5) 的需要,使用的类型检查规则和宿主语言一致。
17031750 宿主语言对象的值描述状态且宿主语言要求的对 volatile 左值的操作也属于可观察行为(@4.1.3) 。
17041751
17051752 @5.3 互操作(@1.2.3) 支持:
@@ -1747,8 +1794,8 @@
17471794 对象语言的值(@4.1) 被对象语言的实体类型表示蕴含它被映射的宿主类型表示,反之亦然。
17481795 类型映射可以是非空的多对一、一对多或一一映射。
17491796 若类型映射是一一映射,其类型等价性同宿主语言的语义规则;否则,由类型的语义规则约定。
1750-类型系统(@4.7) 是开放(@1.5.3.6) 的,可能提供不被对象语言支持的宿主语言类型和值,如中间值(@6.8) 。
1751-但符合已指定的类型的实体需能被视为同种类型的实体使用,即子类型(@4.7) 。
1797+类型系统(@4.7.1) 是开放(@1.5.3.6) 的,可能提供不被对象语言支持的宿主语言类型和值,如中间值(@6.8) 。
1798+但符合已指定的类型的实体需能被视为同种类型的实体使用,即子类型(@4.7.2) 。
17521799 因需提供与作为宿主语言的 C++ 的互操作(@5.3) 支持,所以明确约定实现中部分实体类型对应的 C++ 类型:
17531800 用于条件判断的单一值的宿主类型是 bool 。
17541801 字符串(@3.2) 及和字符串的子集一一对应的词素(@3.3.1) 的宿主类型都是 string 类型(@6.1.5) 。
@@ -1899,25 +1946,31 @@
18991946 被求值的表达式使用被规约项(@6.2) 作为内部表示。
19001947
19011948 @5.8.1 值类别(@4.2.2.1) :
1902-和 ISO C++17(由提案 [WG21 P0135R1] 引入的特性)类似,表达式归类为具有以下值类别之一:
1949+表达式归类为具有以下值类别之一:
19031950 泛左值(glvalue) :求值用于决定被表示的对象的同一性(@4.1) 的表达式;
19041951 纯右值(prvalue) :求值不用于决定对象同一性(而仅用于初始化(@5.8.2) 临时对象(@5.8.5) 或计算对象中存储的值(@4.1.1) )的表达式。
19051952 一个泛左值可能被标记为消亡值(xvalue) 或附加其它元数据(如通过间接的引用(@4.2.3.3.1) ),以提供基于不同的所有权的行为。
1953+**注释** 这和 ISO C++17(由提案 [WG21 P0135R1] 引入的特性)类似。
19061954 纯右值蕴含对象在可观察行为(@4.1.3) 的意义上不被共享,类似不被别名的引用的被引用对象(@4.2.3) 不被共享(@4.2.3.4.3) 。
19071955 左值(lvalue) 是除了消亡值外的泛左值。
19081956 右值(rvalue) 是消亡值或纯右值。
19091957 求值涉及表达式的值类别仅在必要时约定。
19101958 值类别根据是否只关心表达式关联的(对象的或非对象的)值,在需要对象时提供区分两类一等对象(@4.2) 的机制,同时避免在仅需要表达式关联的值时引入不必要的其它对象。
1911-和 ISO C++ 类似,表达式的值类别是上下文相关的,相同表达式构造在不同的上下文可能具有不同的值类别。
1912-和 ISO C++ 不同,NPLA 表达式允许在源语言语法之外的形式被间接构造,这些表达式同样具有值类别。
1959+表达式的值类别是上下文相关的,相同表达式构造在不同的上下文可能具有不同的值类别。
1960+**注释** 这和 ISO C++ 类型。
1961+NPLA 表达式允许在源语言语法之外的形式被间接构造,这些表达式同样具有值类别。
1962+**注释** 这和 ISO C++ 不同。
19131963 求值规约(@4.4.1) 可能重写一个表达式为具有不同值类别的其它形式的表达式。
1914-和 ISO C++ 不同,一般地,NPLA 的表达式不限定从源代码(@4.1.1) 的翻译(@2.4.1) 确定,且一个表达式的求值结果不排除继续构成表达式而被求值,因此表达式的值也普遍具有值类别。
1964+一般地,NPLA 的表达式不限定从源代码(@4.1.1) 的翻译(@2.4.1) 确定,且一个表达式的求值结果不排除继续构成表达式而被求值,因此表达式的值也普遍具有值类别。
1965+**注释** 这和 ISO C++ 不同。
19151966 除非另行指定,若一个 NPLA 表达式没有指定未被求值,则其值类别是其求值结果(@4.1) 的值类别。
19161967 另见引用(@4.2.3) 对一等状态(@4.2.2.1) 的支持(@4.2.3.3.2) 。
19171968
19181969 @5.8.1.1 类型系统和表示:
1919-值类别在 NPLA 中被作为一种内建的类型系统,参见 @4.2.6 。
1970+NPLA 中,值类别作为实体类型(@4.2.6) ,被作为一种内建的类型系统。
19201971 内部表示允许存在附加的相关性(@6.3.1) 。
1972+**注释**
1973+这和 ISO C++ 不同。ISO C++ 的“类型”的定义排除值类别,尽管值类别具有类型论意义上所有可作为类型讨论的对象的性质。
19211974
19221975 @5.8.1.2 设计原理:
19231976 按已有的规则,NPLA 约定求值至少需要支持以下的重要的性质:
@@ -1930,7 +1983,8 @@
19301983 第三,求值满足递归蕴含规则(@4.4.6.1) ;
19311984 第四,允许对象的操作保持资源局域性,满足一等对象不依赖引用的抽象(@4.2.3) 同时允许按需区分是否依赖引用的两类一等对象;
19321985 第五,避免需要假设存在外部具有被引用对象的所有权的所有者(@5.6.4) 。
1933-第三条性质保证表达式的作用(@1.2.4) 是可组合的并允许求值表达为树规约(@6.2) ,还保证能总是通过子表达式的值类别决定表达式的值类别。因为被求值的表达式是有限的,判定过程是总是能终止,即便求值不满足强规范化性质(@4.4.5) 。
1986+第三条性质保证求值表达式的作用(@1.2.4) 是可组合的,并允许求值表达为树规约(@6.2) ,还保证能总是通过子表达式的特定的性质判定表达式的对应性质而不需求值。
1987+因为被求值的表达式是有限的,判定过程是总是能终止,即便求值不满足强规范化性质(@4.4.5) 。
19341988 第四条性质要求提供泛左值总是能作为纯右值使用的机制和通过纯右值引入对象的机制,详见值类别转换(@5.8.4) 。
19351989 第五条性质要求在表达式之外不存在地位相同的对象的存储资源(@5.6) 的所有者,限定了被决定同一性的对象的外延;存储由环境提供(@5.6) ,其中不需要保存对象的引用。
19361990 不像 ISO C++ 一样仅限于静态类型系统范畴,NPLA 值类别的作用机制和 ISO C++ 存在一些明确的不同:
@@ -1989,14 +2043,14 @@
19892043 **注释** 这避免在可能访问引用值的操作随意引入具有潜在未定义行为风险。
19902044 访问无效的引用值不满足内存安全(@5.6.3) 而引起未定义行为。
19912045 **注释**
1992-更一般的其它的实体有效性参见间接值(@6.4.4) 的有效性。
2046+更一般的其它的实体有效性参见间接值的有效性(@6.4.4) 。
19932047
19942048 @5.8.3.2 多重引用:
19952049 和宿主语言不同,被引用对象也可以是引用值。
19962050 被引用对象不是引用值的引用值是完全折叠(fully collapsed) 的。
19972051 除非另行指定,未折叠的引用值指未完全折叠的引用值。
19982052 **注释**
1999-引用值是被引用对象的间接值(@6.4.4) 。
2053+引用值是被引用对象的间接值(@6.4) 。
20002054
20012055 @5.8.3.3 引用值的属性(property) :
20022056 引用值可以具有属性,保存其作为一等对象的状态。
@@ -2019,7 +2073,7 @@
20192073 和 ISO C++ 核心语言(但不是 [res.on.arguments] 中的标准库绑定到右值引用实际参数的约定)的右值引用类似,唯一引用不总是表示被引用对象不被共享。
20202074 接受唯一引用的操作可能只假定被引用对象的子对象(@5.6.6) 不被共享,也可能完全不进行假定,这依赖具体操作的语义。若需要和具体操作无关的无条件非共享假定,使用纯右值(@5.8.1) 而非作为左值的唯一引用。
20212075 和宿主语言的 const 限定类型类似,不可修改引用仅针对特定左值的访问;通过共享的其它未被限定的引用仍可修改对象。
2022-违反不可修改引用引入的假定的错误可能通过类型检查(@4.7.3) 或其它方式引起。
2076+违反不可修改引用引入的假定的错误可能通过类型检查(@4.7.4) 或其它方式引起。
20232077 临时对象引用类似 ISO C++ 的转发引用(forwarding reference) 中保留在表达式声明中的类型信息。
20242078 因为 NPLA 不支持声明元数据,这些信息保存在对象的表示中,且在初始化时被引用值保存;也因此 NPLA 的这可跟随一等对象传递(@6.2.2.2) 。
20252079 这也和宿主语言不同。在宿主语言中:
@@ -2036,7 +2090,7 @@
20362090 但是,消除引用不一定总是预期这种性质,特别当折叠不被预期时。
20372091 例如,ISO C++ 内建指针的不同级 const 不会被隐式转换直接折叠合并。消除间接的指针值不是隐式的(而依赖内建一元 * 操作符),这是因为指针作为类型构造器自身的类型安全需要;是否消除 const 限定符仍然需要基于其它理由考虑。
20382092 而当被引用对象实现子对象时,修饰被指向的类型的 const 不会自动传播到子对象的类型中,此时可有 std::experimental::propagate_const 可选引入这种性质。
2039-对具有非间接访问的子对象的类型,这相当于 ISO C++ 的 mutable 修饰符,可实现内部可变性(@4.1.4.2) 。而允许子对象以外直接不传播不可变性,是一种结构性的平凡的扩展:这允许把被引用对象直接视为一种子对象的实现,而非要求引入新的名义类型(@4.7.1) 。
2093+对具有非间接访问的子对象的类型,这相当于 ISO C++ 的 mutable 修饰符,可实现内部可变性(@4.1.4.2) 。而允许子对象以外直接不传播不可变性,是一种结构性的平凡的扩展:这允许把被引用对象直接视为一种子对象的实现,而非要求引入新的名义类型(@4.7.2) 。
20402094 在 NPLA 这样没有要求显式类型编码是否可变的语言中,首先要求总是具有不可修改的传播性质会显著增加规则形式上的复杂性。若具体操作需要传播不可修改性,仍可进一步约定。
20412095 不涉及引用值的消除时,NPLA 对象的子对象的不可修改性一般也直接依赖具体元素(如不可修改引用作为列表元素)。除不可修改引用蕴含的对直接引用的被引用对象不可修改外,是否满足需要传播不可修改性依赖具体操作的语义。
20422096 **注释**
@@ -2144,11 +2198,11 @@
21442198 这和初始化非引用值类似,但实现需区分是否初始化的是延长生存期的临时对象,以确保之后能区分是否按引用绑定。
21452199
21462200 @5.8.6 表达式的类型:
2147-NPLA 的类型系统(@4.7) 使用隐式类型(@4.7.2) ;和 Scheme 及 Kernel 类似,默认使用潜在类型,保证表达式的值具有类型(@5.2) 。
2201+NPLA 的类型系统(@4.7.1) 使用隐式类型(@4.7.3) ;和 Scheme 及 Kernel 类似,默认使用潜在类型,保证表达式的值具有类型(@5.2) 。
21482202 NPLA 表达式的类型是表达式求值结果(@4.1) 的类型。
21492203 空求值(@4.4.4) 的求值结果要求未求值的合式的(@2.5.1) 表达式应具有和语法分析(@6.1.6) 的输出兼容的类型,参见 @6.2.1 和 @6.8.1 。
21502204 注意表达式的类型和 [R7RS] 的 expression type 无关,后者是语法形式(@3.4.3) 的约定(在 [R5RS] 和 [R7RS] 中称为 form );因为存在合并子(@4.5.3.2) 作为一等对象(@4.1) 的类型,不需要这种约定。
2151-实现对特定的上下文的表达式可使用类型推断(@4.7.2) 。由此确定的类型类似宿主语言的表达式的类型。
2205+实现对特定的上下文的表达式可使用类型推断(@4.7.3) 。由此确定的类型类似宿主语言的表达式的类型。
21522206 和 Scheme 和 Kernel 不同而类似宿主语言,表达式具有值类别(@5.8.1) 。
21532207 值类别的指派是类型系统的一部分(@5.8.1.1) 。但除非另行指定,类似宿主语言,值类别和表达式的类型正交,以简化操作涉及类型的指定。
21542208 和宿主语言不同,语言机制不限制值类别和其它类型的判断操作和结果(@4.1) 被作为一等对象(@4.2.5) ,尽管不一定直接提供。
@@ -2382,9 +2436,9 @@
23822436 TermNode 中保存子节点的容器。TermNode 可作为规约的输入,即被规约项;此时,TermNode 的子节点对象是子项(@4.1) 的表示(@1.2.4) 。
23832437 TermNode 的 Value 数据成员称为值数据成员(value data member) ,是 ValueObject 类型的对象。用于表示对象语言( NPLA 实现)中表达式或对象储存的值,或者包装的中间值(@4.4.6.2) 。
23842438 ValueObject 保存不同动态类型的目标对象(target object) ,可在对象语言程序运行时确定宿主语言对应的对象类型,结合类型映射(@5.5) 实现潜在类型(@5.8.6) 。
2385-**注释** ValueObject::GetObject 是无检查访问目标对象的基本接口。其它通常使用类型擦除(type erasure) 实现的类型,如 std::function 的实例,也有类似的目标对象的概念。
2439+**注释** ValueObject::GetObject 是无检查访问目标对象的基本接口。其它通常使用类型擦除(@4.7.3) 实现的类型,如 std::function 的实例,也有类似的目标对象的概念。
23862440 TermNode 对子节点和值数据成员具有直接的独占所有权。
2387-作为子节点的容器,TermNode 子节点的迭代器、指针和引用保持稳定,即移除子节点时不无效化(invalidate) 其它项的迭代器、指针和引用。
2441+作为子节点的容器,TermNode 子节点的迭代器、指针和引用保持稳定,即移除子节点时保持其它项的迭代器、指针和引用有效而不被无效化(invalidated) 使访问引起宿主语言的未定义行为(@5.4) 。
23882442 TermNode 子节点的稳定性也支持项被转移时(如维护临时对象的内部存储(@5.6.2) )不需要附加操作维护已在其它位置被引用的子项有效性。
23892443 针对 TermNode 的一些操作如移除第一个子节点的 RemoveHead 一般使用 ADL 方式调用。
23902444 树规约可按需添加或删除 TermNode 的子节点。具体添加或删除的时机未指定,取决于具体的规约算法。
@@ -2498,14 +2552,23 @@
24982552 作为 NPL 语义(@4) 的扩展,作用于非表达式项上的规约规则不是求值规则。
24992553
25002554 @6.3.1 值类别:
2501-表达式的值类别(@5.8.1) 也在适用表达式项(@6.3) 及在表达式求值的规约中得到的项。
2555+表达式项(@6.3) 及在表达式求值的规约中得到的项具有表达式的值类别(@5.8.1) 。
25022556 作为名称表达式(@4.5.1) 明确引用直接存储在环境中的对象及其包含的(数据成员)子对象(@5.6.6) 的项都是左值。
25032557 右值一般存储在环境以外的项上;必要时,实现可能存储右值在特定的环境中。
25042558 在类型的值的内部表示上,值类别和特定的类型相关:
2505-当前 NPLA 支持的左值都是项引用(@6.8.3) ,这同时表示引用值(@6.3.4) 。
2559+NPLA 支持的左值都是项引用(@6.8.3) ,这同时表示引用值(@6.3.4) 。
25062560 NPLA 一等对象的值的类型和它作为表达式项的值类别存在以下一一对应关系:
25072561 若类型是引用,则对应的值类别是泛左值;否则,对应的值类别是右值。
25082562 引用值的不同类型指定更精确的具体对应关系(@6.3.4) 。
2563+**原理**
2564+使用类型是否具有特定的引用值确定表达式的值类别简化许多设计,至少包括:
2565+避免要求针对表达式是否具有引用类型时添加转发到非引用类型上的特殊规则,使引用类型实质上不能作为一等的类型;
2566+避免作为类型系统的值类别和其它的类型的共存的概念定义和一般理论的不一致(@5.8.1.1) 。
2567+早期 [WG21 N0130] 曾提案合并 C++ 的左值性(值类别的前身)和表达式的引用类型,但没有被 ISO C++ 接受。
2568+ISO C++ 至今仍不把引用类型作为一等的类型,在一般表达式的类型上调整(adjust) 非引用类型,除非特定的上下文要求引用类型。
2569+从 ISO C 上添加引用类型的历史过程看,这个设计是自然的,并且通过排除上下文添加例外的方式和 ISO C 的左值转换(lvalue conversion) 的方式一致。但类型系统上的冗余问题一直没有解决,即便 ISO C++ 现在完全独立 ISO C 描述这些规则。
2570+作为不需要适应其它不完善的历史设计的语言,不应采取这种设计。
2571+另一方面,表达式和变量的类型仍然被区分,后者原则上仅对清单类型有意义(@4.7.3) 。NPLA 和 NPLA1 不要求在语言的全局设计中引入变量类型。
25092572
25102573 @6.3.1.1 值的标签:
25112574 因为 NPLA 使用潜在类型(@5.2.6) ,表达式不保证提供静态类型(@4.7) 能编码值类别和类型信息;作为替代,这些信息通过规约范式中的项的标签保存。其中:
@@ -2750,7 +2813,7 @@
27502813 NPL::NPLException 是 NPL 实现的异常基类。
27512814 其它 NPL 异常都从此类直接或间接派生。
27522815 这些异常类在 NPLA 模块中以 Doxygen 命令标识为 \ingroup exceptions 。
2753-一般地,使用确切的异常类型以明确更具体的错误条件,如列表类型(@6.2.1) 错误代替一般的类型错误(@4.7.3) 。
2816+一般地,使用确切的异常类型以明确更具体的错误条件,如列表类型(@6.2.1) 错误代替一般的类型错误(@4.7.4) 。
27542817 除非规约节点操作(@6.15.3.1) 外,NPLA 实现可能抛出这些异常或派生实现定义的派生这些类的其它异常。
27552818 NPLA 实现可能抛出标准库异常。
27562819 一般地,不通过对象语言构造(而仅通过互操作(@5.3) 或实现缺陷)引起的异常(如检查宿主值(@6.3) 的值域(@6.3) ),不使用 NPL 异常基类。
@@ -2820,7 +2883,7 @@
28202883 Environment::EnsureValid(@6.8.1) ,失败时抛出异常;
28212884 shared_ptr<Environment> 值作为当前环境(@5.7.3) 的来源,同时提供指定为断言和具有异常的接口,参见上下文类(@6.11.3.1) ;
28222885 由 NPL_NPLA_CheckParentEnvironment(@6.1.2.1) 配置的可选的附加检查,失败时同 Environment::EnsureValid 。
2823-对象语言的动态类型的值需要进行环境类型的类型检查(@4.7.3) 时,只区分对象语言中的类型,不特别排除具有宿主类型的非宿主值,因此不断言失败;
2886+对象语言的动态类型的值需要进行环境类型的类型检查(@4.7.4) 时,只区分对象语言中的类型,不特别排除具有宿主类型的非宿主值,因此不断言失败;
28242887 其余情形可能静态确定不存在来自可能通过互操作(@5.3) 来源取得的非空值,此时一般使用断言而不是异常。
28252888
28262889 @6.8.2.2 循环环境引用和环境引用所有权:
@@ -3175,8 +3238,8 @@
31753238 一次规约后,被规约项中的值数据成员或子项仍然可保留其它状态而非范式;对表示求值的情况,也不是正规表示(@6.10.7) 。
31763239 因为规范化规约可能存在副作用(@6.10.3.2),NPLA 约定求值得到正规表示的规范化规约在抽象机(@2.6) 的意义上总是被进行,称为正规化操作。
31773240 纯值规约(@6.10.1) 的正规化操作包含以下操作:
3178-对子项进行清理(@6.10.2.1) ;
3179-清除项中作为规约合并项(@6.9.1) 而不是一等对象的表示(@6.3) 的标签。
3241+清除项中作为规约合并项(@6.9.1) 而不是一等对象的表示(@6.3) 的标签;
3242+对子项进行清理(@6.10.2.1) 。
31803243 由纯值规约的语义,被清理的子项和被清除的标签不应影响项作为求值结果的表示。若应清理的子项存在,则清理前为平凡(trivial) 的非正规表示。
31813244 若需避免子项的生存期扩展到所在的项,需确保对平凡非正规表示总是存在可预期的清理操作。
31823245 纯值规约中存在的清理、修改标签和修改值数据成员的作用仍非决定性有序(@6.2) ,因此一个项中的清理可能先序(@4.4.3) 最终确定作为值的表示的对值数据成员的修改操作。
@@ -3351,14 +3414,14 @@
33513414 @6.11.4.2 上下文切换:
33523415 NPL::EnvironmentSwitcher 类型用于切换上下文(@6.11.3.1) 中的当前环境(@6.11.3) 。
33533416
3354-@6.12 异步规约(asynchronized reduction) :
3417+@6.12 异步规约(asynchronous reduction) :
33553418 直接式(direct style) 的语言实现利用宿主语言的函数调用实现图规约(@6.2) ,满足规约函数(@6.10.5) 的调用包含子项的规约,即同步规约(synchronized reduction) 。
33563419 非同步规约称为异步规约。异步规约允许在宿主语言中分离部分规约(@6.10.1) 的不同部分的实现,使之公开为一等对象表示控制状态(@4.1) 及被调度。
33573420 NPLA 支持同步规约和异步规约中立(neutral) 的接口设计(@6.12.5) 。具体的异步规约在派生实现使用。
33583421 NPLA 实现支持异步规约,以避免 C++ 本机(@2.4.1) 资源限制下不限定数量(@5.10.2) 调用深度时可能引起的宿主语言的未定义行为(@5.4) 。
33593422 若资源分配失败,应满足常规宿主资源分配要求(@5.4.1) 。
33603423
3361-@6.12.1 异步规约动作(@6.11.3) :
3424+@6.12.1 异步规约动作:
33623425 NPLA 中,这通过以规约动作(@6.11.3) 作为 CPS(@6.11.3) 的被跳板(trampoline) 执行的中间值(@4.4.6.2) 例程的机制实现。此时,当前动作(@6.11.3) 是尾动作(@6.11.3) 。
33633426 跳板例程设置的规约操作将被异步调用(@6.11.3) ,即当前动作(@6.11.3) 设置为异步动作,在跳板中被循环调用。上下文的重写循环(@6.11.3) 提供一个跳板调用当前动作(@6.11.3) 。
33643427 这种方式支持嵌套调用安全(@5.11) 。其它方式实现可能具有更好的性能,但存在限制而不被使用,如:
@@ -3474,7 +3537,7 @@
34743537 除非另行指定,特定不精确数的具体的误差是未指定的。
34753538 数值的绝对精度(precision) 是其内部表示蕴含的误差的上界的倒数。对确定使用进位制的表示,精度也指精确表示的数值位数。
34763539 数值的内部表示中能以实数描述的度量应至少具有整数数量级精度,即误差不大于 1 。
3477-精确数和不精确数在数值上可能相等,而类型不同。
3540+精确数和不精确数在数值上可能相等,而类型不同(@4.7.2) 。
34783541 宿主类型中的本机整数和浮点类型是数值类型的子类型,分别称为 fixnum 和 flonum 。这些类型在项的内部表示(在值数据成员(@6.2) 中)预期直接占据本机存储而不需要动态分配。
34793542 Fixnum 总是精确数;flonum 总是不精确数。
34803543 **注释** 当前实现中,所有数值是 fixnum 或 flonum 之一。两者分别是宿主的整数类型(排除字符和 bool 类型)以及浮点数类型。
@@ -3485,7 +3548,7 @@
34853548 对 fixnum ,+0 和 -0 是相等的数值。Flonum 不同符号的零值在值的表示中可以不同,但在数学意义上相等,表示同一个数。实现中的其它和具体表示无关的等价谓词(@4.1.4) 是否表现这种不同是未指定的。
34863549 如其它大多数类型,数值类型符合正规表示(@6.10.7) ,只使用值数据成员(@6.2) 表示有效的数据。
34873550 **注释**
3488-NPLA 的不同的名义(@4.7.1) 数值类型的集合到宿主类型的集合的类型映射(@5.5) 是满射,即本节指定的宿主类型总是关联至少一个能表示它的值的 NPLA 数值类型。
3551+NPLA 的不同的名义(@4.7.2) 数值类型的集合到宿主类型的集合的类型映射(@5.5) 是满射,即本节指定的宿主类型总是关联至少一个能表示它的值的 NPLA 数值类型。
34893552 同时,NPLA 数值类型可以映射到其它类型,特别地,NPLA 整数的类型映射目标是宿主语言整数类型和包含整数值的非典型宿主类型(@5.5) 的并。所以,NPLA 数值整体的类型映射不构成简单的一对多或多对多关系。
34903553 特殊值同 LIA-1 定义,引用 IEC 60559 的具体值,仅适用于浮点数。
34913554 无限大值在数学上属于超实数(hyperreal number) ,在浮点数实现中属于扩展实数(extended real number) 。
@@ -3510,7 +3573,7 @@
35103573 其中,计算结果依赖影响计算结果的操作数,并依赖至少一个数值操作数。
35113574 除非另行指定:
35123575 在数学上有意义的前提下,数值操作同时支持以上尽可能多的数值类型的操作数。
3513-数值操作对预期的数值操作数进行类型检查(@4.7.3) ,失败时出错,抛出 NPL::TypeError 异常(@6.5) 。
3576+数值操作对预期的数值操作数进行类型检查(@4.7.4) ,失败时出错,抛出 NPL::TypeError 异常(@6.5) 。
35143577 数值操作不区分数值操作数中对应的真值相等的精确数或不精确数。
35153578 可假定数值操作数和计算过程中不出现 SNaN(signaling NaN) 值;
35163579 若作为操作数的精确数决定计算结果在数学上未定义,则引起错误。
@@ -3564,7 +3627,7 @@
35643627 NumberNode 传递 NPL::ResolvedArg<>(@6.9.4) ,包含被 NPL::ResolveTerm(@6.9.4) 解析后的节点和访问引用的指针( NPL::ResolvedTermReferencePtr(@6.9.4) 类型的值)。
35653628 **注释** 后者可以对引用中的标签(@6.8.3.1) 进行处理。
35663629
3567-@6.14.4 数值类型谓词(@4.7.5) :
3630+@6.14.4 数值类型谓词(@4.7.6) :
35683631 以下一元函数判断 const ValueObject& 参数是否表示特定的数值类型的值:
35693632 IsExactValue
35703633 IsInexactValue
@@ -3738,7 +3801,7 @@
37383801 @7.1.1 对象语言约定:
37393802 NPLA1 仅使用宿主语言的类型和值作为状态。
37403803 在 NPLA 的基础上,NPLA1 要求对象语言支持以一等对象作为表达式并被求值(@6.3) 。
3741-类型等价性基于类型映射(@5.5) 及其实现(@6.1.7) ,由 C++ 的语义规则定义。
3804+类型等价性(@4.7.2) 基于类型映射(@5.5) 及其实现(@6.1.7) ,由 C++ 的语义规则定义。
37423805 值等价性由宿主实现的 == 表达式的结果定义。
37433806 除非另行指定,所有类型的外部表示(@4.1.1) 都是允许从作为内部表示的项节点结构确定的同宿主类型(@5.5) 的空字符结尾的字符串(即 ISO C++ 的 NTCTS )。
37443807 引入过程(@4.5.2.1) 的具体形式参见 @8.4.5 ,也可约定通过特定的本机函数(@5.3) 等其它方式提供。
@@ -3778,7 +3841,7 @@
37783841 除非另行指定,为在表达式的求值结果中取得折叠的引用值:
37793842 当被引用对象是被绑定对象时,引用值被折叠一次(@6.8.4.2) ;
37803843 否则,引用值通过蕴含一次引用值提升转换(@5.8.4) 的方式被消除(@5.8.3.4) 一次。
3781-除非另行指定,违反不可修改引用引入的假定的修改操作引起类型错误(@4.7.3) 。
3844+除非另行指定,违反不可修改引用引入的假定的修改操作引起类型错误(@4.7.4) 。
37823845 **原理**
37833846 访问不作为被绑定对象的子对象时,通常并非如宿主语言为支持推断参数类型的方式使用引用折叠,构造折叠的引用值默认不直接使用引用折叠(除临时对象标签外同 ISO C++ )的规则(@5.8.3.5) ,而直接由被引用对象确定。
37843847 对访问列表中的子项构成的子对象引用,这也和宿主语言的涉及成员访问的表达式类似(@6.4.6.1) :除以下不同:
@@ -4020,7 +4083,7 @@
40204083 其中,函数成员 operator() 直接提供核心变换算法。
40214084 变换包含递归和非递归变换。
40224085 变换结果可被进一步规约。
4023-另见 @7.9.2 。
4086+另见异步规约实现(@7.9.2) 。
40244087
40254088 @7.5.3 叶节点分析 :
40264089 叶节点(@6.2.1) 分析 API 作为标记器(@7.8) 的实现转换词素到包含记号的项,包括:
@@ -4085,7 +4148,7 @@
40854148 @7.6.2 助手 API :
40864149 NPLA1 提供枚举 WrappingKind 标识初始化 A1::FormContextHandler 需要的包装数(@7.6.1.1) 。
40874150 基于 NPL::EmplaceLeaf(@6.11.4.1) ,NPLA1 提供注册处理器事件的助手函数模板,无需显式嵌套构造 A1::ContextHandler 和其它处理器(@7.6.1.1) 类型:
4088-A1::RegisterHandler
4151+A1::RegisterFormHandler
40894152 A1::RegisterForm
40904153 A1::RegisterStrict
40914154 助手函数的模板传入被包装的处理器和检查例程。
@@ -4156,7 +4219,7 @@
41564219 绑定操作(@7.7.3) API 。
41574220
41584221 @7.7.2 求值规约操作:
4159-基于 NPLA 合并动作(@6.12.7) ,NPLA1 提供动作合并规约操作:
4222+基于 NPLA 合并动作(@6.12.7) ,NPLA1 提供动作合并规约操作 API :
41604223 类型 A1::EnvironmentGuard
41614224 函数 A1::RelayForEval 直接求值(@4.1) 规约:规约并传递求值的结果。
41624225 函数 A1::RelayForCall 函数调用规约( β 规约(@4.5.3) ):规约并传递词法闭包规则(@4.6.1.2) 下求值的结果。
@@ -4164,6 +4227,10 @@
41644227 为确保嵌套函数调用时允许引用绑定到非直接主调函数的临时对象(@5.8.5) 形式参数,已被匹配作为绑定目标的项所在的不表示操作数的项(参见 @7.7.3 )可能被转移以保证其子项生存期足够长。
41654228 按 @6.4.7.2 ,此时被作为绑定目标的子项不需被修改(因此自动符合 @6.4.8 的要求)。
41664229 间接调用 A1::RelayForEval 或 A1::RelayForCall 的操作是求值规约操作。
4230+求值规约操作附带绑定操作匹配被绑定的操作数树(@4.4.5.1) 。
4231+求值规约操作中可能具有提升操作(@7.1.4) 。
4232+忽略提升操作可允许对象语言的函数调用返回(@4.5.3.1) 引用值(@6.3.4) 。
4233+**注释**
41674234 等效间接调用 A1::RelayForEval 的求值规约操作包括:
41684235 Forms::Eval(@8.4.4.1)
41694236 Forms::EvalRef(@8.4.4.1)
@@ -4171,9 +4238,6 @@
41714238 Forms::EvalStringRef(@8.4.4.1)
41724239 Forms::Apply(@8.4.9)
41734240 间接调用 A1::RelayForCall 的求值规约操作包括通过过程抽象(@8.4.5) 中的函数引入合并子(@4.5.3.2) 的调用。
4174-求值规约操作附带绑定操作匹配被绑定的操作数树(@4.4.5.1) 。
4175-求值规约操作中可能具有提升操作(@7.1.4) 。
4176-忽略提升操作可允许对象语言的函数调用返回(@4.5.3.1) 引用值(@6.3.4) 。
41774241
41784242 @7.7.2.1 求值规约提升项:
41794243 关于提升项的基本用例,参见 @6.4.6.3 。
@@ -4370,6 +4434,17 @@
43704434 函数 A1::BindParameterWellFormed
43714435 函数 A1::BindSymbol
43724436
4437+@7.7.5 诊断 API :
4438+NPLA1 提供一些提供辅助诊断消息的 API 以 Doxygen 命令标识为 \ingroup NPLDiagnostics(@6.13) 。
4439+函数 A1::QueryContinuationName 查询续延名称。
4440+
4441+@7.7.6 辅助公共实现 API :
4442+一些使用以上特性的公共实现以 API 的形式公开,以允许派生实现复用:
4443+函数 A1::MoveGuard
4444+别名模板 A1::GKeptGuardAction
4445+函数模板 A1::MakeKeptGuard
4446+**注释** 另见续延名称(@7.11.6) 和解释(@8.6.2) 。
4447+
43734448 @7.8 REPL API :
43744449 NPLA1 提供 API 通过解释器的各个子模块组装 REPL(read-eval-print loop) 的交互式界面。
43754450 函数 A1::SetupDefaultInterpretation 初始化默认解释,包括一般事件处理例程。
@@ -4379,8 +4454,6 @@
43794454 别名模板 A1::GTokenizer 提供标记器,即和解析器的解析结果类型中的元素类型为参数的转换为 TermNode 的例程。对应解析器类型提供以下实例:
43804455 类型 A1::Tokenizer
43814456 类型 A1::SourcedTokenizer
4382-NPLA1 提供一些辅助 REPL 诊断的 API 以 Doxygen 命令标识为 \ingroup NPLDiagnostics(@6.13) 。
4383-函数 A1::QueryContinuationName 查询续延名称。
43844457 类 A1::REPLContext 可直接作为 REPL 原型。
43854458 A1::REPLContext 以 ContextNode::EvaluateList(@6.11.3.1) 为第二参数调用 A1::SetupDefaultInterpretation 初始化,之后默认解释配置 ContextNode 具有以下能在 A1::ReduceOnce(@7.4.4) 运行的规约算法:
43864459 A1::REPLContext 使用 @7.5.3 的 API 实现标记器。
@@ -4456,8 +4529,8 @@
44564529 以 # 、+ 或 - 起始的不能构成标识符的词素是 NPLA 扩展字面量(@5.2.3.2) 。
44574530 一些求值算法的性质在实现上由 NPLA1 间接值使用规则(@7.1.3) 、A1::EvaluateIdentifier(@7.6.4) 和 A1::REPLContext 配置的求值遍保证。
44584531
4459-@7.9 异步(@6.12.1) 规约支持:
4460-基本概念参见异步规约(@6.12) 。
4532+@7.9 异步规约支持:
4533+异步规约(@6.12) 支持基于异步规约动作(@6.12.1) 实现。
44614534 在 NPLA1 API 中同时提供对应的完全非异步规约的实现,不满足 TCO 要求(@5.10.4) ,仅供参考。
44624535 NPLA1 API 的异步规约依赖上下文中的当前动作(@6.11.3) 。
44634536 以下讨论的由上下文(@6.11.3) 支持的 NPLA API 使用方式和 NPLA1 相关。其它非 NPLA1 的派生实现可使用其它方式或选择不支持。
@@ -4475,6 +4548,7 @@
44754548 部分 NPLA1 API 提供同步和异步的不同实现。一些 API 明确支持异步规约。
44764549 启用异步规约的实现定义为实现选项(@7.1) NPL_Impl_NPLA1_Enable_Thunked 。
44774550 此外,启用分隔符中缀变换(@7.5.2) 的异步的实现定义为实现选项 NPL_Impl_NPLA1_Enable_ThunkedSeparatorPass ,使用单独的异步实现保证嵌套调用安全(@6.1.3) 。
4551+NPL::NPLA1Forms(@8.1) 等同时支持同步和异步规约的 API 可基于这些 API 实现。
44784552
44794553 @7.9.2.1 异步规约基本支持:
44804554 以下 NPLA1 API 可保存当前动作,支持和实现异步规约:
@@ -4776,7 +4850,7 @@
47764850 作为原型实现(@5.1) ,相较性能,NPLA1 在语言设计和实现上都强调扩展性。
47774851 即使替换扩展实现实现对象语言语义等价但削弱 NPLA(@6) 和 NPLA1 API 的变体,一般也不能预期能和常见其它典型动态语言的解释器实现。这主要是由于:
47784852 使用了解释开销较大的 AST 解释器(@6.1.2) ;
4779-缺乏清单类型(@4.7.2) 等提供支持更明确执行路径的元数据;
4853+缺乏清单类型(@4.7.3) 等提供支持更明确执行路径的元数据;
47804854 使用异步规约(@6.12) 和局部的异步调用保证嵌套调用安全(@7.11.7) ;
47814855 为支持合并子可能具有的动态环境(@4.6.1.2) ,规约合并时(@7.6.4.2) 没有优化,可能有不必要的重复开销。
47824856 进一步优化的一般目标是寻找更多可消除的解释开销,这一般可通过以下方法(可能不再使用 NPLA ):
@@ -4857,7 +4931,7 @@
48574931 (begin (define f (lambda () (f))) (f))
48584932
48594933 @7.11.6 续延名称:
4860-模块 NPL::NPLA1 提供特定可由函数 A1::QueryContinuationName(@7.8) 查询得到的续延名称。
4934+模块 NPL::NPLA1 提供特定可由函数 A1::QueryContinuationName(@7.7.5) 查询得到的续延名称。
48614935 其中查询结果的名称和 A1::Continuation::Handler 或 Reducer 的特定目标类型关联。
48624936 除非另行指定,关联的类型是非公开的内部类型。
48634937 在 NPLA1 实现中引入的这些名称包括:
@@ -5007,7 +5081,7 @@
50075081 Forms::SetRestRef
50085082
50095083 @8.4.4 环境和求值操作:
5010-Forms 提供环境相关的操作,以支持对象语言中的环境(@9.9.3 ) 。
5084+Forms 提供环境相关的操作,以支持对象语言中的环境(@9.9.3) 。
50115085
50125086 @8.4.4.1 求值操作:
50135087 Forms 提供以下函数实现对象语言中的求值(@6.15.1) :
@@ -5112,7 +5186,7 @@
51125186 Forms::Wrap 包装(@4.5.3.2) 合并子为应用子(@4.5.3.2) 。
51135187 Forms::WrapOnce 包装操作子为应用子。
51145188 Forms::Unwrap 解包装(@4.5.3.2) 应用子为合并子。
5115-其中后两者会对合并子按 @7.6.1 的约定进行类型检查(@4.7.3) ,若不满足条件则抛出异常。
5189+其中后两者会对合并子按 @7.6.1 的约定进行类型检查(@4.7.4) ,若不满足条件则抛出异常。
51165190 当被解包装的应用子参数是引用时,构造具有非平凡非正规表示(@6.10.11.1) 的子对象引用(@6.8.3.3) 。
51175191
51185192 @8.4.7 错误检查:
@@ -5239,8 +5313,10 @@
52395313 在模块 NPL::Dependency 中引入的续延名称包括:
52405314 eval-$binds1?-env
52415315 eval-lazy-parent
5316+get-module-return
52425317 load-external
52435318 promise-handle-result
5319+require-return
52445320 在模块 NPL::Dependency 中,若特定对象语言操作使用非本机实现(@5.3) ,可使用不同的续延名称。
52455321
52465322 @8.6.2 和 klisp 续延名称的关系:
@@ -5255,17 +5331,18 @@
52555331 combine-return :非 TCO 的规约合并求值(@7.6.4.2) 实现中设置规约合并最后的返回结果。
52565332 equal-subterm :Forms::EqualTermValue(@8.4.1) 比较直接子项。
52575333 equal-siblings :Forms::EqualTermValue 比较同一个项中的剩余其它子项。
5258-因为 klisp 的 equal? 直接使用自动机实现,在比较操作中没有对应的续延。
5259-eval-$binds1?-env :类似 klisp 的 eval-$binds-env 。
5334+eval-$binds1?-env :Forms::LoadGroundContext(@8.5.2) 实现 $binds1?(@11.4.2) ,类似 klisp 的 eval-$binds-env 。
52605335 eval-acc-head-next :Forms::AccL 和 Forms::AccR(@8.4.9) 公共操作后分派的不同操作。
5261-eval-acc-nested :Forms::AccL 和 Forms::AccR(@8.4.9) 嵌套调用抽象列表余下的元素的公共操作。
5336+eval-acc-nested :Forms::AccL 和 Forms::AccR 嵌套调用抽象列表余下的元素的公共操作。
52625337 eval-accl-accl :Forms::AccL 递归调用。
52635338 eval-accl-tail :Forms::AccL 调用取抽象列表余下的元素的函数。
52645339 eval-accr-accr :Forms::AccR 递归调用。
52655340 eval-accr-sum :Forms::AccR 调用取抽象列表部分和的函数。
52665341 eval-bindings-to-env :Forms::BindingsWithParentToEnvironment(@8.4.9) 在创建的环境中绑定变量并返回。
52675342 eval-foldr1-kons :Forms::FoldR1(@8.4.9) 应用子调用。
5268-eval-guard :非 TCO 的求值实现重置守卫的附加动作帧(@6.12.6)。
5343+eval-guard :A1::MakeKeptGuard(@7.7.6) 引入的守卫操作,用于:
5344+非 TCO 的求值实现重置守卫的附加动作帧(@6.12.6) ;
5345+异步规约(@7.9.2) 的 LoadModule_std_io(@8.5.2) 实现 get-module(@12.4) 和 LoadModule_std_module(@8.5.2) 实现 require(@12.5) 加载翻译单元时以守卫保护当前环境。
52695346 eval-lazy-parent :惰性求值指定父环境求值。
52705347 eval-let-parent :Forms::LetWithEnvironment 和 Forms::LetWithEnvironmentRef 中求值过程抽象(@8.4.5) 的父环境的表达式。
52715348 eval-letrec-bind :Forms::LetRec 和 Forms::LetRecRef 中的变量绑定。
@@ -5274,14 +5351,16 @@
52745351 eval-map1-appv :Forms::Map1(@8.4.9) 应用子调用。
52755352 eval-tail :TCO 动作尾上下文求值(和 klisp 不同,不依赖 GC(@5.6.4) 而在 TCO 动作单独保存资源(@7.10.5.1) )。
52765353 eval-vau-parent :Forms::LambdaWithEnvironment 和 Forms::VauWithEnvironment(@8.4.5) 创建操作子时求值过程抽象的父环境的表达式。
5277-注意 klisp 的 $vau 不具有应保证被求值的实际参数,不需要异步调用,没有对应的续延。
5354+get-module-return :异步规约的 LoadModule_std_io 实现 get-module 返回结果。
52785355 load-external :A1::RelayToLoadExternal(@8.5.2) 加载外部翻译单元。
52795356 match-ptree-recursive :支持延迟递归绑定( Forms::DefineWithRecursion 和 Forms::SetWithRecursion(@8.4.4.3) )。
5280-promise-handle-result :求值代理结果。
52815357 provide-let-return :Forms::ProvideLet(@8.4.9) 返回环境。
5358+require-return :异步规约的 LoadModule_std_module 实现 require 加载翻译单元后设置和返回结果。
52825359 sequence-return :非 TCO 的 A1::ReduceOrdered(@7.4.6) 实现中设置规约合并最后的返回结果。
52835360 **注释**
5284-续延 import-bindings 和 Forms::Provide 使用的 provide-let-return 共用部分本机实现。
5361+klisp 的 equal? 直接使用自动机实现,在比较操作中没有对应的续延。NPLA1 支持互操作(@5.3) 允许相等的子对象以异步规约实现,不能使用这种方式而排除续延。
5362+klisp 的 $vau 不具有应保证被求值的实际参数,不需要异步调用,没有 eval-vau-parent 对应的续延。
5363+import-bindings 和 Forms::Provide 使用的 provide-let-return 共用部分本机实现。
52855364
52865365 @9 NPLA1 核心语言设计:
52875366 NPL 是独立设计的,但其派生语言和其它一些语言有类似之处;这些语言和 NPL 方言之间并不具有派生关系。但为简化描述,部分地引用这些现有语言规范(@1.2.1.2) 中的描述,仅强调其中的不同。
@@ -5406,7 +5485,6 @@
54065485 <test> :类似 <object> ,通常预期为 <boolean> ,作为条件。
54075486 和 Scheme 类似但和 Kenerl 不同,非 #t 的值在决定分支时视同 #f ,以允许在 <boolean> 外自然扩展的逻辑代数操作。
54085487 和 Common Lisp 不同,不使用空列表(或符号 nil )代替 #f ,以避免需要特设的规则以特定的其它类型的值(如 Common Lisp 的符号 t )表示逻辑真(这在逻辑非操作中不可避免)。
5409-**原理** 和 [RnRK] 的理由不同,允许布尔代数以外扩展的比较判断在此不认为是易错的,而是有意的设计(by design) 。这避免预设地假定类型的名义语用作用(“角色(role)” ),也避免限定语言和派生语言的类型全集(@4.7.4) 。
54105488 <combiner> :合并子。
54115489 <applicative> :应用子(@4.5.3.2) 。
54125490 <predicate> :谓词,是应用操作数的求值结果的值为 <test> 的 <applicative> 。通常实现结果是 <boolean> 的纯求值(@4.4.4) 。
@@ -5418,6 +5496,8 @@
54185496 此外,支持的数值操作数参见 NPLA 数值类型(@6.14.1) 。
54195497 **原理**
54205498 <object> 等求值得到的操作数不保证是语法意义上连续的词法组合,不能由多个表达式构成,因此即便出现在元素末尾,也不能如 <body> 一样减少括号。
5499+<object> 表示类型全集(@4.7.5) ,其元素可被断言在论域(@1.5.3.6) 内,即任何其它类型都是 <object> 的子类型(@9.5.4.1) 。
5500+和 [RnRK] 的理由不同,允许布尔代数以外扩展的比较判断在此不认为是易错的,而是有意的设计(by design) 。这避免预设地假定类型的名义语用作用(“角色(role)” ),也避免限制语言和派生语言的类型全集(@4.7.5) 的设计。
54215501
54225502 @9.2.2.4 文法形式补充约定:
54235503 除非另行指定,以 <symbols> 指定的值被作为 <definiend> 或 <formals> 使用时不引起错误。注意 <symbols> 在被其它上下文使用时仍可能引起错误。
@@ -5453,7 +5533,7 @@
54535533 由 NPLA 约定和 NPLA 记号值(@6.8.1) 的约定,字符串字面量的类型为 <string>(@9.2.2.2) 。
54545534 基于 NPLA 数值类型(@6.14.1) ,数值字面量的类型为 <number> 。
54555535 除非另行指定,数值的具体宿主类型未指定。
5456-**注释** 部分数值可指定具体的子类型(@4.7) 。
5536+**注释** 部分数值可指定具体的子类型(@4.7.2) 。
54575537
54585538 @9.3.1.2 数值字面量:
54595539 基于 NPLA 数值类型(@6.14.1) ,NPLA1 支持 <integer> 类型的精确数数值字面量和 flonum(@6.14.1) 不精确数数值字面量。
@@ -5653,16 +5733,16 @@
56535733 函数操作的语义可单独指定检查,具体形式由具体操作指定。
56545734
56555735 @9.5.4.1 类型检查:
5656-基于名义类型(@4.7.1) ,对象语言实现应具有语义规则指定的类型检查(@4.7.3) ,以确保程序的执行符合操作的必要前置条件。
5736+基于名义类型(@4.7.2) ,对象语言实现应具有语义规则指定的类型检查(@4.7.4) ,以确保程序的执行符合操作的必要前置条件。
56575737 操作的语义可要求以下的类型检查:
56585738 对求值得到的操作数(@9.2.2.2) ,按文法约定(@9.2.2) 的约束进行类型检查;
56595739 根据特定对象状态,指定动态类型(如要求对象可修改)的检查。
56605740 实现可能添加其它不违反语义要求的类型检查。
56615741 基于表达式的类型(@5.8.6) ,对应对象语言表达式的表示实体的元素可指定操作数上明确的类型要求。
5662-部分实体从属于其它实体类型而构成子类型(subtyping) 关系;部分的规约操作取得求值结果保证结果中的值可能具有的特定类型集合,这些类型也一并在以下描述中给出;其它情形不指定类型。
5742+部分实体从属于其它实体类型而构成子类型(@4.7.2) 关系;部分的规约操作取得求值结果保证结果中的值可能具有的特定类型集合,这些类型也一并在以下描述中给出;其它情形不指定类型。
56635743 规约预期符合约束。若违反由项的值对应的动态类型(@6.3) 不匹配导致,则求值失败;否则,行为未指定。
56645744 除非另行指定,不同类型检查的作用的顺序未指定。
5665-类型错误(@4.7.3) 引发错误对象(@9.5.1) 。
5745+类型错误(@4.7.4) 引发错误对象(@9.5.1) 。
56665746
56675747 @9.6 外部表示:
56685748 同 @4.1.1 ,外部表示由派生实现约定。
@@ -5746,8 +5826,10 @@
57465826 函数调用(@4.5.3.1) 中,(使用 $vau 和 $lambda 等合并子抽象引入的)合并子(@9.9.5) 的调用(注意不包括其它途径引入的外部函数的调用);
57475827 对 <body>(@9.2.2.1) 的求值;
57485828 对 <expression-sequence>(@9.2.2.1) 中最后一个子表达式的求值;
5749-由 <test>(@9.2.2.1) 控制或连续求值多个表达式中最后被求值的子表达式求值,包括 $if(@11.3.3) 、$sequence(@11.4.1) 、$and(@11.4.1) 和 $or(@11.4.1) ;
5750-函数 eval(@11.3.7) 、eval%(@11.3.7) 、$quote(@11.4.1) 、id(@11.4.1) 、idv(@11.4.1) 、eval-string(@12.1) 和 eval-string%(@12.1) 对 <expression> 的求值或替换;
5829+由 <test>(@9.2.2.1) 控制或操作子中连续求值多个表达式中最后被求值的子表达式求值。
5830+**注释** 这包括 $if(@11.3.3) 、$sequence(@11.4.1) 、$and(@11.4.1) 和 $or(@11.4.1) 的最后的参数(若存在)的求值。应用子对参数的求值明确非 PTC 。
5831+函数 eval(@11.3.7) 、eval%(@11.3.7) 、$quote(@11.4.1) 、id(@11.4.1) 、idv(@11.4.1) 、eval-string(@12.1) 和 eval-string%(@12.1) 的调用中蕴含的求值或替换;
5832+**注释** 这不是作为参数的 <expression> 的求值。
57515833 函数 apply(@11.4.1) 对 <applicative> 和 <object> 在 <environment> 或默认的动态环境中的应用;
57525834 函数 accl(@11.4.1) 、map-reverse(@11.4.3) 和 for-each-ltr(@11.4.3) 蕴含的尾上下文的递归调用。
57535835
@@ -5767,7 +5849,7 @@
57675849 @9.7.4.4 限制影响:
57685850 在不保证支持 PTC 时,实现循环可控制结构的程序应注意避免无限递归。如以下 Kernel 程序:
57695851 ($define! (f g) (list ($lambda (x) (g (- x 1))) ($lambda (x) (f (- x 1)))))
5770-对应 NPLA1 程序(设 - 是保证能终止计算的减法):
5852+对应 NPLA1 程序(设 - 是减法,保证为纯函数(@4.5.3.1) 和全函数(@4.8.2) ):
57715853 $def! (f g) (list ($lambda (x) (g (- x 1))) ($lambda (x) (f (- x 1))))
57725854 使用任意数值作为参数调用 f 时,若实现不支持 TCO ,以数值调用函数 f 或 g 可引起宿主语言的未定义行为(非异步规约(@7.9) )或最终无法分配存储(异步规约)而不是保持不终止求值。
57735855 实际实现当前支持这个例子的 TCO 。
@@ -5840,7 +5922,7 @@
58405922 复制赋值在自赋值时的源操作数复制仍可能出错。使用基本提升操作(@6.9.5.1) 实现时,一般使用 NPL::LiftTermOrCopy 而不是检查自赋值的 NPL::LiftOtherOrCopy 。
58415923 类似宿主语言,除非另行指定,赋值操作不保留源操作数的值类别(@9.7.2) 和可修改性。
58425924 **注释**
5843-赋值不保证子对象(@9.8.2) 的同一性不被改变。
5925+赋值不保证子对象(@9.8.2) 的同一性不被改变;子对象的引用仍可能被赋值无效化(@9.8.6) 。
58445926
58455927 @9.8.3.2 转移:
58465928 转移可导致被转移对象的外部可见的修改。
@@ -5865,18 +5947,21 @@
58655947 为了满足不依赖引用的一等对象(@4.1) ,区分引用和被引用的对象都作为一等实体,这是必要的。
58665948 一般地,谓词 eq? 用于判断两个参数对象的同一性(@4.1) 。
58675949
5868-@9.8.6 无效化(@6.3.4) :
5950+@9.8.6 无效化:
58695951 若对象的引用值保持有效(@9.9.1) ,则指称的左值的对象同一性(@4.1) 不变。
5870-作为引用值(@6.3.4) 的派生实现,对象语言中的引用值的无效化包括以下情形:
5952+作为引用值(@6.3.4) 的派生实现,对象语言中的引用值的无效化(@6.4.4) 包括以下情形:
58715953 被引用的对象存储期已结束(此时引用值是悬空引用(@9.4.3.2) );
58725954 对象被除通过重绑定(@9.9.3.7) 、赋值(@9.8.3.1) 和另行指定的情形以外的方式修改(@9.8.3) ,而引起对象同一性的改变。
5873-对项的赋值(@9.8.3.1) 仍可能因为对子项的修改而使其表示的对象的引用值无效化。
5955+**注释**
5956+对项的重绑定或赋值仍可能因为对子项的修改蕴含被替换的对象的销毁,引起子对象的生存期结束,而使其表示的对象的引用值无效化。
58745957
58755958 @9.8.7 类型分类:
5876-和 Kernel 不同,NPLA1 不要求支持任意类型的不相交集合即分区(partition) 。
5877-这避免假定类型全集,并开放类型映射(@5.5) 。但基于实体文法(@9.2.2) 引入的类型仍被分区。
5878-和 Kernel 不同,NPLA1 的类型判断谓词是一元谓词,只接受一个参数(@4.7.5) ,以强调语言提供的接口的正交性(@1.5.2.4) 。这也避免对 map(@11.4.3) 等库函数的依赖。
5959+和 Kernel 不同,NPLA1 不要求支持任意类型的表示(@4.7) 不相交,即分区(partition) 。
5960+但基于实体文法(@9.2.2) 引入的类型仍被分区。
5961+和 Kernel 不同,NPLA1 的类型判断谓词是一元谓词,只接受一个参数(@4.7.6) ,以强调语言提供的接口的正交性(@1.5.2.4) 。这也避免对 map(@11.4.3) 等库函数的依赖。
58795962 和 Kernel 不同,列表类型只包括真列表(@9.9.4) 。列表节点的实现的表示详见 @6.2.1 。
5963+**原理**
5964+不要求分区这避免全局地假定类型全集(@4.7.5) 的具体表示,并支持开放的类型映射(@5.5) 。
58805965
58815966 @9.9 对象语言数据结构:
58825967 本节指定在 NPLA1 允许以一等实体(@4.2) 被使用的基本元素。
@@ -5931,9 +6016,10 @@
59316016 语言实现可提供环境对象以外的非一等环境。总是不能被对象语言以一等对象访问的环境是隐藏环境(hidden environment) 。
59326017 一般地,隐藏环境是某一个(非隐藏的)一等环境的直接或间接父环境(而能通过求值等间接操作被访问)。
59336018
5934-@9.9.3.2 新环境(fresh environment) :
5935-新环境是新创建的环境,和先前的当前环境(@6.11.3) 不共享相同环境对象。
5936-除非另行指定,新环境是不存在能引起程序行为改变的父环境的空环境(@4.6.1) 。
6019+@9.9.3.2 新环境:
6020+新(fresh) 环境是新创建的环境。
6021+新环境和先前的其它的(特别地,包括当前环境(@6.11.3) )不共享相同环境对象。
6022+除非另行指定,新环境是空环境(@4.6.1) 。
59376023 创建新环境的一个例子是 Vau 抽象(@8.4.5) 实现过程调用。
59386024
59396025 @9.9.3.3 环境的宿主值和所有权:
@@ -5948,7 +6034,7 @@
59486034 违反关于环境稳定性的要求的程序具有未定义行为(@9.1.4) 。
59496035 当前要求确保的稳定性包括:
59506036 隐藏环境(@9.9.3.1) 的绑定有效稳定性(@9.9.3.7) 和值稳定性(@9.9.3.8) ;
5951-从构造时即明确要求的一等环境(@10.2.1) 的稳定性。
6037+从构造时即明确要求的一等环境的稳定性(@10.2.2) 。
59526038 一般地,环境的稳定性要求构造环境时不能依赖非特定的动态环境(作为被名称解析(@6.11.1) 访问的父环境),因为这些环境的绑定可能具有在构造环境之后确定的绑定,而不能确保环境中的名称具有可预知的含义。
59536039 环境的稳定性简化分析程序的推理过程,也在许多上下文中允许程序更易被优化。
59546040 和 Kernel 不同,环境(@11.3.7) 和合并子操作(@11.3.8) 及基于这些操作的一些派生操作(@11.4) 的操作数树(@7.7.3) 构造时不检查其中的非列表项是否都为符号或 #ignore(而延迟到匹配时检查);匹配时不检查符号重复;若形式参数中的符号重复,则绑定的目标未指定。
@@ -5963,7 +6049,12 @@
59636049 要求隐藏环境稳定允许实现共享隐藏环境作为父环境而提供标准环境(@11.4.1) 。
59646050
59656051 @9.9.3.5 环境生存期:
5966-引用环境中的名称时,应确保环境在生存期内。
6052+对象语言的实现提供给用户程序(@9.1) 使用的初始环境的环境对象及其中的子对象满足:
6053+其创建先序于用户程序的对象的创建;
6054+除非提供为不满足环境稳定性(@9.9.3.4) 的环境中的被绑定对象,其销毁后序于用户程序的对象的销毁。
6055+程序引用环境中的名称时,应确保环境在生存期内。
6056+**注释**
6057+不满足环境稳定性的环境的被绑定对象可能被修改。若这个对象是一个环境的唯一强引用,则对应的环境对象在替换为其它值时被销毁。
59676058 特别地,应注意使用函数时引入父环境的生存期。
59686059 当前实现对环境引用(@6.11.1) 是 NPL::EnvironmentReference(@6.11.1.1) 的情形提供一定程度的检查(@6.1.2.1) ,这通过引用环境时检查 NPL::EnvironmentReference 中的弱引用是否可被实现。
59696060 特别地,这可针对在 vau 抽象(@8.4.5) 中捕获的静态环境。
@@ -5974,7 +6065,7 @@
59746065 其它情形不受检查。
59756066 注意正常的操作不应有生存期错误。
59766067 引起这些错误时应已导致未定义行为如循环引用(@9.9.1.1) ,因此程序行为不应依赖检查(@8.4.5.2) 。
5977-另见 @9.4 。
6068+另见对象语言内存安全保证(@9.4) 。
59786069
59796070 @9.9.3.6 环境中的绑定(@4.1) :
59806071 和 Kernel 不同,环境中的绑定的抽象不依赖对象语言中表达的引用的概念,允许直接关联一个没有引用的值。另见 @4.2.3 和 @6.11.1 。
@@ -5985,13 +6076,15 @@
59856076
59866077 @9.9.3.7 重绑定(rebinding) :
59876078 和 Scheme 类似,环境中允许变量以相同的名称被重新绑定,简称重绑定。
5988-对象的引用(@9.9.1) 不因其引用的对象被重绑定操作替换值而被无效化(@9.8.6) 。
6079+被绑定对象(@5.7.1) 的引用(@9.9.1) 不因其引用的对象被重绑定操作替换值而被无效化(@9.8.6) 。
59896080 重绑定替换被绑定对象的值,不改变对象的同一性(@9.8.1) 。若其中存在子对象(@9.8.2) ,则子对象被销毁,任何子对象的引用值被无效化。
59906081 特别地,若继续访问已被求值(@9.7.1) 指称的引用值引用的对象,则超出生存期访问而引起 NPLA 未定义行为(@5.4) 。
59916082 因为设计原则禁止无法避免的实现开销,这里没有检查而直接指定为无法满足保证所有权条件的未定义行为(@6.4.6.2) 。
59926083 任意隐藏环境(@9.9.3.1) e 应满足以下绑定有效稳定性:
59936084 通过引用值间接访问 e 中绑定的对象时绑定保持有效(蕴含不被移除或重绑定),保持被绑定对象的生存期和 e 对其的所有权。
59946085 这避免因为上述访问违反内存安全(@5.6.3) 而引起 NPLA 未定义行为。
6086+**注释**
6087+类似赋值(@9.8.3.1) ,重绑定不保证子对象(@9.8.2) 的同一性不被改变;子对象的引用仍可能被重绑定无效化(@9.8.6) 。
59956088
59966089 @9.9.3.8 被绑定对象的值和可观察行为(@4.1.3) :
59976090 任意隐藏环境(@9.9.3.1) 的 e 的任意同一(@9.8.1) 被绑定对象 o 应满足以下的值稳定性:
@@ -6106,19 +6199,28 @@
61066199 除非另行指定,根环境和作为库特性的一等环境对象初始化后在用户程序访问前被冻结(@9.9.3.9) 。
61076200 用户程序初始的当前环境(@6.11.3) 是一个包含基础环境作为直接或间接父环境的空环境(@4.6.1) 。
61086201 实现可选地提供基础环境以外的根环境,允许派生实现定义在用户程序中修改其中的绑定的机制(而不一定是隐藏环境),直至被实现初始化参考环境的特定用户程序封装或冻结而避免进一步修改。
6109-如有必要,用户程序可通过派生实现定义的方式引入其它根环境。这些方式一般依赖本机实现。
6202+如有必要,用户程序可通过派生实现定义的方式引入其它根环境。
6203+**注释**
6204+特性设计注记:
6205+ 为避免依赖逻辑上复杂的形式,一些特性当前在当前设计中排除。
6206+ 例如,依赖一阶算术的操作、其硬件加速形式的 ISA 表示的整数操作及依赖这些操作实现的数值塔(numerical tower) 被整体忽略。
6207+ 上述忽略的特性可由派生实现补充(如以类似 @11.4 的形式),在派生根环境后按需进行 AOT(ahead-of-time) 优化(如 Kernel 的 $let-safe! 中包含的内容,其中引用基础环境的符号不再可变),然后组成类似基础环境。
6208+通过派生实现定义的方式一般依赖本机实现。
61106209
61116210 @10.1.1 初始化:
61126211 通过 Forms::LoadGroundContext(@8.5.2) 初始化基础上下文(@8.5.2) 蕴含的根环境是求值(@9.7.1) 默认使用的求值环境(@4.6.1),初始化后可直接封装为基础环境使用。
6113-以上初始化同时提供扩展字面量(@9.3.1.3) 支持。
6114-
6115-@10.1.2 特性设计注记:
6116-为避免依赖逻辑上复杂的形式,一些特性当前在当前设计中排除。例如,依赖一阶算术的操作、其硬件加速形式的 ISA 表示的整数操作及依赖这些操作实现的数值塔(numerical tower) 被整体忽略。
6117-上述忽略的特性可由派生实现补充(如以类似 @11.4 的形式),在派生根环境后按需进行 AOT(ahead-of-time) 优化(如 Kernel 的 $let-safe! 中包含的内容,其中引用基础环境的符号不再可变),然后组成类似基础环境。
6212+派生实现可在以上初始化结束之后,在运行用户程序之前完成其它初始化。
6213+初始化成功后,用户程序被运行;否则,程序非正常终止。
6214+以上初始化同时可能提供扩展字面量(@9.3.1.3) 支持。
6215+**注释**
6216+对初始化失败终止的程序,建议但不要求实现给出诊断。
6217+
6218+@10.1.2 导入(import) 符号:
6219+在环境中定义另一个环境中的同名变量,使被定义的变量是后者的引用值或值的副本,则称指定此变量名的符号在后者被导入前者。
61186220
61196221 @10.2 模块:
61206222 同 [RnRK] ,以绑定提供的语言特性被分组归类为模块(@1.5.6.4) 。
6121-模块的源(source) 提供特性的实现,可以是本机实现或者 NPLA1 程序。
6223+模块的源(source) 提供特性的实现,可以是本机实现或者 NPLA1 程序。对应的模块分别是本机模块和源程序(@1.2.4) 模块。
61226224 模块的源可以是实现内建的,或位于实现环境提供的外部资源(如文件系统)。
61236225 因为模块以绑定的集合的形式提供,需被包含在可访问的环境,或包含环境作为子对象的其它对象中。
61246226 以环境对象作为模块的源的模块化方式称为环境作为模块(environment as module) 。[RnRK] 的 get-module 的结果和参考实现扩展环境(@12) 的模块是这种方式的例子。
@@ -6128,13 +6230,31 @@
61286230 一般地,模块和加载模块得到的环境对象没有直接对应关系:一个模块的绑定可以由一个或多个环境提供,一个已被加载的环境可能提供零个或多个程序可见的模块。但除非另行指定,一个模块的绑定不在超过一个的不相交的环境(之间没有直接或间接父环境关系)中提供。
61296231 程序可通过加载外部模块来源取得模块。除非另行指定,这种模块以一个一等环境对象(可包含作为环境的直接或间接子对象)中的绑定提供。
61306232
6131-@10.2.1 模块稳定性:
6233+@10.2.1 模块初始化:
6234+标准库实现可作为语言实现(@1.2.3) 。实现初始化(@10.1.1) 时以提供模块时,可访问不作为公开接口提供的模块的源。
6235+**注释**
6236+派生实现可同时以标准库以外形式提供这些源为公开接口,用户程序也可显式地加载这些源对应的模块。
6237+除非另行指定:
6238+ 若这些源可能引起引入非公开的接口的副作用,则对应的模块不应被用户程序直接加载。
6239+ 假定加载这些模块时,当前环境(@6.11.3) 是和标准环境(@11.4.1) 或与其等价的其它环境。
6240+ 其中,等价指使用其它环境不引入程序可观察行为(@4.1.3) 差异。
6241+ **注释** 等价的环境的例子包括以标准环境为父环境的空环境,以及这样的空环境导入符号(@10.1.2) 的得到的结果。
6242+ **注释** 若模块的加载不访问加载时初始的当前环境(通常仅在本机模块上适用),加载模块使用的环境可不影响可观察行为而不影响假定(即便和标准环境不等价)。
6243+违反以上要求或假定的程序行为未定义。
6244+**原理**
6245+以源程序模块实现时,一般不要求检查初始环境。这能有效减少实现的复杂性。
6246+因为标准环境不提供用户程序检查是否和其中定义的实体一致的直接的方法,通过 eq?(@11.3.2) 等替代的检查可能排除符合假定的初始环境。
6247+**注释**
6248+这里的初始化可包含派生实现定义的其它初始化。
6249+虽然 NPLA1 标准库不作为接口保证提供这些源,这里的假定和 ISO C++ [using.headers] 对引入标准库头的程序位置的限制类似:语言实现能有效地假定源程序中引入标准库头的上下文,因此标准库中的名称具有预期的含义。
6250+
6251+@10.2.2 模块稳定性:
61326252 同 [RnRK] 的 get-module 的约定,提供模块绑定的环境依赖已知来源的绑定而确保稳定(@9.9.3) 。
61336253 对标准库模块,这一般表示其中的特性不能依赖用户程序运行时的非特定的当前环境,而可依赖从基础环境(@10.1) 及从基础环境派生的新环境(@9.9.3) 。
61346254 除非另行指定,模块中的特性依赖提供模块绑定的环境的生存期。例如,其中的合并子(@9.9.5) 可具有静态环境是上述环境的子对象的合并子的实现。
61356255 除非另行指定,标准库实现应确保其中的模块在程序的生存期中可用。这一般要求标准库实现在初始化后保存环境强引用(@6.11.1) 。
61366256 用户程序应自行保证加载的其它模块具有足够的生存期,以避免访问悬空引用(@9.4.3.2) 导致的未定义行为,特别是使用在环境中保留引用值的操作(@10.8.4) 的情形。
6137-另见可能破坏环境稳定性的操作(@10.8.6) 。
6257+另见可能破坏环境稳定性的操作(@10.8.7) 。
61386258
61396259 @10.3 库接口约定:
61406260 基础环境的特性在根环境(@10.1) 中提供,统称根环境特性,详见 NPLA1 根环境特性(@11) 。
@@ -6152,9 +6272,17 @@
61526272 操作的结果是对应的函数调用的求值结果,即函数值(@4.5.3.1) 。
61536273 根据操作的功能描述,对应的函数可能具有非正常的控制条件(@4.8) 。此时,函数调用不取得函数值,操作不具有结果。
61546274 除非另行指定,函数调用时具有的错误条件(@9.5.3) 是非正常的控制条件;其中,以异常(@9.5.2) 实现错误条件的情形具有异常条件(@4.8.1) 。
6275+特定的操作约定对应的函数是终止函数(@4.8.2) 或全函数(@4.8.2) ;这不适用于错误条件。
6276+特定的操作约定对应的函数具有复杂度要求。除非另行指定:
6277+这些复杂度是任意避免符合错误条件的方式调用时求值蕴含的渐进(asymptotic) 时间复杂度;
6278+若指定边界,明确的输入规模以哑变量(dummy) n 表示;
6279+指定复杂度的函数是终止函数。
61556280 本章其余各节适用 NPLA1 对象语言中的这些操作。
61566281 这些操作中的大部分具有特定的名称。这些名称符合函数名称约定(@10.7) ;其分类详见函数分类(@11.2) 。
61576282 其它操作不具有特定名称,可由上述操作间接地提供,如蕴含在某些操作涉及的函数值(@4.5.3) 中。
6283+**注释**
6284+渐进复杂度常以 O 记号指定上界。
6285+若函数调用总是取得值,指定复杂度的函数同时是全函数。
61586286
61596287 @10.3.2 库特性实现分类:
61606288 库特性分为基本的和派生的(同 NPLA1 实现的支持(@8.1) )。
@@ -6313,12 +6441,12 @@
63136441 NPLA1 中谓词是返回类型为 <boolean> 的函数。
63146442 除非另行指定,以下引入的对象语言中的谓词调用时没有副作用。
63156443 为提供库的描述,约定以下谓词的典型实例:
6316- 类型谓词(@4.7.5) :接受一个 <object> 参数,判断参数是特定的类型的对象。
6444+ 类型谓词(@4.7.6) :接受一个 <object> 参数,判断参数是特定的类型的对象。
63176445 调用这些类型谓词不引起错误。
63186446 仅当参数指定的对象具有对应类型时结果是 #t 。
63196447 除非另行指定,这些类型谓词忽略值类别的差异。
63206448 等价谓词(@4.1.4) :接受两个参数,判断参数是否属于同一个等价类。
6321-因为 <boolean> 是 <test> 的子类型,按照返回值为 <test> 的函数可在不严格要求 <boolean> 的上下文中起类似的作用,视为广义谓词(general predicate) 。
6449+因为 <boolean> 是 <test> 的子类型(@9.5.4.1) ,按照返回值为 <test> 的函数可在不严格要求 <boolean> 的上下文中起类似的作用,视为广义谓词(general predicate) 。
63226450 谓词是广义谓词的子类型。
63236451 **注释**
63246452 大多数上下文接受 <test> 而不严格要求 <boolean> 。这和 [RnRK] 不同(@9.2.2.2) 。
@@ -6378,15 +6506,15 @@
63786506 结尾的引用标记字符用于强调无法总是保证内存安全的危险操作(@1.5.5.2) 。
63796507 不带有引用标记字符结尾的操作通过避免保留引用值(@10.4.3) 提供一定的内存安全保证,而带有引用标记字符结尾的操作较容易引起注意。
63806508 这符合易预测性(@1.5.5.2.1) 。
6381-一个典型例子是在函数中返回标识符求值的表达式:
6382-标识符求值(@9.7.1) 后指称左值引用值(@7.8.2) ,这个引用值的有效性依赖合并子调用时创建的新环境(@9.9.3) 的可被访问;
6509+一个典型例子是在函数中返回标识符求值(@9.7.1) 的表达式:
6510+标识符求值后指称左值引用值(@7.8.2) ,这个引用值的有效性依赖合并子调用时创建的新环境(@9.9.3) 的可被访问;
63836511 这个环境在调用后通常被销毁,若使用带有引用标记字符结尾的操作关于对应的函数返回引用值的语义,在返回值中保留引用值,返回为悬空引用(@9.4.3.2) ,容易误用。
63846512 一般地,仅在明确需要引用值时使用引用标记字符结尾的操作,而避免返回悬空引用(这类似宿主语言函数的 auto 而非 auto&& 的返回类型,但宿主语言中返回非引用类型的表达式两者含义不同)。
63856513
63866514 @10.7.3.3 保留引用值(@10.4.3) 的约定:
63876515 可能直接保留引用值(@10.4.3) 的操作中,不带有引用标记字符的操作转发参数(@10.5.2) 。
63886516 可能直接保留引用值的操作包括容器构造器或访问器(@10.5.5) ,以及可能使对象中包含引用值(@9.4.4.1) 的修改操作(@9.8.3) 。
6389-以上操作是否确定地保留引用值在一些情形容易证明附加调用安全(@9.4.6.1) ,此时可放宽安全子集(@10.8.7) 的条件确保安全性;在此不作要求。
6517+以上操作是否确定地保留引用值在一些情形容易证明附加调用安全(@9.4.6.1) ,此时可放宽安全子集(@10.8.8) 的条件确保安全性;在此不作要求。
63906518 对构造器及部分修改操作区分引用标记字符结尾可强调一些非预期保留引用值的容易误用情形:
63916519 尽管总是返回非引用值,因转发参数而被保留的引用值(@10.4.3) 不会被返回值转换(@10.4.2) 或类似的操作影响,在构造的容器对象作为非引用值返回仍会保留引用值。
63926520 (对应宿主语言中,可有更显著的差异,如构造器对应的 std::tuple 的 std::make_tuple 和 std::forward_as_tuple 。)
@@ -6411,7 +6539,7 @@
64116539 函数名不带有标记字符结尾的访问器属于参数转发操作(@10.5.2) 和函数值转发操作(@10.5.4) 。
64126540
64136541 @10.7.4.3 可能使对象中包含引用值的修改操作(@9.8.3) :
6414-修改对象或对象的子对象(@9.8.2) 可无效化引用值(@6.8.5) 而影响内存安全。
6542+修改对象或对象的子对象(@9.8.2) 可无效化引用值(@6.4.4) 而影响内存安全。
64156543 对可能保留参数中的引用值(@9.4.4.2) 的操作,内存安全也依赖这些操作的指定修改后的值的内存安全性。
64166544 在判定内存安全(@9.4) 的意义上,以下操作的所有参数都可能是被保留的间接值(@9.4.4) :
64176545 简单赋值(simple assignment)(包含于赋值操作(@9.8.3.1) );
@@ -6489,8 +6617,8 @@
64896617 类似可提供以引用标记字符结尾变体的对应操作(@10.7.4.4) ,部分不带有引用标记字符的操作可能间接保留引用值(@10.4.3) 。
64906618 这包括由类型为合并子的参数(而非 <body> 或 <expressions> )决定是否保留引用值同时对其它参数进行转发的操作。
64916619
6492-@10.8 不安全操作(@9.4.5) 约定:
6493-除非另行指定,执行时包含以下操作的操作是不安全操作:
6620+@10.8 不安全操作约定:
6621+除非另行指定,执行时蕴含以下操作的操作是不安全操作(@9.4.5) :
64946622 以下不具有内存安全保证(@9.4) 的操作:
64956623 使用函数名称约定(@10.7) 的命名的带有后缀的操作(@10.7.4) ;
64966624 其它保留间接值的操作(@9.4.4) ;
@@ -6505,15 +6633,17 @@
65056633
65066634 @10.8.1 在返回值中保留引用值(@10.4.3) 的操作:
65076635 在返回值中保留引用值的操作可引起之后的不安全引用值访问(@9.4.2) 。
6508-例如,赋值操作(@9.8.3.1) 可无效化引用值(@9.8.6) 。
65096636 这包括按函数名称约定(@10.7) 具有引用标记字符结尾的操作。
65106637 直接保留引用值(@10.4.3) 操作可配合带有返回值转换(@6.4.6.4) 的操作,指定个别函数参数不再保留引用值。
6638+**注释**
6639+一些修改操作无效化(@9.8.6) 引用值。这些引用值若被保留且被访问,可引起未定义行为。
6640+不引起被绑定对象无效的修改操作不被视为不安全操作,即便它们无效化子对象的引用值。
65116641
65126642 @10.8.2 在返回值中保留环境引用的操作:
65136643 环境引用(@6.4.3.1) 被返回时,总是被保留(@10.4.4) 。
65146644 创建环境强引用的操作(@6.11.1) 是在返回值中保留环境引用的操作。
65156645 这些对象可能因为没有及时保存环境引用使环境对象和其中的绑定一并被销毁,而使引用值访问其中的对象的程序具有未定义行为:
6516-注意直接返回有效的环境弱引用的操作不引起环境失效,不在此列。
6646+注意直接返回有效的环境弱引用(@6.11.1) 的操作不引起环境失效,不在此列。
65176647
65186648 @10.8.3 在返回值中保留其它间接值的操作:
65196649 特定的支持强递归绑定(@9.7.3.1) 而在返回值中保留其它间接值,可能是无效的间接值(@9.4.3.3) 。
@@ -6523,21 +6653,37 @@
65236653 @10.8.4 在环境中保留环境引用的操作:
65246654 环境中的被绑定对象可具有环境引用子对象(@9.8.2) ,间接地在环境中保留环境引用。
65256655 这些操作使当前环境或参数指定的环境(而不是合并子调用时创建的新环境(@9.9.3) )中的变量绑定包含间接值,后者可能依赖合并子调用时创建的新环境。
6526-被绑定的对象中可能保留环境引用,而使用环境间接地保留对象中的引用。典型的例子是合并子对象的静态环境。
6527-使用这些操作时应总是注意被依赖的环境的可用性。若环境对象销毁,所有直接和间接依赖环境对象的间接值被无效化。这些间接值的不安全间接值访问(@9.4.2) 引起未定义行为。
6528-**注释** 创建合并子可在合并子中的环境中保留环境引用,如 $lambda/e(@11.4.3) 。
6529-
6530-@10.8.5 可能引入循环引用的操作:
6656+被绑定的对象中可能保留环境引用,而使用环境间接地保留对象中的引用。
6657+使用这些操作时应总是注意被依赖的环境的可用性。
6658+若环境对象销毁,所有直接和间接依赖环境对象的间接值被无效化(@9.8.6)。这些间接值的不安全间接值访问(@9.4.2) 引起未定义行为。
6659+**注释**
6660+绑定的对象中可能保留环境引用的典型的例子是合并子对象的静态环境。
6661+创建合并子可在合并子中的环境中保留环境引用,如 $lambda/e(@11.4.3) 。
6662+
6663+@10.8.5 无效化被绑定对象或环境引用的操作:
6664+特定的操作蕴含被绑定对象的存储期的结束而无效化它的引用值。
6665+若子对象引用已被绑定,这些引用值不需要通过其它不安全操作,而仅通过之后访问标识符求值(@9.7.1) 的结果(@7.8.2) 即可引起未定义行为。
6666+因为环境稳定性(@9.9.3.4) 要求,NPLA1 实现环境不提供这类绑定,因此这些操作不是不安全操作。
6667+但派生实现可能在语言实现中提供不满足环境稳定性的一等环境,其中对象的子对象引用被绑定为变量,且前者可能被修改。
6668+此时,这些操作可能允许无效化引用后的被引用对象被访问,成为不安全操作。
6669+类似地,无效化环境引用而无效化环境对象也可使其中包含的被绑定对象的引用无效化。
6670+但环境生存期(@9.9.3.5) 要求,除非作为不满足环境稳定性的环境的被绑定对象,NPLA1 实现环境不提供唯一的环境强引用(@6.11.1) 可被用户程序(@9.1) 修改而使环境对象被销毁。
6671+在这个前提下,要通过使环境引用作为子对象被修改而结束环境对象的生存期,首先要求通过在返回值中保留环境引用的操作(@10.8.2) 取得环境引用,得到包含环境引用作为子对象的对象,且保证只有这个对象保存环境强引用(@6.11.1) 。
6672+因此,若不存在其它不安全操作,即蕴含不存在在对象语言操作中无效化环境引用的情形。
6673+类似地,派生实现可提供不满足环境生存期中的销毁顺序的环境,而使用户无效化对应的环境对象。
6674+此时,这些操作可能允许无效化环境引用后的环境对象被访问,成为不安全操作。
6675+
6676+@10.8.6 可能引入循环引用的操作:
65316677 可能通过非引用的形式引入环境循环引用的操作可破坏环境的资源所有权。
65326678 自赋值(@9.8.3.1) 可能引入循环引用值。
65336679
6534-@10.8.6 可能破坏环境稳定性的操作:
6680+@10.8.7 可能破坏环境稳定性的操作:
65356681 通过引用值进行的修改操作(@9.8.3) 可因破坏环境稳定性(@9.9.3) 而引起扩展 NPLA 未定义行为(@9.1.4)(不一定违反内存安全)。
65366682 这包括以下可无效化对象包含的引用值而使可通过环境访问的某个子对象的同一性被改变,从而破坏环境稳定性(@9.9.3) 的操作:
65376683 对可能具有对象语言中可访问的子对象的对象的赋值操作(@9.8.3.1) ;
65386684 可修改被绑定对象(@6.11.1) 的操作(包括重绑定(@9.9.3.7) )。
65396685
6540-@10.8.7 安全操作子集:
6686+@10.8.8 安全操作子集:
65416687 作为对象语言安全性保证(@9.4.6) 的一部分,用户程序通过限制或避免依赖特定的不安全操作,在特定情形下可实现对象语言内存安全保证(@9.4) ,而不需要分析具体操作的语义:
65426688 不依赖操作命名(@10.7) 约定的带有后缀的操作;
65436689 若使用保留环境引用的操作,如在返回值中保留环境引用的操作(@10.8.2) 和在环境中保留环境引用的操作(@10.8.4) ,总是保存被依赖的环境以确保相关的环境对象及其中的被绑定对象(@6.11.1) 在间接访问对象时不被销毁;
@@ -6604,8 +6750,9 @@
66046750 在返回值中保留引用值的不安全操作(@10.8.1) 已被操作命名(@10.7) 归纳和函数分类(@11.2) 枚举,此处从略。
66056751 不安全操作中,在参数以外直接引入间接值的操作仅有以下的在返回值中保留引用值的不安全操作:
66066752 ref&(@11.3.4) 。
6607-附加调用安全(@9.4.6.1) 包括少量的在返回值中保留引用值的不安全操作(@10.8.1) 的调用。
6608-当前,这种操作包括 assign!(@11.4.1) ;隐藏环境排除可修改对象的引用通过冻结(@9.9.3.9) 环境保证而提供静态的证明。
6753+附加调用安全(@9.4.6.1) 包括在返回值中保留引用值的不安全操作(@10.8.1) 的调用。
6754+当前,这种操作包括 assign!(@11.4.1) 。
6755+隐藏环境排除可修改对象的引用,通过冻结(@9.9.3.9) 环境保证而提供静态的证明。
66096756
66106757 @11.1.1 在返回值中保留环境引用的操作(@10.8.2) :
66116758 包括基本操作(@11.3.7) :
@@ -6627,17 +6774,21 @@
66276774 $provide/let!
66286775 $provide!
66296776
6630-@11.1.4 可能引入循环引用的操作(@10.8.5) :
6777+@11.1.4 无效化被绑定对象的操作(@10.8.5) :
6778+当前不提供此类操作。
6779+这可包含直接移除变量绑定的操作。
6780+
6781+@11.1.5 可能引入循环引用的操作(@10.8.6) :
66316782 包括可能引入环境循环引用的基本操作(@11.3.7) :
66326783 copy-environment
66336784 lock-environment
66346785 包括可能引入环境循环引用的派生操作(@11.4.1) :
66356786 lock-current-environment
6636-包括可能自赋值(@10.8.5) 引入循环引用值的基本操作(@11.2.1.3) :
6787+包括可能自赋值(@10.8.6) 引入循环引用值的基本操作(@11.2.1.3) :
66376788 assign@!
66386789 assign%!
66396790
6640-@11.1.5 可能破坏环境稳定性的操作(@10.8.6) :
6791+@11.1.6 可能破坏环境稳定性的操作(@10.8.7) :
66416792 对可能具有对象语言中可访问的子对象的对象的赋值操作(@9.8.3.1) 包括:
66426793 简单赋值(@10.7.4.3) 。
66436794 可直接修改被绑定对象(@6.11.1) 的操作包括:
@@ -6817,39 +6968,48 @@
68176968 操作:
68186969 eq? <object1> <object2> :判断参数同一。
68196970 当且仅当两个参数是指定同一对象时,比较结果是 #t 。
6971+eq? 的复杂度是 O(1) 。
68206972 eql? <object1> <object2> :判断表示参数的项的值数据成员(@6.2) 相等。
68216973 当且仅当表示两个参数是的项的值数据成员相等时,比较结果是 #t 。
6822-值数据成员相等蕴含参数的动态类型(@4.7) 相等。
6974+值数据成员相等蕴含参数的动态类型(@4.7) 相同(@4.7.2) 。
68236975 eqr? <object1> <object2> :判断表示参数的项的数据成员同一。
68246976 当且仅当表示两个参数是的项的值数据成员指定宿主语言中的同一对象(即引用相等)时,比较结果是 #t 。
68256977 eqv? <object1> <object2> :判断非枝节点(@6.2.1) 表示的值相等。
6826-当表示参数的项都是枝节点时,同 eq? ;
6978+若参数是引用值,则被比较的值是它的被引用对象(@4.2.3) 。
6979+当表示值的项都是枝节点时,同 eq? ;
6980+否则,若这两个参数的类型不同(@4.7.2) ,则结果是 #f ;
68276981 否则,若这两个参数的 eql? 比较结果是 #t ,则结果是 #t 。
68286982 若两个参数的 eqv? 比较结果是 #f ,则这两个参数以 eq? 比较结果总是 #f 。
6829-不等价(@4.5.3.2) 的函数的 eqv? 比较结果是 #f 。
6983+除非互操作(@1.2.3) (参见以下描述)或派生实现另行指定,不等价(@4.5.3.2) 的函数的 eqv? 比较结果是 #f 。
68306984 除以上规则确定的结果外,eqv? 对合并子或列表的比较结果未指定。
6831-在互操作(@1.2.3) 的意义上,当前其 eqv? 定义的合并子的相等性由过程相等性(@8.4.5.5) 或不影响可观察行为的其它宿主实现提供的 == 操作通过和 eql? 比较相同的方式确定。
6832-通常,等价谓词比较的求值应保证能终止且对非列表项和 n 个子项的列表分别具有 O(1) 和 O(n) 平摊复杂度。这是依赖数据结构实现的细节;语言不需要约束这个性质。
6985+在互操作的意义上,当前 eqv? 定义的合并子的相等性由过程相等性(@8.4.5.5) 或不影响可观察行为的其它宿主实现提供的 == 操作通过和 eql? 比较相同的方式确定。
6986+除非另行指定,具有本文档引入的类型且不涉及互操作意义上用户自定义值的比较的操作数使用以上 eq? 以外的谓词比较的求值应保证能终止。
68336987 **原理**
6834-开放类型映射(@5.5) 的类型系统(@4.7) 通常要求避免依赖良序和良基的理论(@4.1.4) ,以避免对现有类型系统的扩展时需要修改已有的类型的相关操作。
6835-不需要依赖序的等价谓词可为名义类型(@4.7.1) 提供直接的支持。
6988+除任何其它类型都可作为 <object> 的子类型(@9.2.2.2) ,开放类型映射(@5.5) 的类型系统(@4.7.1) 通常要求避免依赖 <object> 上的其它的良序和良基的理论(@4.1.4) ,以避免对现有类型系统的扩展时需要修改已有的类型的相关操作。
6989+不需要依赖序的等价谓词可为名义类型(@4.7.2) 提供直接的支持。
68366990 NPLA1 提供默认相等为抽象相等(@4.1.4) ,对任意的值适用。
68376991 NPLA1 还提供对一等对象保证结果有意义的引用相等(@4.1.4) 操作。非一等实体的引用相等关系未指定。
68386992 当前 NPLA1 不支持 EGAL(@4.1.4) ,因为 EGAL 要求存在分辨任意对象的值是否可被修改的元数据。
68396993 因为对应等价的不变性(@1.2.1.1) 关系不具有唯一性(@4.1.4.3) ,且可能允许不唯一的方式产生副作用(如缓存),和 Kernel 不同,不以基本操作提供 equal? 对任意对象提供一般的相等操作。
68406994 未指定 eq? 的比较结果可允许实现复用存储右值的驻留(@9.8.4) 对象。
68416995 eql? 实际比较宿主值的相等。允许 eqv? 和 eql? 的不同可允许一对多的类型映射(@5.5.1) 下比较对象语言的值的相等。(而多对一的类型映射 eql? 和 eqv? 可一致地比较。)
6842-但是,当前实现中,大多数一对多映射的类型(如环境)都没有引起使 eql? 和 eqv? 不同的比较实现,因为不同宿主值类型的对象具有足够显著的差异,在大多数上下文不通过一些具有不可忽略开销的转换机制(如锁定环境弱引用转换为环境强引用),无法直接相互替换而保证行为差异可被忽略,因此逻辑上不适合定义为相等的。
6996+但是,当前实现中,大多数一对多映射的类型(如环境)都没有引起使 eql? 和 eqv? 不同的比较实现,因为不同宿主值类型的对象具有足够显著的差异,在大多数上下文不通过一些具有不可忽略开销的转换机制(如锁定环境弱引用(@6.11.1) 转换为环境强引用(@11.3.7) ),无法直接相互替换而保证行为差异可被忽略,因此逻辑上不适合定义为相等的。
68436997 而基于性能等理由,等其它一对多映射的类型(特别是可能基于宿主类型的值的子集的,如 NPLA 数值类型(@6.14.1) 中的 <integer> )的值的比较也没有特别的处理,而引起 eqv? 和 eql? 的不同。
68446998 这些类型可能需要其它针对特定类型的等价谓词(如 =?(@12.2) )进行相等性的比较。
6999+类似 [RnRS] ,不同类型决定 eqv? 的结果是 #f ,但此处类型相同的含义不通过类型分区(@9.8.7) 定义。
7000+类似 [RnRS] ,行为不等价的函数的 eqv? 结果原则上应为 #f ,但这种等价性一般不可证明而无法保证,特别在关于语言实现以外的调用上。
7001+为支持互操作使用本机实现(@5.3) 及避免限制合并子的子类型的开放性(@1.5.3.6) ,允许这些实现另行指定规则,假定引起程序可观察行为差异的函数调用调用名义等价。
68457002 **注释**
7003+通常,等价谓词比较的求值应保证能终止且对非列表项和 n 个子项的列表分别具有 O(1) 和 O(n) 平摊复杂度。这是依赖数据结构实现的细节;语言不需要约束这个性质。
68467004 实现中,值数据成员的相等由 ValueObject 的 == 操作定义。这对应 C++ 意义上的对象相等。
6847-对仅由值数据成员决定值的表示的情形(@6.10.7) ,动态类型相等即值数据成员的目标类型相等。
7005+对仅由值数据成员决定值的表示的情形(@6.10.7) ,动态类型相等(@4.7.2) 即值数据成员的目标类型相等。
68487006 这种情形使用 eql? 和 eqv? 相同,即仅比较宿主值 TermNode 的值数据成员(@6.2) 相等。
6849-由 @6.10.10 ,列表和非列表之间的 eql? 结果总是为 #f ,列表之间的 eql? 的比较结果总是 #t 。
7007+由正规表示分类(@6.10.10) :
7008+ 列表和非列表之间的 eql? 结果总是为 #f ,列表之间的 eql? 的比较结果总是 #t 。
7009+ 所有列表在 TermNode 的值数据成员中的表示都一致,在 eqr? 等价关系下视为相同的对象。
68507010 使用 eqr? 判断宿主值 TermNode 的值数据成员表示的左值(@5.8.1) 的标识,当且仅当为操作数比较同一个对象时比较结果是 #t 。
6851-由 @6.10.10 ,所有列表在 TermNode 的值数据成员中的表示都一致,在 eqr? 等价关系下视为相同的对象。
68527011 关于合并子相等,另见 TCO 实现(@7.11.5) 中关于函数右值去重(@7.10.7.1) 的说明。
7012+因为枝节点和其它节点不共享除公共超类型(@4.7.7) <object> 外具有相同类型的表示,任意一个被比较的值是枝节点时,eqv? 的结果也同 eq? 。
68537013
68547014 @11.3.3 控制:
68557015 $if <test> <consequent> <alternate> :条件分支,按条件成立与否返回 <consequent> 或 <alternative> 之一,可能是引用值。
@@ -6922,7 +7082,7 @@
69227082 assign@! <reference> <object> :赋值(@9.8.3.1) 被引用的对象为指定对象的值,且 <object> 不隐含左值到右值转换且不被折叠。
69237083 检查 <reference> 是可修改的左值。
69247084 赋值对象直接修改(@9.8.3) 被引用的对象,但不无效化(@9.8.6) 参数指定的引用。
6925-Scheme 的 set! 在 SRFI-17 提供具有类似作用的支持,但第一操作数限于 set! 且为特定的过程调用;Kernel 没有类似的操作。
7085+**注释** 被赋值替换的子对象的引用可被无效化。Scheme 的 set! 在 SRFI-17 提供具有类似作用的支持,但第一操作数限于 set! 且为特定的过程调用;Kernel 没有类似的操作。
69267086
69277087 @11.3.5 列表:
69287088 cons <object> <list> :构造两个元素的列表。
@@ -6973,7 +7133,7 @@
69737133 make-environment <environment>... :创建以参数为父环境的环境。
69747134 和 Kernel 不同,除对象类型外,没有对列表和绑定的附加检查。
69757135 结果是新创建的环境,是环境强引用,具有宿主值类型 shared_ptr<Environment> 。
6976-weaken-environment <environment> :使用环境强引用创建环境弱引用。
7136+weaken-environment <environment> :使用环境强引用创建环境弱引用(@6.11.1) 。
69777137 检查参数类型是环境强引用,若失败则引起类型错误(@9.5.4.1) 。
69787138 **原理** 因为 NPLA1 需要精确控制所有权而不依赖 GC(@5.2) ,这可用于派生实现某些操作(如 $sequence(@11.4.1) 必要的)。
69797139 **注释** 实现检查操作数的宿主值类型(@6.11.1.1) 是 shared_ptr<Environment> ,结果的宿主类型 NPL::EnvironmentReference 。
@@ -7077,7 +7237,7 @@
70777237 本节约定以下求值得到的操作数:
70787238 <box> :箱(@4.2.3.5.3) 。
70797239 操作:
7080-() get-current-environment :取当前环境:取当前环境的环境弱引用。
7240+() get-current-environment :取当前环境:取当前环境的弱引用(@6.11.1) 。
70817241 结果具有宿主值类型 NPL::EnvironmentReference 。派生需要非派生实现的 vau/e 。
70827242 () lock-current-environment :锁定当前环境:取当前环境的环境强引用。
70837243 结果具有宿主值类型 shared_ptr<Environment> 。
@@ -7098,9 +7258,10 @@
70987258 forward! <object> :转发(@10.5.4) 可能是引用的值(@11.2.2.4) 。
70997259 转移可修改的右值操作数(包括消亡值和临时对象)。
71007260 其中,需转移时,使用使用项的转移(@6.3.2) 。这和对象的转移(@5.8.2.3) 不同,不保证调用宿主实现的转移构造函数。
7101-本机实现使用 NPL::MoveRValueToForward(@6.9.5.3) 可简化操作。
7102-这个函数类似宿主语言以对象类型参数和推断的函数参数类型作为模板参数调用 std::forward ,但若需转移,直接转移而非如 expire(@11.3.2) 返回指定结果是消亡值(@5.8.1) 的唯一引用(@6.2.2) 。
7103-和宿主语言不同,直接转移允许区分消亡值和纯右值,同等地作为一等对象(如作为列表的元素)。
7261+**原理** 和宿主语言不同,直接转移允许区分消亡值和纯右值,同等地作为一等对象(如作为列表的元素)。
7262+**注释** 被转发的值若是形式参数树(@7.7.3) 中的变量,一般应以带有标记字符 & 的形式绑定(@7.7.3.5) ;否则,转发的不是对应的实际参数,而可能是其按值绑定的副本。
7263+**注释** 本机实现使用 NPL::MoveRValueToForward(@6.9.5.3) 可简化操作。
7264+**注释** 这个函数类似宿主语言以对象类型参数和推断的函数参数类型作为模板参数调用 std::forward ,但若需转移,直接转移而非如 expire(@11.3.2) 返回指定结果是消亡值(@5.8.1) 的唯一引用(@6.2.2) 。
71047265 list% <object>... :同 list ,但每个参数都不隐含左值到右值转换,在结果中保留参数的引用值(@10.7.3.3) 。
71057266 rlist <list> :转换参数为引用列表元素的列表。
71067267 若参数是左值,则结果是参数的元素的左值引用值构成的列表;否则,结果同 idv 。
@@ -7135,10 +7296,9 @@
71357296 collapse <object> :折叠可能是引用的值。
71367297 forward <object> :转发可能是引用的非临时对象的值。
71377298 同 forward! ,但对可修改的临时对象操作数,使用复制代替转移。
7138-按在所在的环境中解析的操作数的类型可选地提升项(@6.9.5) 作为结果,其作用 id 或 idv 之一。
7139-被转发的值若是形式参数树(@7.7.3) 中的变量,一般应以带有标记字符 & 的形式绑定(@7.7.3.5) ;否则,转发的不是对应的实际参数,而可能是其按值绑定的副本。
7140-转移(而不是复制)可修改的右值操作数。注意若右值操作数不可修改(如本机实现引入带有 TermTags::Nonmodifying 标签(@6.2.2) 的引用操作数),复制不可复制构造的宿主对象会失败。
7141-本机实现使用 NPL::MoveRValueToReturn(@6.9.5.3) 可简化操作。
7299+**注释** 按在所在的环境中解析的操作数的类型可选地提升项(@6.9.5) 作为结果,其作用 id 或 idv 之一。
7300+**注释** 转移(而不是复制)可修改的右值操作数。注意若右值操作数不可修改(如本机实现引入带有 TermTags::Nonmodifying 标签(@6.2.2) 的引用操作数),复制不可复制构造的宿主对象会失败。
7301+**注释** 本机实现使用 NPL::MoveRValueToReturn(@6.9.5.3) 可简化操作。
71427302 assign%! <reference> <object> :同 assign@!(@11.3.4) ,但 <object> 是引用值时赋值的源操作数是 <object> 折叠后的值。
71437303 assign! <reference> <object> :同 assign%! ,但 <object> 隐含左值到右值转换。
71447304 **注释** 因为左值到右值转换,即便 <object> 指定的值来自 <reference> ,也可赋值而不因此引起未定义行为。
@@ -7198,6 +7358,7 @@
71987358 check-environment <object> :检查环境(@9.9.3) 。
71997359 若参数是 <environment> 则检查通过,结果是转发的参数;
72007360 否则,引发错误对象(@9.5.2) 。
7361+**注释** 当前实现中其它要求 <enviornment> 参数的操作中类型检查(@9.5.4.1) 失败和 check-environment 失败的行为一致。
72017362 check-parent <object> :检查作为环境的父环境(@9.9.3) 的对象。
72027363 若参数是可以作为合并子环境的 <parent> 则检查通过,结果是转发的参数;否则,引发错误对象。
72037364 检查环境通过的条件同创建合并子时的检查(@11.3.8) 。
@@ -7216,7 +7377,7 @@
72167377 $and <test>... :逻辑与。
72177378 顺序短路求值。操作数为空时结果是 #t ;参数求值存在 #f 时结果是 #f ;否则结果是最后一个参数的值。
72187379 结果保留引用值。
7219-**注释** 和 Kernel 的 $and? 不同,不检查类型,也不保证结果类型是 <boolean>(所以命名不以 ? 结尾(@10.7.2.1) )。这同时允许直接的 PTC 实现。
7380+**注释** 和 Kernel 的 $and? 不同,不检查类型,也不保证结果类型是 <boolean>(所以命名不以 ? 结尾(@10.7.2.1) )。和 [RnRK] 中的原理描述的不同,这同时允许直接的满足对 PTC 要求(@9.7.4.1) 的派生实现。
72207381 $or <test>... :逻辑或。
72217382 顺序短路求值。操作数为空时结果是 #f ,参数求值存在不是 #f 的值时结果是第一个这样的值;否则结果是 #t 。
72227383 结果保留引用值。
@@ -7278,7 +7439,7 @@
72787439 注意以上 $let 等函数的 <body> 形式和 Kernel 不同。
72797440 derive-current-environment <environment>... :创建当前环境的派生环境:以参数指定的环境和当前环境为父环境的空环境。
72807441 当前环境以外的父环境顺序同参数顺序。当前环境是最后一个父环境。
7281-() make-standard-environment :创建标准环境(standard environment) :以基础环境(@10.1) 为父环境的空环境。
7442+() make-standard-environment :创建新(@9.9.3.2) 标准环境(standard environment) :以基础环境(@10.1) 为父环境的空环境。
72827443 同 Kernel 的 make-standard-kernel-environment ,但创建的环境基于 NPLA1 基础环境。
72837444 **注释** 标准环境同 [RnRK] 约定的定义。
72847445 derive-environment <environment>... :创建基础环境的派生环境:以参数指定的环境和基础环境为父环境的空环境。
@@ -7293,7 +7454,7 @@
72937454 使用 make-environment(@11.3.7) 而不是 $let/e(@11.4.1) 等绑定构造实现。
72947455 $bindings->environment <binding>... :转换绑定列表为没有父环境的具有这些绑定的环境。
72957456 **注释** 类似 Kernel 的同名操作,但因为要求对内部父环境环境所有权,使用 $binding/p->environment 而不是 $let/e 等绑定构造派生。
7296-symbols->imports <symbol>... :转换符号列表为未求值的适合初始化符号导入列表的初值符列表。
7457+symbols->imports <symbol>... :转换符号列表为未求值的适合初始化符号导入(@10.1.2) 列表的初值符列表。
72977458 结果是包含同 desigil(@11.3.6) 的方式移除标记字符(@9.2.2.4) 后的参数作为间接子项的列表。
72987459 求值这个列表,结果是同 forward!(@11.4.1) 的方式转发每个符号的列表,其元素顺序和 <symbols>... 中的值的顺序对应。
72997460 结果的结构和使用满足以下约定(可允许更有效的本机实现):
@@ -7309,13 +7470,13 @@
73097470 蕴含 $let <bindings> <body> ,在求值 <body> 后以结果作为操作数绑定到 <symbols> 的符号。
73107471 <symbols> 应能被作为 <definiend> 使用。
73117472 结果是对这些绑定具有所有权的环境强引用。
7312-需要导入符号(即 <symbols>... 具有至少一个实际参数)时,以同 symbols->imports(@11.4.1) 的方式确定初值符。其中,等效的 symbols->imports 的求值次数未指定。
7473+需要导入符号(@10.1.2) ,即 <symbols>... 具有至少一个实际参数时,以同 symbols->imports(@11.4.1) 的方式确定初值符。其中,等效的 symbols->imports 的求值次数未指定。
73137474 **注释** 绑定后的符号可通过作为 vau 抽象的父环境(@9.9.3.5) 等形式依赖这个环境,因此用户需适当保存返回值使其生存期(@9.9.3.5) 覆盖在被使用的绑定符号指称的对象生存期。
73147475 $provide! <symbols> <body> :在当前环境中提供绑定。
73157476 同 $provide/let! 但不指定单独的 <bindings> 。
73167477 作用同 <bindings> 为空列表的 $provide/let! 。
73177478 结果是创建的环境的强引用。
7318-且需要导入符号时,以同 symbols->imports 的方式确定初值符。其中,等效的 symbols->imports 的求值次数未指定。
7479+需要导入符号时,以同 symbols->imports 的方式确定初值符。其中,等效的 symbols->imports 的求值次数未指定。
73197480 **注释** 类似 Kernel 的同名操作,但结果是创建的环境的强引用,且确定初值符的方式被显式要求。
73207481 仅当 <symbols> 类型检查通过时求值 <body> 。
73217482 检查当前环境可修改(@9.9.3.9) 失败时的副作用和以上任一等效求值 symbols->imports 应用子的结果可能具有的副作用非决定性有序(@4.4.3) 。
@@ -7336,8 +7497,13 @@
73367497 作为传递操作,保留引用值。
73377498 以上 4 个函数除引用标记字符(@10.7) 对应处理引用值的差异外,功能和使用方式对应类似 SRFI-111 的 3 个过程 box 、box? 和 unbox 。
73387499 类型分区(@9.8.7) 使 box? 对 <list> 类型的参数的结果总是 #f 。若没有这个限制,用以下 <list> 的相关操作可整体替换进行功能等价的代替:
7500+**原理**
7501+[RnRK] 的 $and? 和 $or? 的实现使用 apply 和 wrap ,这没有必要:
7502+ 按 [RnRK] 的 apply 的原理,这种对任意合并子适用的操作 combine 容易实现且干扰意图的理解。
7503+ 对 NPLA1 的 apply ,还保证在第二参数是空列表时,被求值的结果仍是函数合并(而不是单独的函数),但这在 NPLA1 的 $and 和 $or 中不必要,因为空列表的情形应被单独处理。
7504+ 对派生实现,apply 通常比 eval% 更低效(因为包含了无用的检查和更多的非本机实现)。
73397505 **注释**
7340-用 list 、list% 和 first 可代替 box 、box% 和 unbox 。
7506+不考虑封装性时,用 list 、list% 和 first 可代替 box 、box% 和 unbox 。
73417507 和 http://community.schemewiki.org/?scheme-faq-language 关于装箱的描述不同,这样的代替不一定保证有更好的性能。
73427508 以上这些函数可使用 make-encapsulation-type(@11.3.10) 实现。
73437509 和 Scheme 等不同,箱具有被装箱对象的所有权,因此使用 box% 和 unbox 时,需注意保存被构造的箱或被箱中引用值引用的对象。
@@ -7494,12 +7660,12 @@
74947660 和数值操作约定不同,幂等操作要求超过一次应用时,结果和参数的宿主类型也相同。
74957661
74967662 @12.2.1 除法约定:
7497-二元除法或者取余数的操作中,<number1> 是被除数,<number2> 是除数。
7663+二元除法或者取余数的操作中,第一个参数是被除数,第二个参数是除数。
74987664 当除数是不精确数 0 时:
7499-若被除数是非零有限数值或无限大值,则商的符号同 <number1> ;
7665+若被除数是非零有限数值或无限大值,则商的符号同被除数的符号;
75007666 否则,商的符号未指定。
75017667 当被除数是不精确数时,若除数是精确数 0 ,则结果除符号外同除数是不精确数 0 的情形。
7502-同时计算商和余数的函数的结果的结果是商和余数构成的列表。
7668+同时计算商和余数的操作的结果是商和余数构成的列表。
75037669 **原理**
75047670 Scheme 方言及实现中普遍存在不同的除零错误的条件。
75057671 [R5RS] 的过程 / 除以零的条件没有被明确约定。
@@ -7583,17 +7749,26 @@
75837749 类似 klisp 的同名操作。类似地,不使用 klisp 的 find-required-filename 机制,直接以宿主的运行环境为基准使用路径。
75847750 和 klisp 不同,在尾上下文中求值被加载后读取的对象,并以其求值结果作为表达式的求值结果。
75857751 [Shu09] 缺少 load 的详细描述而仅有标题。
7586-**注释** 参数一般指定视为外部翻译单元的文件名。
7752+get-module <string> <environment>? :创建标准环境并在其中加载模块。
7753+创建新(@9.9.3.2) 标准环境并以这个环境为当前环境加载 <string> 指定的翻译单元作为源的模块。
7754+若第二参数非空,则在加载前首先设置创建的环境中的 module-parameters 变量为第二参数的值。
7755+加载完成后取得调用结果。
7756+结果是先前被创建的标准环境。
7757+同 klisp 和 [Shu09] 中的同名操作。
7758+**原理**
7759+和 [R7RS] 不同,load 不支持指定环境,而总是使用当前环境。
7760+类似 Kernel ,当前环境可通过不同机制改变,而不需由 load 提供特设的支持。例如,可使用 eval(@11.3.7) 指定蕴含 load 的调用的求值使用的环境。
7761+load 的语义隐含从外部来源取得求值构造后在当前环境求值,其中的求值明确允许隐含副作用。这和其它一些语言的类似命名的功能(如 Lua 的 loadfile )不同。在此,load 的求值被视为初始化加载的模块过程中的一部分。
7762+因为当前不提供取得求值构造的读取(read) 等函数,不要求 load 具有非本机派生实现。并且,取得求值构造可能有其它方式,如从二进制映像映射(map) 到内部表示等替代,这些实现通常不应被要求为总是具有本机派生实现而降低实现质量。
75877763 **注释**
75887764 关于 newline 、put 和 puts :
75897765 实现使用 REPLContext::GetOutputStreamRef(@7.8.1) 。
75907766 在使用前,一般应初始化 REPLContext::OutputStreamPtr(@7.8.1) 指向特定的 std::ostream 对象;否则,总是失败,引起错误(@9.5.1) 。
7591-关于 load :
7767+关于 load 和 get-module :
7768+参数一般指定视为外部翻译单元的文件名。
75927769 http://klisp.org/docs/Ports.html#Ports 的 load 描述中求值环境有误:
7593-按 [Shu09] 一致的描述和 klisp 的实际实现,调用 load 时应在当前环境求值,而不同于 [Shu09] 的 get-module 中描述的使用创建的环境(即新环境(@9.9.3) )进行求值,否则使用 [Shu09] 的 get-module 的派生不能实现 klisp 和 [Shu09] 中描述的 get-module 的预期语义。
7594-和 [R7RS] 不同,load 不支持指定环境,而总是使用当前环境。类似 Kernel ,当前环境可通过不同机制改变,而不需由 load 提供特设的支持。
7595-load 的语义隐含从外部来源取得求值构造后在当前环境求值,其中的求值明确允许隐含副作用。这和其它一些语言的类似命名的功能(如 Lua 的 loadfile )不同。在此,load 的求值被视为初始化加载的模块过程中的一部分。
7596-因为当前不提供取得求值构造的读取(read) 等函数,不要求 load 具有非本机派生实现。并且,取得求值构造可能有其它方式,如从二进制映像映射(map) 到内部表示等替代,这些实现通常不应被要求为总是具有本机派生实现而降低实现质量。
7770+ 按 [Shu09] 一致的描述和 klisp 的实际实现,调用 load 时应在当前环境求值,而不同于 [Shu09] 的 get-module 中描述的使用创建的新标准环境进行求值。
7771+ 否则,使用 [Shu09] 的 get-module 的派生不能实现 klisp 和 [Shu09] 中描述的 get-module 的预期语义。
75977772
75987773 @12.5 系统:
75997774 通过初始化基础上下文后调用 Forms::LoadModule_std_system(@8.5.2) 初始化,默认加载为根环境下的 std.system 环境。
@@ -7637,30 +7812,36 @@
76377812 操作:
76387813 registered-requirement? <string> :判断参数是否是已在本模块注册的需求字符串。
76397814 register-requirement! <string> :在本模块注册参数为需求字符串。
7815+若已被注册,则引起错误;否则,在内部创建新标准环境。
7816+结果是创建的环境的弱引用(@6.11.1) 。
76407817 unregister-requirement! <string> :在本模块注册解除参数为需求字符串。
76417818 find-requirement-filename <string> :查找需求字符串对应的文件名。
7642-在需求字符串模板中顺序地搜索字符串,返回匹配字符串的结果。若不存在这样的结果,则引起错误。
7819+在需求字符串模板中顺序地搜索字符串。若不存在这样的结果,则引起错误;否则,结果是匹配字符串的搜索结果。
76437820 判断需求字符串模板中的每一个字符串是否能被需求字符串匹配时,首先替换字符串中的单字符子串 "?" 为需求字符串,取得替换结果,再判断它是否为可读的文件的文件名。
76447821 替换字符串时,每一个子串被同时一次替换;不对替换结果进一步递归地替换。
7645-require <string> :按需加载需求字符串对应的模块。
7646-若参数指定的需求字符串没有注册,则注册需求字符串并加载同调用 find-requirement-filename 等价的方式搜索得到的结果;否则没有作用。
7822+require <string> :按需在新(@9.9.3.2) 标准环境加载需求字符串对应的模块。
7823+若参数指定的需求字符串没有注册,则注册需求字符串并加载同调用 find-requirement-filename 等价的方式搜索得到的结果,并保存加载的结果;否则不进行加载。
7824+结果是保存的加载的结果。
76477825 **原理**
76487826 为避免名称污染等问题,不提供显式指定命名环境的模块创建操作,如 Lua 5.1 的 module 函数(http://www.lua.org/manual/5.1/manual.html#pdf-module )。
7649-具体问题参见 http://lua-users.org/wiki/LuaModuleFunctionCritiqued 。
7827+具体问题参见 http://lua-users.org/wiki/LuaModuleFunctionCritiqued 。
7828+不提供访问创建的环境的操作,以避免污染外部的访问。若需公开其中的变量绑定,可使用返回或模块参数。
7829+操作的设计同 klisp 的 ports 模块中的同名操作,但有以下不同:
7830+同时保存创建的环境,以避免因程序没有保存环境引用而无效化(@9.8.6) ,使访问变量绑定的程序具有未定义行为;
7831+结果是加载结果(同 std.io 模块中的 load(@12.4) )而不是 #inert 。
76507832 **注释**
76517833 当前实现依赖可用的 std.strings(@12.3) 、std.io(@12.4) 和 std.system(@12.5) 环境。
7652-操作的设计同 klisp 的 ports 模块中的同名操作。
7653-当前实现中,加载操作同 std.io 模块中的 load(@12.4) 。
76547834 通过不经过本模块的操作、重复字符串模板的重复项、符号链接和字符串大小写不敏感的文件名等可能绕过本模块的注册机制而重复加载同一个外部文件。本模块的操作不对这些情形进行任何检查。
76557835
76567836 @13 SHBuild 实现环境:
76577837 SHBuild 实现环境是派生 NPLA1 参考实现扩展环境(@12) 的用于 SHBuild 和外部脚本的构建的初始环境。
7838+SHBuild 实现环境的初始化(@10.1.1) 可加载模块(@10.2) ,这些模块的加载适用和标准库实现相同的要求和假定(@10.2.1) 。
76587839 关于 SHBuild 的调用方式说明,详见 https://frankhb.github.io/YSLib-book/SHBuild.zh-CN.html 。
76597840 操作约定同 @10 ,包括 @10.6 和 @10.7 ;但除此之外,遵循 NPLA1 用户程序(@9.1) 的约定。
76607841
76617842 @13.1 NPL::Dependency 派生特性:
76627843 SHBuild 实现环境由 NPL::Dependency 提供初始的派生特性。
7663-在基础上下文(@8.5.2) 上,SHBuild 实现环境通过切换新环境(@9.9.3) 并调用 Forms::LoadModule_SHBuild(@8.5.2) 初始化。
7844+在基础上下文(@8.5.2) 上,SHBuild 实现环境通过切换新环境(@9.9.3.2) 并调用 Forms::LoadModule_SHBuild(@8.5.2) 初始化。
76647845 SHBuild 的基础环境(@10.1) 的子环境中提供对象引用 env_SHBuild_ ,其中包含这些特性。
76657846 部分 SHBuild 互操作特性(@13.1.1) 在 YFramework 的 NPL::Dependency 实现中提供。
76667847 此外,NPLA1 在默认实现的 SHBuild_BaseTerminalHook_(@13.1.1) 的实现被覆盖,以使 SHBuild_EchoVar(@13.1.1) 等和 SHBuild 的其它输出兼容。
Show on old repository browser