The YSLib project - main repository
Revisión | 30522accfd38ceb1fcff035656ca08694af1b85f (tree) |
---|---|
Tiempo | 2022-04-05 17:56:05 |
Autor | FrankHB <frankhb1989@gmai...> |
Commiter | FrankHB |
更新主分支版本: build 942 rev 10 。
@@ -407,10 +407,14 @@ | ||
407 | 407 | $def! prom_C_CXXFLAGS_PIC |
408 | 408 | $lazy-env-val "C_CXXFLAGS_PIC" |
409 | 409 | $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"; | |
410 | 413 | $def! prom_C_CXXFLAGS_COMMON |
411 | 414 | $lazy-env-val "C_CXXFLAGS_COMMON" cons-cmd "-pipe" |
412 | 415 | (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); | |
414 | 418 | $def! prom_C_CXXFLAGS_OPT_LV $lazy-env-val "C_CXXFLAGS_OPT_LV" "-O3"; |
415 | 419 | $def! prom_C_CXXFLAGS_WARNING $lazy-env-val "C_CXXFLAGS_WARNING" |
416 | 420 | cons-cmd "-Wall" "-Wcast-align" "-Wdeprecated" |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - © 2013-2016, 2018-2021 FrankHB. | |
2 | + © 2013-2016, 2018-2022 FrankHB. | |
3 | 3 | |
4 | 4 | This file is part of the YSLib project, and may only be used, |
5 | 5 | modified, and distributed under the terms of the YSLib project |
@@ -10,14 +10,14 @@ | ||
10 | 10 | |
11 | 11 | /*! \file apply.hpp |
12 | 12 | \ingroup YStandardEx |
13 | -\brief 元组应用操作。 | |
14 | -\version r994 | |
13 | +\brief 元组和函数应用操作。 | |
14 | +\version r1102 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 333 |
17 | 17 | \par 创建时间: |
18 | 18 | 2019-01-11 19:43:23 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2021-12-26 12:32 +0800 | |
20 | + 2022-03-21 12:08 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -28,9 +28,8 @@ | ||
28 | 28 | #ifndef YB_INC_ystdex_apply_hpp_ |
29 | 29 | #define YB_INC_ystdex_apply_hpp_ 1 |
30 | 30 | |
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; | |
34 | 33 | #include <tuple> // for <tuple>, __cpp_lib_apply, __cpp_lib_tuple_element_t, |
35 | 34 | // __cpp_lib_make_from_tuple, std::tuple, std::get, std::forward_as_tuple, |
36 | 35 | // std::tuple_size, std::make_from_tuple; |
@@ -195,6 +194,7 @@ | ||
195 | 194 | \brief 应用函数对象和参数元组。 |
196 | 195 | \tparam _func 函数对象及其引用类型。 |
197 | 196 | \see ISO C++17 [tuple.apply]/1 。 |
197 | +\see WG21 P0220R1 。 | |
198 | 198 | \since build 547 |
199 | 199 | */ |
200 | 200 | template<typename _func, class _tTuple> |
@@ -231,6 +231,120 @@ | ||
231 | 231 | |
232 | 232 | } // inline namespace cpp2017; |
233 | 233 | |
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 | + | |
234 | 348 | } // namespace ystdex; |
235 | 349 | |
236 | 350 | #endif |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - © 2013-2016, 2018-2021 FrankHB. | |
2 | + © 2013-2016, 2018-2022 FrankHB. | |
3 | 3 | |
4 | 4 | This file is part of the YSLib project, and may only be used, |
5 | 5 | modified, and distributed under the terms of the YSLib project |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file cache.hpp |
12 | 12 | \ingroup YStandardEx |
13 | 13 | \brief 高速缓冲容器模板。 |
14 | -\version r604 | |
14 | +\version r748 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 521 |
17 | 17 | \par 创建时间: |
18 | 18 | 2013-12-22 20:19:14 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2021-12-29 01:58 +0800 | |
20 | + 2022-03-22 18:11 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -28,13 +28,16 @@ | ||
28 | 28 | #ifndef YB_INC_ystdex_cache_hpp_ |
29 | 29 | #define YB_INC_ystdex_cache_hpp_ 1 |
30 | 30 | |
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; | |
33 | 34 | #include <list> // for std::list; |
35 | +#include "scope_guard.hpp" // for std::hash, optional_function, std::ref, | |
36 | +// ystdex::unique_guard, ystdex::dismiss; | |
34 | 37 | #include <unordered_map> // for std::unordered_map; |
35 | 38 | #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; | |
38 | 41 | |
39 | 42 | namespace ystdex |
40 | 43 | { |
@@ -44,6 +47,7 @@ | ||
44 | 47 | /*! |
45 | 48 | \brief 使用双向链表实现的最近使用列表。 |
46 | 49 | \note 保证移除项时的顺序为最远端先移除。 |
50 | +\warning 非虚析构。 | |
47 | 51 | */ |
48 | 52 | template<typename _tKey, typename _tMapped, |
49 | 53 | class _tAlloc = std::allocator<std::pair<const _tKey, _tMapped>>> |
@@ -56,13 +60,23 @@ | ||
56 | 60 | */ |
57 | 61 | using allocator_type = _tAlloc; |
58 | 62 | 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>; | |
60 | 64 | using size_type = typename list_type::size_type; |
61 | 65 | using const_iterator = typename list_type::const_iterator; |
62 | 66 | using iterator = typename list_type::iterator; |
63 | 67 | |
68 | + //! \since build 942 | |
69 | + using list_type::list_type; | |
70 | + | |
64 | 71 | using list_type::begin; |
65 | 72 | |
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 | + | |
66 | 80 | using list_type::cbegin; |
67 | 81 | |
68 | 82 | using list_type::cend; |
@@ -109,8 +123,7 @@ | ||
109 | 123 | |
110 | 124 | auto& val(list_type::back()); |
111 | 125 | |
112 | - if(f) | |
113 | - f(val); | |
126 | + f(val); | |
114 | 127 | m.erase(val.first); |
115 | 128 | list_type::pop_back(); |
116 | 129 | } |
@@ -121,22 +134,24 @@ | ||
121 | 134 | while(max_use < size_type(m.size())) |
122 | 135 | { |
123 | 136 | yassume(!m.empty()); |
124 | - | |
125 | 137 | shrink(m, yforward(args)...); |
126 | 138 | } |
127 | 139 | } |
128 | 140 | |
141 | + //! \since build 942 | |
129 | 142 | void |
130 | - undo_emplace() ynothrow | |
143 | + undo_emplace() ynothrowv | |
131 | 144 | { |
132 | 145 | yassume(!empty()); |
133 | - | |
134 | 146 | list_type::pop_front(); |
135 | 147 | } |
136 | 148 | }; |
137 | 149 | |
138 | 150 | |
139 | -//! \brief 最近刷新策略的缓存特征。 | |
151 | +/*! | |
152 | +\brief 最近刷新策略的缓存特征。 | |
153 | +\ingroup traits | |
154 | +*/ | |
140 | 155 | //@{ |
141 | 156 | template<typename _tKey, typename _tMapped, typename _fHash = std::hash<_tKey>, |
142 | 157 | class _tAlloc = std::allocator<std::pair<const _tKey, _tMapped>>, |
@@ -166,6 +181,7 @@ | ||
166 | 181 | /*! |
167 | 182 | \brief 按最近使用策略刷新的缓存。 |
168 | 183 | \note 默认策略替换最近最少使用的项,保留其它项。 |
184 | +\warning 非虚析构。 | |
169 | 185 | \todo 加入异常安全的复制构造函数和复制赋值操作符。 |
170 | 186 | \todo 支持其它刷新策略。 |
171 | 187 | */ |
@@ -188,8 +204,13 @@ | ||
188 | 204 | //! \since build 611 |
189 | 205 | static_assert(are_same<size_type, typename used_list_type::size_type, |
190 | 206 | 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>>; | |
191 | 213 | |
192 | -private: | |
193 | 214 | /*! |
194 | 215 | \invariant <tt>std::count(used_list.begin(), used_list.end()) |
195 | 216 | == used_cache.size()</tt> 。 |
@@ -202,8 +223,13 @@ | ||
202 | 223 | size_type max_use; |
203 | 224 | |
204 | 225 | 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{}; | |
207 | 233 | |
208 | 234 | explicit |
209 | 235 | used_list_cache(size_type s = yimpl(15U)) |
@@ -223,7 +249,7 @@ | ||
223 | 249 | void |
224 | 250 | check_max_used() ynothrowv |
225 | 251 | { |
226 | - used_list.shrink(used_cache, max_use, flush); | |
252 | + used_list.shrink(used_cache, max_use, std::ref(flush)); | |
227 | 253 | } |
228 | 254 | |
229 | 255 | public: |
@@ -243,18 +269,25 @@ | ||
243 | 269 | |
244 | 270 | //! \since build 611 |
245 | 271 | //@{ |
246 | - iterator | |
247 | - begin() | |
272 | + YB_ATTR_nodiscard YB_PURE iterator | |
273 | + begin() ynothrow | |
248 | 274 | { |
249 | - return used_list.begin(); | |
275 | + return ystdex::begin(used_list); | |
250 | 276 | } |
251 | - iterator | |
252 | - begin() const | |
277 | + YB_ATTR_nodiscard YB_PURE iterator | |
278 | + begin() const ynothrow | |
253 | 279 | { |
254 | - return used_list.cbegin(); | |
280 | + return ystdex::begin(used_list); | |
255 | 281 | } |
256 | 282 | //@} |
257 | 283 | |
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 | + | |
258 | 291 | void |
259 | 292 | clear() ynothrow |
260 | 293 | { |
@@ -263,79 +296,89 @@ | ||
263 | 296 | } |
264 | 297 | |
265 | 298 | template<typename... _tParams> |
266 | - std::pair<iterator, bool> | |
299 | + inline std::pair<iterator, bool> | |
267 | 300 | emplace(const key_type& k, _tParams&&... args) |
268 | 301 | { |
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)...); | |
290 | 303 | } |
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 | |
291 | 315 | 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>> | |
293 | 318 | emplace(_tParams&&... args) |
294 | 319 | { |
295 | 320 | check_max_used(); |
296 | 321 | |
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)); | |
302 | 327 | |
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}; | |
308 | 332 | } |
309 | - catch(...) | |
310 | - { | |
311 | - used_list.undo_emplace(); | |
312 | - throw; | |
313 | - } | |
314 | - return {i, true}; | |
333 | + return {pr.first->second, false}; | |
315 | 334 | } |
316 | 335 | |
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: | |
317 | 360 | //! \since build 611 |
318 | 361 | //@{ |
319 | - iterator | |
320 | - end() | |
362 | + YB_ATTR_nodiscard YB_PURE iterator | |
363 | + end() ynothrow | |
321 | 364 | { |
322 | - return used_list.end(); | |
365 | + return ystdex::end(used_list); | |
323 | 366 | } |
324 | - iterator | |
325 | - end() const | |
367 | + YB_ATTR_nodiscard YB_PURE iterator | |
368 | + end() const ynothrow | |
326 | 369 | { |
327 | - return used_list.cend(); | |
370 | + return ystdex::end(used_list); | |
328 | 371 | } |
329 | 372 | //@} |
330 | 373 | |
331 | - iterator | |
374 | + YB_ATTR_nodiscard iterator | |
332 | 375 | find(const key_type& k) |
333 | 376 | { |
334 | 377 | const auto i(used_cache.find(k)); |
335 | 378 | |
336 | 379 | return i != used_cache.end() ? used_list.refresh(i->second) : end(); |
337 | 380 | } |
338 | - const_iterator | |
381 | + YB_ATTR_nodiscard const_iterator | |
339 | 382 | find(const key_type& k) const |
340 | 383 | { |
341 | 384 | const auto i(used_cache.find(k)); |
@@ -345,13 +388,13 @@ | ||
345 | 388 | |
346 | 389 | //! \since build 646 |
347 | 390 | //@{ |
348 | - const used_cache_type& | |
391 | + YB_ATTR_nodiscard YB_PURE const used_cache_type& | |
349 | 392 | get() const ynothrow |
350 | 393 | { |
351 | 394 | return used_cache; |
352 | 395 | } |
353 | 396 | |
354 | - const used_list_type& | |
397 | + YB_ATTR_nodiscard YB_PURE const used_list_type& | |
355 | 398 | list() const ynothrow |
356 | 399 | { |
357 | 400 | return used_list; |
@@ -359,7 +402,7 @@ | ||
359 | 402 | //@} |
360 | 403 | |
361 | 404 | //! \since build 611 |
362 | - size_type | |
405 | + YB_ATTR_nodiscard YB_PURE size_type | |
363 | 406 | size() const ynothrow |
364 | 407 | { |
365 | 408 | return used_cache.size(); |
@@ -369,28 +412,25 @@ | ||
369 | 412 | |
370 | 413 | |
371 | 414 | /*! |
372 | -\brief 以指定的关键字查找作为缓存的无序关联容器, | |
373 | - 若没有找到使用指定的可调用对象和参数初始化内容。 | |
374 | -\tparam _tMap 映射类型,可以是 std::map 、 std::unordered_map | |
415 | +\brief 以指定的关键字查找关联容器访问对应的元素并按需初始化其中的条目。 | |
416 | +\tparam _tMap 映射类型,可以是 std::map 、std::unordered_map | |
375 | 417 | 或 ystdex::used_list_cache 等。 |
418 | +\note 若没有找到使用指定的可调用对象和参数初始化内容。 | |
419 | +\note 使用 ADL emplace_hint_in_place 。 | |
420 | +\sa emplace_hint_in_place | |
421 | +\sa ystdex::try_emplace | |
376 | 422 | \since build 521 |
377 | 423 | */ |
378 | 424 | template<class _tMap, typename _tKey, typename _func, typename... _tParams> |
379 | 425 | 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)) | |
382 | 428 | { |
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; | |
394 | 434 | } |
395 | 435 | |
396 | 436 | } // namespace ystdex; |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file container.hpp |
12 | 12 | \ingroup YStandardEx |
13 | 13 | \brief 通用容器操作。 |
14 | -\version r2345 | |
14 | +\version r2610 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 338 |
17 | 17 | \par 创建时间: |
18 | 18 | 2012-09-12 01:36:20 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-02-26 22:49 +0800 | |
20 | + 2022-04-05 07:04 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -29,13 +29,14 @@ | ||
29 | 29 | #define YB_INC_ystdex_container_hpp_ 1 |
30 | 30 | |
31 | 31 | #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_; | |
37 | 38 | #include "algorithm.hpp" // for YB_VerifyIterator, sort_unique; |
38 | -#include "function.hpp" // for ystdex::seq_apply; | |
39 | +#include "apply.hpp" // for ystdex::seq_apply; | |
39 | 40 | #include "utility.hpp" // for ystdex::as_const; |
40 | 41 | |
41 | 42 | namespace ystdex |
@@ -72,69 +73,6 @@ | ||
72 | 73 | //@} |
73 | 74 | |
74 | 75 | |
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 | - | |
138 | 76 | /*! |
139 | 77 | \brief 插入参数指定的元素到容器。 |
140 | 78 | \since build 274 |
@@ -248,6 +186,38 @@ | ||
248 | 186 | } |
249 | 187 | |
250 | 188 | |
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 | + | |
251 | 221 | //! \since build 488 |
252 | 222 | namespace details |
253 | 223 | { |
@@ -271,28 +241,28 @@ | ||
271 | 241 | |
272 | 242 | template<class _tCon, typename _tKey> |
273 | 243 | 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_) | |
275 | 245 | { |
276 | - return std::find(ystdex::cbegin(con), ystdex::cend(con), key) | |
246 | + return std::find(ystdex::cbegin(con), ystdex::cend(con), k) | |
277 | 247 | != ystdex::cend(con); |
278 | 248 | } |
279 | 249 | template<class _tCon, typename _tKey> |
280 | 250 | 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_) | |
282 | 252 | { |
283 | - return con.count(key) != 0; | |
253 | + return con.count(k) != 0; | |
284 | 254 | } |
285 | 255 | template<class _tCon, typename _tKey> |
286 | 256 | YB_ATTR_nodiscard YB_PURE bool |
287 | -exists(const _tCon& con, const _tKey& key, false_) | |
257 | +exists(const _tCon& con, const _tKey& k, false_) | |
288 | 258 | { |
289 | - return details::exists(con, key, false_(), has_mem_count<_tCon, _tKey>()); | |
259 | + return details::exists(con, k, false_(), has_mem_count<_tCon, _tKey>()); | |
290 | 260 | } |
291 | 261 | template<class _tCon, typename _tKey> |
292 | 262 | YB_ATTR_nodiscard YB_PURE bool |
293 | -exists(const _tCon& con, const _tKey& key, true_) | |
263 | +exists(const _tCon& con, const _tKey& k, true_) | |
294 | 264 | { |
295 | - return con.find(key) != ystdex::cend(con); | |
265 | + return con.find(k) != ystdex::cend(con); | |
296 | 266 | } |
297 | 267 | //@} |
298 | 268 |
@@ -454,9 +424,9 @@ | ||
454 | 424 | */ |
455 | 425 | template<class _tCon, typename _tKey> |
456 | 426 | YB_ATTR_nodiscard YB_PURE inline bool |
457 | -exists(const _tCon& con, const _tKey& key) | |
427 | +exists(const _tCon& con, const _tKey& k) | |
458 | 428 | { |
459 | - return details::exists(con, key, details::has_mem_find<_tCon, _tKey>()); | |
429 | + return details::exists(con, k, details::has_mem_find<_tCon, _tKey>()); | |
460 | 430 | } |
461 | 431 | |
462 | 432 | /*! |
@@ -611,21 +581,22 @@ | ||
611 | 581 | |
612 | 582 | /*! |
613 | 583 | \brief 移除 const_iterator 的 const 限定。 |
614 | -\since build 680 | |
584 | +\since build 942 | |
615 | 585 | */ |
616 | 586 | //@{ |
617 | 587 | 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 | |
620 | 590 | { |
621 | 591 | return con.erase(i, i); |
622 | 592 | } |
593 | +//! \note 对迭代器数据成员使用 ADL cast_mutable 。 | |
623 | 594 | 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>()) | |
627 | 598 | { |
628 | - return {ystdex::cast_mutable(con, pr.first), pr.second}; | |
599 | + return {cast_mutable(con, pr.first), pr.second}; | |
629 | 600 | } |
630 | 601 | //@} |
631 | 602 |
@@ -776,11 +747,11 @@ | ||
776 | 747 | |
777 | 748 | /*! |
778 | 749 | \ingroup metafunctions |
779 | -\brief 判断关联容器、键和参数类型是否可以按 map 的方式无转移地构造。 | |
750 | +\brief 判断容器、键和参数类型是否可以按 map 的方式无转移地构造。 | |
780 | 751 | \since build 681 |
781 | 752 | */ |
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, | |
784 | 755 | std::piecewise_construct_t, std::tuple<_tKey&&>, std::tuple<_tParams&&...>>; |
785 | 756 | |
786 | 757 |
@@ -788,62 +759,102 @@ | ||
788 | 759 | namespace details |
789 | 760 | { |
790 | 761 | |
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 | + | |
791 | 781 | //! \since build 708 |
792 | 782 | template<class _tAssocCon> |
793 | 783 | struct assoc_con_traits |
794 | 784 | { |
795 | - //! \since build 730 | |
785 | + //! \since build 942 | |
796 | 786 | //@{ |
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. | |
797 | 791 | template<typename _tKey, typename... _tParams> |
798 | 792 | 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, | |
800 | 811 | typename _tAssocCon::const_iterator hint, _tKey&&, _tParams&&... args) |
801 | 812 | { |
802 | 813 | return con.emplace_hint(hint, yforward(args)...); |
803 | 814 | } |
804 | 815 | template<typename _tKey, typename... _tParams> |
805 | 816 | static inline typename _tAssocCon::iterator |
806 | - emplace_hint_in_place(true_, _tAssocCon& con, | |
817 | + emplace_hint_in_place(true_, true_, _tAssocCon& con, | |
807 | 818 | typename _tAssocCon::const_iterator hint, _tKey&& k, _tParams&&... args) |
808 | 819 | { |
809 | 820 | return con.emplace_hint(hint, std::piecewise_construct, |
810 | 821 | std::forward_as_tuple(yforward(k)), |
811 | 822 | std::forward_as_tuple(yforward(args)...)); |
812 | 823 | } |
824 | + //@} | |
813 | 825 | |
826 | + //! \since build 942 | |
827 | + //@{ | |
814 | 828 | 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 | |
816 | 830 | { |
817 | 831 | return val; |
818 | 832 | } |
819 | 833 | 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 | |
821 | 835 | { |
822 | 836 | return val.first; |
823 | 837 | } |
824 | - //@} | |
825 | 838 | |
826 | - //! \since build 792 | |
827 | - //@{ | |
828 | 839 | 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 | |
830 | 841 | { |
831 | 842 | return val; |
832 | 843 | } |
833 | 844 | 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 | |
835 | 846 | { |
836 | 847 | return val; |
837 | 848 | } |
838 | 849 | template<typename _tMap = _tAssocCon> |
839 | 850 | 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 | |
841 | 852 | { |
842 | 853 | return val.second; |
843 | 854 | } |
844 | 855 | template<typename _tMap = _tAssocCon> |
845 | 856 | 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 | |
847 | 858 | { |
848 | 859 | return val.second; |
849 | 860 | } |
@@ -853,7 +864,28 @@ | ||
853 | 864 | template<class _type> |
854 | 865 | using mapped_type_t = typename _type::mapped_type; |
855 | 866 | |
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 | + | |
857 | 889 | |
858 | 890 | /*! |
859 | 891 | \brief 带有提示的原地插入构造。 |
@@ -865,45 +897,95 @@ | ||
865 | 897 | _tKey&& k, _tParams&&... args) -> typename _tAssocCon::iterator |
866 | 898 | { |
867 | 899 | 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)...); | |
870 | 902 | } |
871 | 903 | |
872 | -/*! | |
873 | -\brief 从关联容器的值取键。 | |
874 | -\since build 679 | |
875 | -*/ | |
904 | +//! \since build 942 | |
905 | +//@{ | |
906 | +//! \brief 从关联容器的值取键。 | |
876 | 907 | template<class _tAssocCon, typename _type, |
877 | 908 | yimpl(typename = enable_if_convertible_t<const _type&, |
878 | 909 | const typename _tAssocCon::value_type&>)> |
879 | 910 | inline const typename _tAssocCon::key_type& |
880 | -extract_key(const _type& val) | |
911 | +extract_key(const _type& val) ynothrow | |
881 | 912 | { |
882 | 913 | return details::assoc_con_traits<_tAssocCon>::extract_key( |
883 | 914 | is_detected<details::mapped_type_t, _tAssocCon>(), val); |
884 | 915 | } |
885 | 916 | |
886 | -/*! | |
887 | -\brief 从关联容器的值取映射的值。 | |
888 | -\since build 792 | |
889 | -*/ | |
917 | +//! \brief 从关联容器的值取映射的值。 | |
890 | 918 | template<class _tAssocCon, typename _type, |
891 | 919 | yimpl(typename = enable_if_convertible_t<_type&, |
892 | 920 | typename _tAssocCon::value_type&>)> |
893 | 921 | inline auto |
894 | -extract_mapped(_type& val) | |
922 | +extract_mapped(_type& val) ynothrow | |
895 | 923 | -> decltype(details::assoc_con_traits<_tAssocCon>::extract_mapped( |
896 | 924 | is_detected<details::mapped_type_t, _tAssocCon>(), val)) |
897 | 925 | { |
898 | 926 | return details::assoc_con_traits<_tAssocCon>::extract_mapped( |
899 | 927 | is_detected<details::mapped_type_t, _tAssocCon>(), val); |
900 | 928 | } |
929 | +//@} | |
901 | 930 | |
902 | 931 | |
903 | 932 | //! \since build 792 |
904 | 933 | namespace details |
905 | 934 | { |
906 | 935 | |
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 | + | |
907 | 989 | /*! |
908 | 990 | \since build 681 |
909 | 991 | \note 使用 ADL extract_mapped 。 |
@@ -925,31 +1007,35 @@ | ||
925 | 1007 | |
926 | 1008 | |
927 | 1009 | /*! |
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 。 | |
931 | 1014 | */ |
932 | 1015 | //@{ |
933 | 1016 | /*! |
934 | 1017 | \brief 按指定键搜索指定关联容器。 |
935 | -\since build 677 | |
1018 | +\since build 942 | |
936 | 1019 | */ |
937 | 1020 | //@{ |
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> | |
940 | 1024 | search_map(const _tAssocCon& con, const _tKey& k) |
941 | 1025 | { |
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); | |
946 | 1033 | } |
947 | -template<class _tAssocCon, typename _tKey> | |
1034 | +template<class _tAssocCon, typename _tKey = typename _tAssocCon::key_type> | |
948 | 1035 | YB_ATTR_nodiscard YB_PURE inline std::pair<typename _tAssocCon::iterator, bool> |
949 | 1036 | search_map(_tAssocCon& con, const _tKey& k) |
950 | 1037 | { |
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)); | |
953 | 1039 | } |
954 | 1040 | //@} |
955 | 1041 | //! \since build 680 |
@@ -958,35 +1044,26 @@ | ||
958 | 1044 | \brief 按指定键和提示的迭代器位置搜索指定关联容器。 |
959 | 1045 | \pre 容器非空或提示的迭代器位置指向尾部。 |
960 | 1046 | \note 使用 ystdex::cbegin 。 |
1047 | +\since build 942 | |
961 | 1048 | */ |
962 | 1049 | //@{ |
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> | |
965 | 1053 | search_map(const _tAssocCon& con, typename _tAssocCon::const_iterator hint, |
966 | 1054 | const _tKey& k) |
967 | 1055 | { |
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); | |
982 | 1059 | } |
983 | -template<class _tAssocCon, typename _tKey> | |
1060 | +template<class _tAssocCon, typename _tKey = typename _tAssocCon::key_type> | |
984 | 1061 | YB_ATTR_nodiscard YB_PURE inline std::pair<typename _tAssocCon::iterator, bool> |
985 | 1062 | search_map(_tAssocCon& con, typename _tAssocCon::const_iterator hint, |
986 | 1063 | const _tKey& k) |
987 | 1064 | { |
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)); | |
990 | 1067 | } |
991 | 1068 | //@} |
992 | 1069 |
@@ -1012,7 +1089,7 @@ | ||
1012 | 1089 | inline std::pair<typename _tAssocCon::iterator, bool> |
1013 | 1090 | search_map_by(_func f, _tAssocCon& con, _tParams&&... args) |
1014 | 1091 | { |
1015 | - return ystdex::cast_mutable(con, | |
1092 | + return cast_mutable(con, | |
1016 | 1093 | ystdex::search_map_by(f, ystdex::as_const(con), yforward(args)...)); |
1017 | 1094 | } |
1018 | 1095 | //@} |
@@ -1032,8 +1109,8 @@ | ||
1032 | 1109 | std::pair<typename _tAssocCon::iterator, bool> |
1033 | 1110 | try_emplace(_tAssocCon& con, _tKey&& k, _tParams&&... args) |
1034 | 1111 | { |
1035 | - // XXX: Blocked. 'yforward' may cause G++ 5.2 silent crash. | |
1036 | 1112 | return ystdex::search_map_by([&](typename _tAssocCon::const_iterator i){ |
1113 | + // XXX: Blocked. 'yforward' may cause G++ 5.2 silent crash. | |
1037 | 1114 | return emplace_hint_in_place(con, i, yforward(k), |
1038 | 1115 | std::forward<_tParams>(args)...); |
1039 | 1116 | }, con, k); |
@@ -1044,8 +1121,8 @@ | ||
1044 | 1121 | try_emplace_hint(_tAssocCon& con, typename _tAssocCon::const_iterator hint, |
1045 | 1122 | _tKey&& k, _tParams&&... args) |
1046 | 1123 | { |
1047 | - // XXX: Blocked. 'yforward' may cause G++ 5.2 silent crash. | |
1048 | 1124 | return ystdex::search_map_by([&](typename _tAssocCon::const_iterator i){ |
1125 | + // XXX: Blocked. 'yforward' may cause G++ 5.2 silent crash. | |
1049 | 1126 | return emplace_hint_in_place(con, i, yforward(k), |
1050 | 1127 | std::forward<_tParams>(args)...); |
1051 | 1128 | }, con, hint, k); |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file function.hpp |
12 | 12 | \ingroup YStandardEx |
13 | 13 | \brief 函数基本操作和调用包装对象。 |
14 | -\version r4953 | |
14 | +\version r5052 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 847 |
17 | 17 | \par 创建时间: |
18 | 18 | 2018-12-13 01:24:06 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-02-14 18:05 +0800 | |
20 | + 2022-03-21 12:08 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -209,118 +209,6 @@ | ||
209 | 209 | //@} |
210 | 210 | |
211 | 211 | |
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 | - | |
324 | 212 | //! \ingroup transformation_traits |
325 | 213 | //@{ |
326 | 214 | //! \brief 取参数列表。 |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file meta.hpp |
12 | 12 | \ingroup YStandardEx |
13 | 13 | \brief 通用元编程设施。 |
14 | -\version r1988 | |
14 | +\version r2002 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 832 |
17 | 17 | \par 创建时间: |
18 | 18 | 2018-07-23 17:22:28 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-02-28 21:08 +0800 | |
20 | + 2022-03-18 03:20 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -1077,7 +1077,22 @@ | ||
1077 | 1077 | //@} |
1078 | 1078 | |
1079 | 1079 | |
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 | +*/ | |
1081 | 1096 | template<typename _type> |
1082 | 1097 | using sizeof_t = size_t_<sizeof(_type)>; |
1083 | 1098 |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file placement.hpp |
12 | 12 | \ingroup YStandardEx |
13 | 13 | \brief 放置对象管理操作。 |
14 | -\version r1117 | |
14 | +\version r1127 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 715 |
17 | 17 | \par 创建时间: |
18 | 18 | 2016-08-03 18:56:31 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-11 22:38 +0800 | |
20 | + 2022-03-21 12:08 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -89,8 +89,14 @@ | ||
89 | 89 | #endif |
90 | 90 | //@} |
91 | 91 | |
92 | +/*! \defgroup guards Guards | |
93 | +\brief 守卫:销毁时确保外部不变量有效的类型或实例是这些类型的模板。 | |
94 | +\warning 一般非虚析构。 | |
95 | +\since build 942 | |
96 | +*/ | |
97 | + | |
92 | 98 | /*! \defgroup allocators Allcators |
93 | -\brief 分配器。 | |
99 | +\brief 分配器:满足分配器要求的类型或实例是这些类型的模板。 | |
94 | 100 | \warning 一般非虚析构。 |
95 | 101 | \see WG21 N4606 17.6.3.5 [allocator.requirements] 。 |
96 | 102 | \since build 746 |
@@ -925,7 +931,10 @@ | ||
925 | 931 | }; |
926 | 932 | |
927 | 933 | |
928 | -//! \brief 独占放置存储的对象所有权的指针。 | |
934 | +/*! | |
935 | +\ingroup guards | |
936 | +\brief 独占放置存储的对象所有权的指针。 | |
937 | +*/ | |
929 | 938 | template<typename _type, typename _tPointer = _type*> |
930 | 939 | using placement_ptr |
931 | 940 | = std::unique_ptr<_type, placement_delete<_type, _tPointer>>; |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file range.hpp |
12 | 12 | \ingroup YStandardEx |
13 | 13 | \brief 范围操作。 |
14 | -\version r1167 | |
14 | +\version r1225 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 624 |
17 | 17 | \par 创建时间: |
18 | 18 | 2015-08-18 22:33:54 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-02-05 09:01 +0800 | |
20 | + 2022-03-21 12:08 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -28,10 +28,11 @@ | ||
28 | 28 | #ifndef YB_INC_ystdex_range_hpp_ |
29 | 29 | #define YB_INC_ystdex_range_hpp_ 1 |
30 | 30 | |
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; | |
33 | 33 | #include <iterator> // for <iteartor>, std::reverse_iterator, std::begin, |
34 | 34 | // std::end; |
35 | +#include <algorithm> // for internal <algorithm>, std::distance; | |
35 | 36 | // XXX: For efficiency, <valarray> is not supported here. Use %std names |
36 | 37 | // instead. |
37 | 38 |
@@ -681,6 +682,68 @@ | ||
681 | 682 | = decltype(ystdex::cbegin(std::declval<const _tRange&>())); |
682 | 683 | //@} |
683 | 684 | |
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 | + | |
684 | 747 | } // namespace ystdex; |
685 | 748 | |
686 | 749 | #endif |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file type_op.hpp |
12 | 12 | \ingroup YStandardEx |
13 | 13 | \brief C++ 类型操作。 |
14 | -\version r2922 | |
14 | +\version r2944 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 201 |
17 | 17 | \par 创建时间: |
18 | 18 | 2011-04-14 08:54:25 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-02-26 22:51 +0800 | |
20 | + 2022-03-18 03:23 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -32,9 +32,9 @@ | ||
32 | 32 | #define YB_INC_ystdex_type_op_hpp_ 1 |
33 | 33 | |
34 | 34 | #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; | |
38 | 38 | |
39 | 39 | namespace ystdex |
40 | 40 | { |
@@ -272,6 +272,27 @@ | ||
272 | 272 | using cond_or_t = _t<cond_or<_tCond, _tDefault, _gOp, _tParams...>>; |
273 | 273 | //@} |
274 | 274 | |
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 | + | |
275 | 296 | /*! |
276 | 297 | \brief 取公共非空类型:若第一参数为非空类型则为第一参数,否则从其余参数推断。 |
277 | 298 | \since build 562 |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file memory_resource.cpp |
12 | 12 | \ingroup YStandardEx |
13 | 13 | \brief 存储资源。 |
14 | -\version r1798 | |
14 | +\version r1805 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 842 |
17 | 17 | \par 创建时间: |
18 | 18 | 2018-10-27 19:30:12 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-16 14:37 +0800 | |
20 | + 2022-03-30 12:10 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -34,11 +34,10 @@ | ||
34 | 34 | #if YB_Has_memory_resource != 1 |
35 | 35 | # include <atomic> // for std::atomic; |
36 | 36 | #endif |
37 | -#include "ystdex/map.hpp" // for map, greater; | |
38 | 37 | #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, | |
40 | 40 | // ystdex::lower_bound_n; |
41 | -#include "ystdex/scope_guard.hpp" // for unique_guard, ystdex::dismiss; | |
42 | 41 | |
43 | 42 | namespace ystdex |
44 | 43 | { |
@@ -268,7 +267,7 @@ | ||
268 | 267 | if(alignment > 1) |
269 | 268 | { |
270 | 269 | // 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? | |
272 | 271 | // NOTE: The checks are necessary to prevent wrapping of the |
273 | 272 | // results of '+'. See also https://gcc.gnu.org/bugzilla/show_bug.cgi?id=19351. |
274 | 273 | if(bytes + alignment > bytes) |
@@ -369,7 +368,7 @@ | ||
369 | 368 | static_assert(is_power_of_2_positive(largest_required_pool_block_limit), |
370 | 369 | "Invalid limit value found."); |
371 | 370 | |
372 | - // TODO: Use interval arithmetic libraries. | |
371 | + // TODO: Add and use interval arithmetic library calls. | |
373 | 372 | if(opts.max_blocks_per_chunk - 1 >= max_blocks_per_chunk_limit) |
374 | 373 | opts.max_blocks_per_chunk = max_blocks_per_chunk_limit; |
375 | 374 | if(opts.largest_required_pool_block - 1 |
@@ -764,7 +763,8 @@ | ||
764 | 763 | : upstream_rsrc(upstream), next_buffer_size([](size_t size) ynothrow{ |
765 | 764 | // NOTE: Since 'mono_alloc_max == -yalignof(monobuf_header)', |
766 | 765 | // '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. | |
768 | 768 | return size < mono_alloc_min ? mono_alloc_min : (size >= mono_alloc_max |
769 | 769 | ? mono_alloc_max : ((size + yalignof(monobuf_header) - 1) |
770 | 770 | & mono_alloc_max)); |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - © 2015-2021 FrankHB. | |
2 | + © 2015-2022 FrankHB. | |
3 | 3 | |
4 | 4 | This file is part of the YSLib project, and may only be used, |
5 | 5 | modified, and distributed under the terms of the YSLib project |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file Dependency.h |
12 | 12 | \ingroup NPL |
13 | 13 | \brief 依赖管理。 |
14 | -\version r500 | |
14 | +\version r549 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 623 |
17 | 17 | \par 创建时间: |
18 | 18 | 2015-08-09 22:12:37 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2021-11-04 02:51 +0800 | |
20 | + 2022-03-26 09:25 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -119,65 +119,65 @@ | ||
119 | 119 | namespace Forms |
120 | 120 | { |
121 | 121 | |
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> | |
127 | 126 | void |
128 | -InvokeIn(ContextNode& ctx, _fCallable&& f) | |
127 | +InvokeIn(ContextNode& ctx, _fCallable&& f, _tParams&&... args) | |
129 | 128 | { |
130 | 129 | EnvironmentGuard gd(ctx, |
131 | 130 | NPL::SwitchToFreshEnvironment(ctx, ValueObject(ctx.WeakenRecord()))); |
132 | 131 | |
133 | - ystdex::invoke(f); | |
132 | + ystdex::invoke(yforward(f), yforward(args)...); | |
134 | 133 | } |
135 | 134 | |
136 | 135 | /*! |
137 | 136 | \brief 加载代码作为模块。 |
138 | 137 | \return 作为环境模块的环境对象强引用。 |
139 | 138 | \post 返回值非空,指定冻结的环境。 |
140 | -\since build 838 | |
141 | 139 | */ |
142 | -template<typename _fCallable> | |
140 | +template<typename _fCallable, typename... _tParams> | |
143 | 141 | shared_ptr<Environment> |
144 | -GetModuleFor(ContextNode& ctx, _fCallable&& f) | |
142 | +GetModuleFor(ContextNode& ctx, _fCallable&& f, _tParams&&... args) | |
145 | 143 | { |
146 | 144 | EnvironmentGuard gd(ctx, |
147 | 145 | NPL::SwitchToFreshEnvironment(ctx, ValueObject(ctx.WeakenRecord()))); |
148 | 146 | |
149 | - ystdex::invoke(f); | |
147 | + ystdex::invoke(yforward(f), yforward(args)...); | |
150 | 148 | ctx.GetRecordRef().Frozen = true; |
151 | 149 | return ctx.ShareRecord(); |
152 | 150 | } |
153 | 151 | |
154 | -//! \pre 间接断言:模块名称字符串的数据指针非空。 | |
155 | -//@{ | |
156 | 152 | /*! |
157 | -\brief 加载模块为变量,若已存在则忽略。 | |
158 | -\since build 871 | |
153 | +\pre 间接断言:模块名称字符串的数据指针非空。 | |
154 | +\sa Forms::GetModuleFor | |
159 | 155 | */ |
160 | -template<typename _fCallable> | |
156 | +//@{ | |
157 | +//! \brief 加载模块为变量,若已存在则忽略。 | |
158 | +template<typename _fCallable, typename... _tParams> | |
161 | 159 | 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) | |
163 | 162 | { |
164 | 163 | ctx.GetRecordRef().Define(module_name, |
165 | - Forms::GetModuleFor(ctx, yforward(f))); | |
164 | + Forms::GetModuleFor(ctx, yforward(f), yforward(args)...)); | |
166 | 165 | } |
167 | 166 | |
168 | 167 | /*! |
169 | 168 | \brief 加载模块为变量,若已存在抛出异常。 |
170 | 169 | \exception BadIdentifier 变量绑定已存在。 |
171 | -\since build 867 | |
172 | 170 | */ |
173 | -template<typename _fCallable> | |
171 | +template<typename _fCallable, typename... _tParams> | |
174 | 172 | 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) | |
176 | 175 | { |
177 | 176 | ctx.GetRecordRef().DefineChecked(module_name, |
178 | - Forms::GetModuleFor(ctx, yforward(f))); | |
177 | + Forms::GetModuleFor(ctx, yforward(f), yforward(args)...)); | |
179 | 178 | } |
180 | 179 | //@} |
180 | +//@} | |
181 | 181 | |
182 | 182 | |
183 | 183 | /*! |
@@ -192,6 +192,7 @@ | ||
192 | 192 | 支持的语法形式包括预定义对象、基本操作和定义在基础环境中的派生操作。 |
193 | 193 | 其中,派生操作包括基本派生操作和核心库函数。 |
194 | 194 | 加载的基础环境被冻结。 |
195 | +当前派生实现:求值合并子调用前已加载字符串模块或等价方式初始化为模块 std.strings 。 | |
195 | 196 | */ |
196 | 197 | YF_API void |
197 | 198 | LoadGroundContext(REPLContext&); |
@@ -230,37 +231,50 @@ | ||
230 | 231 | LoadModule_std_strings(REPLContext&); |
231 | 232 | |
232 | 233 | /*! |
234 | +\pre 当前派生实现:已加载和初始化依赖的模块,在当前环境可访问的指定的模块名称。 | |
235 | +\exception NPLException 违反加载模块的前置条件而无法成功初始化。 | |
236 | +*/ | |
237 | +//@{ | |
238 | +/*! | |
233 | 239 | \brief 加载输入/输出模块。 |
240 | +\pre 断言:第二参数非空。 | |
241 | +\note 第二参数指定基础环境。 | |
242 | +\since build 942 | |
234 | 243 | |
235 | 244 | 加载输入/输出库操作。 |
245 | +派生实现依赖模块: | |
246 | +字符串模块 std.strings 。 | |
236 | 247 | */ |
237 | 248 | YF_API void |
238 | -LoadModule_std_io(REPLContext&); | |
249 | +LoadModule_std_io(REPLContext&, const shared_ptr<Environment>&); | |
239 | 250 | |
240 | 251 | /*! |
241 | 252 | \brief 加载系统模块。 |
242 | 253 | \sa LoadModule_std_strings |
243 | 254 | |
244 | 255 | 加载系统库操作。 |
245 | -当前实现:求值合并子调用前已加载字符串模块或等价方式初始化为模块 std.strings 。 | |
256 | +派生实现依赖模块: | |
257 | +字符串模块 std.strings 。 | |
246 | 258 | */ |
247 | 259 | YF_API void |
248 | 260 | LoadModule_std_system(REPLContext&); |
249 | 261 | |
250 | 262 | /*! |
251 | 263 | \brief 加载模块管理模块。 |
252 | -\pre 当前实现:已加载初始化依赖的模块。 | |
253 | -\since build 923 | |
264 | +\pre 断言:第二参数非空。 | |
265 | +\note 第二参数指定基础环境。 | |
266 | +\since build 942 | |
254 | 267 | |
255 | 268 | 加载模块管理操作。 |
256 | -当前实现:已加载初始化以下模块: | |
269 | +派生实现依赖模块: | |
257 | 270 | 字符串模块 std.strings ; |
258 | 271 | 输入/输出模块 std.io ; |
259 | 272 | 代理求值模块 std.promises ; |
260 | 273 | 系统模块 std.system 。 |
261 | 274 | */ |
262 | 275 | YF_API void |
263 | -LoadModule_std_modules(REPLContext&); | |
276 | +LoadModule_std_modules(REPLContext&, const shared_ptr<Environment>&); | |
277 | +//@} | |
264 | 278 | |
265 | 279 | /*! |
266 | 280 | \brief 加载 SHBuild 使用的内部模块。 |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA.h |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA 公共接口。 |
14 | -\version r9401 | |
14 | +\version r9422 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 663 |
17 | 17 | \par 创建时间: |
18 | 18 | 2016-01-07 10:32:34 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-13 01:56 +0800 | |
20 | + 2022-04-05 15:16 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -737,8 +737,8 @@ | ||
737 | 737 | //! \since build 926 |
738 | 738 | PDefHOp(EnvironmentReference&, =, const EnvironmentReference& env_ref) |
739 | 739 | 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 | |
742 | 742 | ImplRet(swap(*this, env_ref), *this) |
743 | 743 | #else |
744 | 744 | //! \since build 894 |
@@ -2616,17 +2616,34 @@ | ||
2616 | 2616 | |
2617 | 2617 | /*! |
2618 | 2618 | \brief 访问指定类型的当前动作目标。 |
2619 | + \todo 添加 const 重载。 | |
2620 | + */ | |
2621 | + //@{ | |
2622 | + /*! | |
2623 | + \sa AccessCurrentAsUnchecked | |
2619 | 2624 | \since build 892 |
2620 | - \todo 添加 const 重载。 | |
2621 | 2625 | */ |
2622 | 2626 | template<typename _type> |
2623 | 2627 | YB_ATTR_nodiscard YB_PURE _type* |
2624 | 2628 | AccessCurrentAs() |
2625 | 2629 | { |
2626 | - return IsAlive() ? current.front().template target<_type>() : nullptr; | |
2630 | + return IsAlive() ? AccessCurrentAsUnchecked<_type>() : nullptr; | |
2627 | 2631 | } |
2628 | 2632 | |
2629 | 2633 | /*! |
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 | + /*! | |
2630 | 2647 | \brief 转移并应用作为尾动作的当前动作,并设置 LastStatus 。 |
2631 | 2648 | \note 非强异常安全:当动作调用抛出异常,不恢复已转移的动作。 |
2632 | 2649 | \note 不无效化作序列中第一个动作以外的元素。 |
@@ -2877,6 +2894,7 @@ | ||
2877 | 2894 | |
2878 | 2895 | /*! |
2879 | 2896 | \brief 分配环境。 |
2897 | +\return 新创建环境的非空指针。 | |
2880 | 2898 | \relates Environment |
2881 | 2899 | \since build 847 |
2882 | 2900 | */ |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA1.h |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA1 公共接口。 |
14 | -\version r9347 | |
14 | +\version r9423 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 472 |
17 | 17 | \par 创建时间: |
18 | 18 | 2014-02-02 17:58:24 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-07 02:49 +0800 | |
20 | + 2022-03-29 18:07 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -31,18 +31,18 @@ | ||
31 | 31 | #include "YModules.h" |
32 | 32 | #include YFM_NPL_NPLA // for NPLATag, TermNode, ContextNode, |
33 | 33 | // 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, | |
39 | 39 | // ystdex::make_parameter_list_t, ystdex::make_function_type_t, ystdex::true_, |
40 | 40 | // ystdex::decay_t, ystdex::expanded_caller, std::is_constructible, |
41 | 41 | // ystdex::or_, ArityMismatch, TermTags, RegularizeTerm, type_id, TokenValue, |
42 | 42 | // 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; | |
46 | 46 | #include YFM_YSLib_Core_YEvent // for YSLib::GHEvent, YSLib::GCombinerInvoker, |
47 | 47 | // YSLib::GDefaultLastValueInvoker; |
48 | 48 | #include <ystdex/algorithm.hpp> // for ystdex::fast_any_of, ystdex::split; |
@@ -1145,11 +1145,36 @@ | ||
1145 | 1145 | //@} |
1146 | 1146 | |
1147 | 1147 | |
1148 | -//! \since build 871 | |
1148 | +/*! | |
1149 | +\brief 注册一般形式上下文处理器。 | |
1150 | +\pre 间接断言:第二参数的数据指针非空。 | |
1151 | +\since build 942 | |
1152 | +*/ | |
1149 | 1153 | //@{ |
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 | + | |
1150 | 1174 | /*! |
1151 | 1175 | \brief 包装数种类枚举。 |
1152 | 1176 | \note 用于指定创建上下文处理器的种类。 |
1177 | +\since build 871 | |
1153 | 1178 | */ |
1154 | 1179 | enum WrappingKind : decltype(FormContextHandler::Wrapping) |
1155 | 1180 | { |
@@ -1159,24 +1184,6 @@ | ||
1159 | 1184 | Strict = 1 |
1160 | 1185 | }; |
1161 | 1186 | |
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 | - | |
1180 | 1187 | /*! |
1181 | 1188 | \pre 间接断言:第二参数的数据指针非空。 |
1182 | 1189 | \since build 838 |
@@ -1184,18 +1191,18 @@ | ||
1184 | 1191 | //@{ |
1185 | 1192 | //! \brief 注册一般形式上下文处理器。 |
1186 | 1193 | template<class _tTarget, typename... _tParams> |
1187 | -inline void | |
1194 | +YB_ATTR_always_inline inline void | |
1188 | 1195 | RegisterForm(_tTarget& target, string_view name, _tParams&&... args) |
1189 | 1196 | { |
1190 | - A1::RegisterHandler<Form>(target, name, yforward(args)...); | |
1197 | + A1::RegisterFormHandler(target, name, yforward(args)..., Form); | |
1191 | 1198 | } |
1192 | 1199 | |
1193 | 1200 | //! \brief 注册严格上下文处理器。 |
1194 | 1201 | template<class _tTarget, typename... _tParams> |
1195 | -inline void | |
1202 | +YB_ATTR_always_inline inline void | |
1196 | 1203 | RegisterStrict(_tTarget& target, string_view name, _tParams&&... args) |
1197 | 1204 | { |
1198 | - A1::RegisterHandler<>(target, name, yforward(args)...); | |
1205 | + A1::RegisterFormHandler(target, name, yforward(args)..., Strict); | |
1199 | 1206 | } |
1200 | 1207 | //@} |
1201 | 1208 |
@@ -1421,6 +1428,7 @@ | ||
1421 | 1428 | //! \since build 876 |
1422 | 1429 | //@{ |
1423 | 1430 | /*! |
1431 | +\ingroup guards | |
1424 | 1432 | \brief 求值环境守卫。 |
1425 | 1433 | |
1426 | 1434 | 作用域退出时调用环境切换器恢复保存的环境的作用域守卫。 |
@@ -1818,6 +1826,40 @@ | ||
1818 | 1826 | //@} |
1819 | 1827 | |
1820 | 1828 | |
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 | + | |
1821 | 1863 | /* |
1822 | 1864 | \brief REPL 上下文。 |
1823 | 1865 | \warning 非虚析构。 |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA1Forms.h |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA1 语法形式。 |
14 | -\version r8641 | |
14 | +\version r8664 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 882 |
17 | 17 | \par 创建时间: |
18 | 18 | 2020-02-15 11:19:21 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-02-14 07:39 +0800 | |
20 | + 2022-03-26 05:26 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -231,7 +231,10 @@ | ||
231 | 231 | \pre 设置为处理器调用的操作在进入调用前应确保设置尾上下文等内部状态。 |
232 | 232 | \pre 作为操作数的项的子项不包含引用或非正规表示引入的对操作数内的子项的循环引用。 |
233 | 233 | \pre 作为 NPLA1 规约函数的函数的参数符合规约函数实现约定。 |
234 | +\pre 若参数指定被规约项,参数是分支列表节点。 | |
235 | +\pre 若参数指定被规约项的容器非空,参数非空(对应枝节点)。 | |
234 | 236 | \pre 间接断言:作为规约函数第一参数指定的项是枝节点,以符合语法形式的实现要求。 |
237 | +\post 第一参数指定的被规约项在规约函数调用完成可表示一等对象。 | |
235 | 238 | \sa ContextState |
236 | 239 | \see %Documentation::NPL. |
237 | 240 | \since build 732 |
@@ -239,14 +242,12 @@ | ||
239 | 242 | 提供支持 NPLA1 对象语言文法的操作的接口。 |
240 | 243 | 提供的操作用于实现操作子或应用子底层的操作子。 |
241 | 244 | 除非另行指定,操作子的参数被通过直接转移项的形式转发。 |
242 | -对其中符合规约函数的 API ,在 NPLA1 规约函数约定的基础上,除非另行指定: | |
245 | +对其中符合规约函数的 API ,满足各项前置和后置条件,且除非另行指定: | |
243 | 246 | 可能使用同步(不依赖上下文)或异步(依赖上下文)实现; |
244 | 247 | 没有依赖上下文参数的规约函数使用同步实现; |
245 | 248 | 异步实现依赖的上下文是当前上下文; |
246 | 249 | 在异步实现中都要求下一项项和参数指定的项相同; |
247 | - 对规约函数的约定可隐含使用间接的断言检查; | |
248 | - 参数指定的被规约项是分支列表节点; | |
249 | - 参数指定的被规约项的容器非空(对应枝节点)。 | |
250 | + 对规约函数的上述约定(及各项前置和后置条件)可隐含使用间接的断言检查。 | |
250 | 251 | */ |
251 | 252 | namespace Forms |
252 | 253 | { |
@@ -602,24 +603,24 @@ | ||
602 | 603 | inline void |
603 | 604 | RegisterUnary(_tTarget& target, string_view name, _func f) |
604 | 605 | { |
605 | - A1::RegisterHandler<_vWrapping>(target, name, | |
606 | - UnaryExpansion<_func>(std::move(f))); | |
606 | + A1::RegisterFormHandler(target, name, | |
607 | + UnaryExpansion<_func>(std::move(f)), _vWrapping); | |
607 | 608 | } |
608 | 609 | //! \since build 927 |
609 | 610 | template<size_t _vWrapping = Strict, typename _func, class _tTarget> |
610 | 611 | inline void |
611 | 612 | RegisterUnary(_tTarget& target, string_view name, trivial_swap_t, _func f) |
612 | 613 | { |
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); | |
615 | 616 | } |
616 | 617 | template<size_t _vWrapping = Strict, typename _type, typename _func, |
617 | 618 | class _tTarget> |
618 | 619 | inline void |
619 | 620 | RegisterUnary(_tTarget& target, string_view name, _func f) |
620 | 621 | { |
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); | |
623 | 624 | } |
624 | 625 | //! \since build 927 |
625 | 626 | template<size_t _vWrapping = Strict, typename _type, typename _func, |
@@ -627,8 +628,8 @@ | ||
627 | 628 | inline void |
628 | 629 | RegisterUnary(_tTarget& target, string_view name, trivial_swap_t, _func f) |
629 | 630 | { |
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); | |
632 | 633 | } |
633 | 634 | //@} |
634 | 635 |
@@ -638,24 +639,24 @@ | ||
638 | 639 | inline void |
639 | 640 | RegisterBinary(_tTarget& target, string_view name, _func f) |
640 | 641 | { |
641 | - A1::RegisterHandler<_vWrapping>(target, name, | |
642 | - BinaryExpansion<_func>(std::move(f))); | |
642 | + A1::RegisterFormHandler(target, name, | |
643 | + BinaryExpansion<_func>(std::move(f)), _vWrapping); | |
643 | 644 | } |
644 | 645 | //! \since build 927 |
645 | 646 | template<size_t _vWrapping = Strict, typename _func, class _tTarget> |
646 | 647 | inline void |
647 | 648 | RegisterBinary(_tTarget& target, string_view name, trivial_swap_t, _func f) |
648 | 649 | { |
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); | |
651 | 652 | } |
652 | 653 | template<size_t _vWrapping = Strict, typename _type, typename _type2, |
653 | 654 | typename _func, class _tTarget> |
654 | 655 | inline void |
655 | 656 | RegisterBinary(_tTarget& target, string_view name, _func f) |
656 | 657 | { |
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); | |
659 | 660 | } |
660 | 661 | //! \since build 927 |
661 | 662 | template<size_t _vWrapping = Strict, typename _type, typename _type2, |
@@ -663,8 +664,8 @@ | ||
663 | 664 | inline void |
664 | 665 | RegisterBinary(_tTarget& target, string_view name, trivial_swap_t, _func f) |
665 | 666 | { |
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); | |
668 | 669 | } |
669 | 670 | //@} |
670 | 671 | //@} |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file SContext.h |
12 | 12 | \ingroup NPL |
13 | 13 | \brief S 表达式上下文。 |
14 | -\version r4178 | |
14 | +\version r4186 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 304 |
17 | 17 | \par 创建时间: |
18 | 18 | 2012-08-03 19:55:41 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-14 18:23 +0800 | |
20 | + 2022-03-18 03:12 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -40,9 +40,8 @@ | ||
40 | 40 | // YSLib::ListContainerTag, std::initializer_list, |
41 | 41 | // ystdex::enable_if_same_param_t, ystdex::exclude_self_t, |
42 | 42 | // 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; | |
46 | 45 | #include YFM_YSLib_Core_YException // for YSLib::LoggedEvent; |
47 | 46 | #include <ystdex/deref_op.hpp> // for ystdex::call_value_or; |
48 | 47 | #include <ystdex/range.hpp> // for std::iterator_traits, |
@@ -418,7 +417,7 @@ | ||
418 | 417 | yimpl(ystdex::enable_if_t<sizeof...(_tParams) != 0 |
419 | 418 | || !ystdex::is_same_param<ValueObject, _tParam>::value, int> = 0, |
420 | 419 | 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, | |
422 | 421 | TermNode::allocator_type, void> |
423 | 422 | SetValue(_tParam&& arg, _tParams&&... args) ynoexcept_spec(Value.assign( |
424 | 423 | std::allocator_arg, std::declval<TermNode::allocator_type&>(), |
@@ -804,12 +803,11 @@ | ||
804 | 803 | |
805 | 804 | //! \brief 创建项节点。 |
806 | 805 | //@{ |
807 | -//! \since build 853 | |
806 | +//! \since build 942 | |
808 | 807 | template<typename... _tParam, typename... _tParams> |
809 | 808 | 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> | |
813 | 811 | AsTermNode(_tParams&&... args) |
814 | 812 | { |
815 | 813 | return TermNode(NoContainer, yforward(args)...); |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file Dependency.cpp |
12 | 12 | \ingroup NPL |
13 | 13 | \brief 依赖管理。 |
14 | -\version r6634 | |
14 | +\version r6880 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 623 |
17 | 17 | \par 创建时间: |
18 | 18 | 2015-08-09 22:14:45 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-07 02:50 +0800 | |
20 | + 2022-03-31 02:43 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -32,8 +32,9 @@ | ||
32 | 32 | // RetainN, NPL::ResolveRegular, NPL::Deref, RelaySwitched, trivial_swap, |
33 | 33 | // A1::NameTypedReducerHandler, std::bind, std::ref, Forms::CallResolvedUnary, |
34 | 34 | // 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, | |
37 | 38 | // NPL::forward_as_tuple, LiftOther, ThrowNonmodifiableErrorForAssignee, |
38 | 39 | // ThrowValueCategoryError, ValueToken, ResolveTerm, TokenValue, |
39 | 40 | // CheckVariadicArity, A1::AsForm, ystdex::bind1, std::placeholders, |
@@ -48,9 +49,11 @@ | ||
48 | 49 | // ReferenceTerm, ResolveName, ystdex::fast_any_of, Ensigil, YSLib::ufexists, |
49 | 50 | // YSLib::to_std_string, EmplaceCallResultOrReturn, NPL::TryAccessTerm, |
50 | 51 | // ystdex::plus, ystdex::tolower, YSLib::OwnershipTag, YSLib::IO::StreamPut, |
51 | -// YSLib::FetchEnvironmentVariable, YSLib::SetEnvironmentVariable, | |
52 | +// YSLib::FetchEnvironmentVariable, YSLib::SetEnvironmentVariable, NPL::tuple, | |
52 | 53 | // YSLib::IO::UniqueFile, YSLib::uremove, YSLib::allocate_shared, |
53 | 54 | // ReduceReturnUnspecified, ystdex::throw_error; |
55 | +#include <ystdex/container.hpp> // for ystdex::exists, ystdex::search_map, | |
56 | +// ystdex::emplace_hint_in_place; | |
54 | 57 | #include YFM_NPL_NPLA1Forms // for EncapsulateValue, Encapsulate, Encapsulated, |
55 | 58 | // Decapsulate, NPL::Forms functions, StringToSymbol, SymbolToString; |
56 | 59 | #include YFM_NPL_NPLAMath // for NumerLeaf, NumberNode, NPL math functions; |
@@ -60,11 +63,10 @@ | ||
60 | 63 | // ystdex::make_transform; |
61 | 64 | #include YFM_YSLib_Service_TextFile // for |
62 | 65 | // YSLib::IO::SharedInputMappedFileStream, YSLib::Text; |
63 | -#include <cerrno> // for errno, ERANGE; | |
64 | 66 | #include <regex> // for std::regex, std::regex_replace, std::regex_match; |
65 | 67 | #include <ostream> // for std::endl; |
66 | 68 | #include "NPLA1Internals.h" // for NPL_Impl_NPLA1_Enable_Thunked, |
67 | -// ReduceSubsequent; | |
69 | +// ReduceSubsequent, A1::RelayCurrentNext, MakeKeptGuard; | |
68 | 70 | #include YFM_YSLib_Core_YCoreUtilities // for YSLib::LockCommandArguments, |
69 | 71 | // YSLib::FetchCommandOutput, YSLib::RandomizeTemplatedString; |
70 | 72 | #include <ystdex/string.hpp> // for ystdex::begins_with; |
@@ -231,7 +233,7 @@ | ||
231 | 233 | ReduceToLoadFile(TermNode& term, ContextNode& ctx, REPLContext& context, |
232 | 234 | string filename) |
233 | 235 | { |
234 | - term = context.Load(context, ctx, filename); | |
236 | + term = context.Load(context, ctx, std::move(filename)); | |
235 | 237 | // NOTE: This is explicitly not same to klisp. This is also friendly to PTC. |
236 | 238 | // XXX: Same to %A1::ReduceOnce, without setup the next term. |
237 | 239 | return ContextState::Access(ctx).ReduceOnce.Handler(term, ctx); |
@@ -243,8 +245,9 @@ | ||
243 | 245 | ReduceToLoadExternal(TermNode& term, ContextNode& ctx, REPLContext& context) |
244 | 246 | { |
245 | 247 | 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())); | |
248 | 251 | } |
249 | 252 | |
250 | 253 | ReductionStatus |
@@ -468,7 +471,7 @@ | ||
468 | 471 | NPL::AddToken(t2, "body"); |
469 | 472 | NPL::AddToken(term, "d"); |
470 | 473 | Reduce(term, ctx); |
471 | -#if false | |
474 | +# if false | |
472 | 475 | // NOTE: Usage: |
473 | 476 | AddDefineFunction(rctx, "$defv!", {"&$f", "&formals", "&ef"}, |
474 | 477 | {"$f", "$vau", "formals", "ef"}); |
@@ -494,10 +497,27 @@ | ||
494 | 497 | }, {"f", "$lambda/e", "p", "formals"}); |
495 | 498 | AddDefineFunction(rctx, "$defl/e%!", {"&f", "&p", "&formals", |
496 | 499 | }, {"f", "$lambda/e%", "p", "formals"}); |
497 | -#endif | |
500 | +# endif | |
498 | 501 | } |
499 | 502 | #endif |
500 | 503 | //@} |
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 | +} | |
501 | 521 | #endif |
502 | 522 | |
503 | 523 | //! \since build 834 |
@@ -619,8 +639,8 @@ | ||
619 | 639 | // basic than vau. |
620 | 640 | RegisterStrict(ctx, "copy-environment", CopyEnvironment); |
621 | 641 | 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(); | |
624 | 644 | }); |
625 | 645 | RegisterUnary(ctx, "freeze-environment!", [](TermNode& x){ |
626 | 646 | Environment::EnsureValid(ResolveEnvironment(x).first).Frozen = true; |
@@ -893,16 +913,9 @@ | ||
893 | 913 | // shall not be moved after being called. |
894 | 914 | // TODO: Blocked. Use C++14 lambda initializers to simplify the |
895 | 915 | // implementation. |
896 | - ystdex::bind1([] YB_LAMBDA_ANNOTATE( | |
897 | - (TermNode& term, const EnvironmentReference& ce), , flatten){ | |
916 | + ystdex::bind1([](TermNode& term, const EnvironmentReference& ce){ | |
898 | 917 | 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); | |
906 | 919 | }, context.Root.WeakenRecord())); |
907 | 920 | RegisterStrict(renv, "derive-current-environment", |
908 | 921 | [] YB_LAMBDA_ANNOTATE((TermNode& term, ContextNode& ctx), , flatten){ |
@@ -1146,14 +1159,14 @@ | ||
1146 | 1159 | $cond |
1147 | 1160 | ((null? x) #t) |
1148 | 1161 | ((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) | |
1150 | 1163 | (#t #f); |
1151 | 1164 | $defv%! $or &x d |
1152 | 1165 | $cond |
1153 | 1166 | ((null? x) #f) |
1154 | 1167 | ((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)); | |
1157 | 1170 | $defw%! accl (&l &pred? &base &head &tail &sum) d |
1158 | 1171 | $if (apply pred? (list% l) d) (forward! base) |
1159 | 1172 | (apply accl (list% (apply tail (list% l) d) pred? |
@@ -1400,9 +1413,9 @@ | ||
1400 | 1413 | throw NPLException("Empty requirement name found."); |
1401 | 1414 | } |
1402 | 1415 | |
1403 | -//! \since build 923 | |
1416 | +//! \since build 941 | |
1404 | 1417 | YB_ATTR_nodiscard string |
1405 | -FindValidRequirementIn(const vector<string>& specs, const string req) | |
1418 | +FindValidRequirementIn(const vector<string>& specs, const string& req) | |
1406 | 1419 | { |
1407 | 1420 | YAssert(!req.empty(), "Invalid requirement found."); |
1408 | 1421 | const std::regex re("\\?"); |
@@ -1601,8 +1614,8 @@ | ||
1601 | 1614 | ystdex::bind1([&, lift](TermNode& t, ContextNode& c, |
1602 | 1615 | shared_ptr<Environment>& p_env){ |
1603 | 1616 | 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)); | |
1606 | 1619 | }, std::placeholders::_2, std::move(p_env_saved)), |
1607 | 1620 | trivial_swap, A1::NameTypedReducerHandler([&, p_ref]{ |
1608 | 1621 | // NOTE: Different to [RnRK], the promise object may be assigned and |
@@ -1635,6 +1648,26 @@ | ||
1635 | 1648 | }, "promise-handle-result")); |
1636 | 1649 | } |
1637 | 1650 | //@} |
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 | +} | |
1638 | 1671 | #endif |
1639 | 1672 | |
1640 | 1673 |
@@ -1911,8 +1944,11 @@ | ||
1911 | 1944 | } |
1912 | 1945 | |
1913 | 1946 | void |
1914 | -LoadModule_std_io(REPLContext& context) | |
1947 | +LoadModule_std_io(REPLContext& context, | |
1948 | + const shared_ptr<Environment>& p_ground) | |
1915 | 1949 | { |
1950 | + YAssertNonnull(p_ground); | |
1951 | + | |
1916 | 1952 | auto& renv(context.Root.GetRecordRef()); |
1917 | 1953 | |
1918 | 1954 | RegisterUnary<Strict, const string>(renv, "readable-file?", |
@@ -1966,6 +2002,66 @@ | ||
1966 | 2002 | return ValueToken::Unspecified; |
1967 | 2003 | }); |
1968 | 2004 | #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 | |
1969 | 2065 | } |
1970 | 2066 | |
1971 | 2067 | void |
@@ -2005,8 +2101,8 @@ | ||
2005 | 2101 | }); |
2006 | 2102 | context.ShareCurrentSource("<lib:std.system>"); |
2007 | 2103 | 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); | |
2010 | 2106 | )NPL"); |
2011 | 2107 | RegisterStrict(renv, "system", CallSystem); |
2012 | 2108 | RegisterStrict(renv, "system-get", [](TermNode& term){ |
@@ -2034,13 +2130,18 @@ | ||
2034 | 2130 | } |
2035 | 2131 | |
2036 | 2132 | void |
2037 | -LoadModule_std_modules(REPLContext& context) | |
2133 | +LoadModule_std_modules(REPLContext& context, | |
2134 | + const shared_ptr<Environment>& p_ground) | |
2038 | 2135 | { |
2136 | + YAssertNonnull(p_ground); | |
2039 | 2137 | #if NPL_Impl_NPLA1_Native_Forms |
2138 | + | |
2139 | + using namespace std::placeholders; | |
2040 | 2140 | using YSLib::to_std_string; |
2041 | 2141 | auto& renv(context.Root.GetRecordRef()); |
2042 | 2142 | 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)); | |
2044 | 2145 | auto& registry(*p_registry); |
2045 | 2146 | const auto p_specs([&]{ |
2046 | 2147 | auto p_vec(YSLib::allocate_shared<vector<string>>(a)); |
@@ -2077,16 +2178,28 @@ | ||
2077 | 2178 | throw NPLException("Empty requirement name found."); |
2078 | 2179 | }); |
2079 | 2180 | RegisterStrict(renv, "register-requirement!", trivial_swap, |
2080 | - [&](TermNode& term){ | |
2181 | + ystdex::bind1([&](TermNode& term, const EnvironmentReference& r_ground){ | |
2081 | 2182 | auto i(term.begin()); |
2082 | 2183 | const auto& req(NPL::ResolveRegular<const string>(NPL::Deref(++i))); |
2083 | 2184 | |
2084 | 2185 | 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 | + } | |
2087 | 2200 | throw NPLException("Requirement '" + to_std_string(req) |
2088 | 2201 | + "' is already registered."); |
2089 | - }); | |
2202 | + }, EnvironmentReference(p_ground))); | |
2090 | 2203 | RegisterUnary<Strict, const string>(renv, "unregister-requirement!", |
2091 | 2204 | trivial_swap, [&](const string& req){ |
2092 | 2205 | CheckRequirement(req); |
@@ -2103,26 +2216,83 @@ | ||
2103 | 2216 | return FindValidRequirementIn(NPL::Deref(p_specs), req); |
2104 | 2217 | }); |
2105 | 2218 | 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 | + | |
2107 | 2223 | auto i(term.begin()); |
2108 | 2224 | const auto& req(NPL::ResolveRegular<const string>(NPL::Deref(++i))); |
2109 | 2225 | |
2110 | 2226 | 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) | |
2112 | 2246 | { |
2113 | 2247 | auto filename(FindValidRequirementIn(specs, req)); |
2114 | 2248 | |
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."); | |
2117 | 2286 | } |
2118 | - return ReduceReturnUnspecified(term); | |
2119 | - }); | |
2287 | + return reduce_to_res(pr.first->second.first); | |
2288 | + }, _2, EnvironmentReference(p_ground))); | |
2120 | 2289 | #else |
2290 | + yunused(p_ground); | |
2121 | 2291 | context.ShareCurrentSource("<lib:std.modules>"); |
2122 | 2292 | // XXX: Thread-safety is not respected currently. |
2123 | 2293 | context.Perform(R"NPL( |
2124 | 2294 | $provide/let! (registered-requirement? register-requirement! |
2125 | - unregister-requirement! find-requirement-filename) | |
2295 | + unregister-requirement! find-requirement-filename require) | |
2126 | 2296 | ((mods $as-environment ( |
2127 | 2297 | $import! std.strings &string-empty? &++ &string->symbol; |
2128 | 2298 |
@@ -2132,9 +2302,10 @@ | ||
2132 | 2302 | $def! registry () make-environment; |
2133 | 2303 | $defl! bound-name? (&req) |
2134 | 2304 | $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), | |
2136 | 2307 | $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 | |
2138 | 2309 | ), |
2139 | 2310 | $def! prom_pathspecs ($remote-eval% $lazy std.promises) |
2140 | 2311 | $let ((spec ($remote-eval% env-get std.system) "NPLA1_PATH")) |
@@ -2159,21 +2330,32 @@ | ||
2159 | 2330 | $if (string-empty? req) (() requirement-error) (bound-name? req), |
2160 | 2331 | $defl/e! ®ister-requirement! mods (&req) |
2161 | 2332 | $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)))), | |
2164 | 2338 | $defl/e! &unregister-requirement! mods (&req) |
2165 | 2339 | $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 | |
2167 | 2341 | (++ "Requirement '" req "' is not registered."))), |
2168 | 2342 | $defl/e! &find-requirement-filename mods (&req) |
2169 | 2343 | 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); | |
2171 | 2358 | ); |
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)); | |
2177 | 2359 | )NPL"); |
2178 | 2360 | #endif |
2179 | 2361 | } |
@@ -2384,10 +2566,13 @@ | ||
2384 | 2566 | continue; |
2385 | 2567 | #endif |
2386 | 2568 | } |
2569 | + | |
2387 | 2570 | // NOTE: This is nearly fatal. It is not intended to be handled in |
2388 | 2571 | // the object language in general. Thus, currently it is not |
2389 | 2572 | // 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" | |
2391 | 2576 | " prefix '" + to_std_string(pfx) + '\''); |
2392 | 2577 | }); |
2393 | 2578 | } |
@@ -2397,19 +2582,26 @@ | ||
2397 | 2582 | { |
2398 | 2583 | LoadGroundContext(context); |
2399 | 2584 | |
2585 | + const auto pfx("std."); | |
2586 | + string mod(pfx, context.Allocator); | |
2400 | 2587 | 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)); | |
2405 | 2595 | }); |
2596 | + const auto p_ground(rctx.ShareRecord()); | |
2406 | 2597 | |
2407 | 2598 | load_std_module("promises", LoadModule_std_promises); |
2408 | 2599 | 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); | |
2411 | 2602 | 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); | |
2413 | 2605 | } |
2414 | 2606 | |
2415 | 2607 | } // namespace Forms; |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA1.cpp |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA1 公共接口。 |
14 | -\version r22291 | |
14 | +\version r22297 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 472 |
17 | 17 | \par 创建时间: |
18 | 18 | 2014-02-02 18:02:47 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-12 21:58 +0800 | |
20 | + 2022-04-03 02:07 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -661,13 +661,10 @@ | ||
661 | 661 | } |
662 | 662 | |
663 | 663 | template<typename _func> |
664 | - static void | |
664 | + static inline void | |
665 | 665 | HandleLeaf(_func f, const TermNode& t, bool t_has_ref) |
666 | 666 | { |
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); | |
671 | 668 | } |
672 | 669 | |
673 | 670 | template<typename _func> |
@@ -697,7 +694,7 @@ | ||
697 | 694 | } |
698 | 695 | |
699 | 696 | template<typename _func> |
700 | - static void | |
697 | + static inline void | |
701 | 698 | HandleLeaf(_func f, const TermNode& t) |
702 | 699 | { |
703 | 700 | if(!IsIgnore(t)) |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA1Forms.cpp |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA1 语法形式。 |
14 | -\version r26358 | |
14 | +\version r26431 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 882 |
17 | 17 | \par 创建时间: |
18 | 18 | 2014-02-15 11:19:51 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-14 18:22 +0800 | |
20 | + 2022-03-29 02:31 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -53,6 +53,8 @@ | ||
53 | 53 | // LiftCollapsed, YSLib::usystem; |
54 | 54 | #include "NPLA1Internals.h" // for A1::Internals API; |
55 | 55 | #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; | |
56 | 58 | |
57 | 59 | namespace NPL |
58 | 60 | { |
@@ -1855,6 +1857,45 @@ | ||
1855 | 1857 | LiftOther(term, head); |
1856 | 1858 | } |
1857 | 1859 | |
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 | + | |
1858 | 1899 | /*! |
1859 | 1900 | \pre 第一参数的类型可平凡交换。 |
1860 | 1901 | \since build 917 |
@@ -1871,12 +1912,7 @@ | ||
1871 | 1912 | auto& con(term.GetContainerRef()); |
1872 | 1913 | auto& lv_l(con.back()); |
1873 | 1914 | 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); | |
1880 | 1916 | }); |
1881 | 1917 | |
1882 | 1918 | // NOTE: This shall be stored separatedly to %l because %l is abstract, |
@@ -1898,7 +1934,7 @@ | ||
1898 | 1934 | return Combine<NonTailCall>::ReduceCallSubsequent(nterm, ctx, d, |
1899 | 1935 | A1::NameTypedReducerHandler( |
1900 | 1936 | 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); | |
1902 | 1938 | }, std::move(i_0)), "eval-acc-head-next")); |
1903 | 1939 | } |
1904 | 1940 | LiftOther(term, base); |
@@ -1917,7 +1953,7 @@ | ||
1917 | 1953 | return Acc([&] YB_LAMBDA_ANNOTATE( |
1918 | 1954 | (TermNode& l, TermNode& base, TermNode& lv_l, TermNode& nterm, |
1919 | 1955 | const shared_ptr<Environment>& d, TNIter& i) , , flatten){ |
1920 | - auto& tail(*++i); | |
1956 | + auto& tail(*i); | |
1921 | 1957 | |
1922 | 1958 | { |
1923 | 1959 | TermNode::Container tcon(base.get_allocator()); |
@@ -1932,14 +1968,7 @@ | ||
1932 | 1968 | A1::NameTypedReducerHandler( |
1933 | 1969 | // XXX: Capture of %d by copy is a slightly more efficient. |
1934 | 1970 | [&, 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); | |
1943 | 1972 | return Combine<NonTailCall>::ReduceCallSubsequent(nterm, ctx, d, |
1944 | 1973 | A1::NameTypedReducerHandler( |
1945 | 1974 | [&] YB_LAMBDA_ANNOTATE(() , , flatten){ |
@@ -1959,23 +1988,18 @@ | ||
1959 | 1988 | return Acc([&] YB_LAMBDA_ANNOTATE( |
1960 | 1989 | (TermNode& l, TermNode&, TermNode& lv_l, TermNode& nterm, |
1961 | 1990 | const shared_ptr<Environment>& d, TNIter& i) , , flatten){ |
1991 | + auto& tail(*i); | |
1962 | 1992 | auto& n2term(*std::next(term.rbegin())); |
1963 | - auto& tail(*++i); | |
1964 | 1993 | const auto& lv_sum_op(*++i); |
1965 | 1994 | |
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); | |
1974 | 1996 | return Combine<NonTailCall>::ReduceCallSubsequent(n2term, ctx, d, |
1975 | 1997 | // XXX: Capture of %d by copy is a slightly more efficient. |
1976 | 1998 | A1::NameTypedReducerHandler( |
1977 | 1999 | [&, d] YB_LAMBDA_ANNOTATE(() , , flatten){ |
1978 | 2000 | l = std::move(n2term); |
2001 | + // XXX: %A1::ReduceCurrentNext would not prevent direct recursive | |
2002 | + // calls to %ReduceAccR on %NPL_Impl_NPLA1_Enable_InlineDirect. | |
1979 | 2003 | return A1::ReduceCurrentNextThunked( |
1980 | 2004 | *term.emplace(ystdex::exchange(term.GetContainerRef(), [&]{ |
1981 | 2005 | TermNode::Container tcon(term.get_allocator()); |
@@ -1984,9 +2008,9 @@ | ||
1984 | 2008 | tcon.push_back(std::move(nterm)); |
1985 | 2009 | return tcon; |
1986 | 2010 | }())), 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( | |
1990 | 2014 | [&, d] YB_LAMBDA_ANNOTATE(() , , flatten){ |
1991 | 2015 | return Combine<NonTailCall>::ReduceEnvSwitch(term, ctx, d); |
1992 | 2016 | }, "eval-accr-sum")); |
@@ -2021,7 +2045,7 @@ | ||
2021 | 2045 | \brief 准备递归调用使用的列表对象:绑定对象为列表临时对象范围。 |
2022 | 2046 | \since build 913 |
2023 | 2047 | */ |
2024 | -YB_FLATTEN void | |
2048 | +void | |
2025 | 2049 | PrepareFoldRList(TermNode& term) |
2026 | 2050 | { |
2027 | 2051 | // NOTE: Conceptually, the 1st call to the list element can be applied to |
@@ -2095,6 +2119,8 @@ | ||
2095 | 2119 | auto i(term.begin()); |
2096 | 2120 | |
2097 | 2121 | if(!tr.empty()) |
2122 | + // XXX: %A1::ReduceCurrentNext would not prevent direct recursive calls | |
2123 | + // to %ReduceFoldR1 on %NPL_Impl_NPLA1_Enable_InlineDirect. | |
2098 | 2124 | return A1::ReduceCurrentNextThunked( |
2099 | 2125 | *term.emplace(ystdex::exchange(con, [&]{ |
2100 | 2126 | TermNode::Container tcon(term.get_allocator()); |
@@ -2188,14 +2214,15 @@ | ||
2188 | 2214 | { |
2189 | 2215 | auto& con(term.GetContainerRef()); |
2190 | 2216 | |
2191 | - con.pop_front(); | |
2192 | 2217 | // NOTE: Bind the last argument as the local reference to list temporary |
2193 | 2218 | // object. |
2194 | 2219 | PrepareFoldRList(con.back()); |
2195 | 2220 | |
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()); | |
2199 | 2226 | // NOTE: Save 'kons' (for %FoldR1) or 'appv' (for %Map1). |
2200 | 2227 | PrepareTailOp(term, ctx, *rterm.begin()); |
2201 | 2228 | return ReduceLiftSum(term, ctx, rterm, f); |
@@ -2719,8 +2746,8 @@ | ||
2719 | 2746 | } |
2720 | 2747 | else if(IsBranchedList(extracted)) |
2721 | 2748 | { |
2722 | - // XXX: As %BranchFirstReferenced, with 'NPL::IsMovable(tags)' | |
2723 | - // always 'true'. | |
2749 | + // XXX: As %BranchFirstReferenced, with 'NPL::IsMovable(tags)' always | |
2750 | + // 'true'. | |
2724 | 2751 | auto& nterm(*con.emplace_front().emplace()); |
2725 | 2752 | auto& tm(AccessFirstSubterm(extracted)); |
2726 | 2753 |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA1Internals.cpp |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA1 内部接口。 |
14 | -\version r20595 | |
14 | +\version r20599 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 473 |
17 | 17 | \par 创建时间: |
18 | 18 | 2020-02-15 13:20:08 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-09 02:27 +0800 | |
20 | + 2022-04-05 15:19 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 非公开模块名称: |
@@ -27,8 +27,8 @@ | ||
27 | 27 | |
28 | 28 | #include "NPL/YModules.h" |
29 | 29 | #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; | |
32 | 32 | |
33 | 33 | namespace NPL |
34 | 34 | { |
@@ -211,10 +211,10 @@ | ||
211 | 211 | { |
212 | 212 | auto p_act(AccessTCOAction(ctx)); |
213 | 213 | |
214 | - if(!p_act) | |
214 | + if(YB_UNLIKELY(!p_act)) | |
215 | 215 | { |
216 | 216 | SetupTailTCOAction(ctx, term, {}); |
217 | - p_act = AccessTCOAction(ctx); | |
217 | + p_act = AccessTCOActionUnchecked(ctx); | |
218 | 218 | } |
219 | 219 | return NPL::Deref(p_act); |
220 | 220 | } |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLA1Internals.h |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA1 内部接口。 |
14 | -\version r21955 | |
14 | +\version r22014 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 882 |
17 | 17 | \par 创建时间: |
18 | 18 | 2020-02-15 13:20:08 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-08 00:46 +0800 | |
20 | + 2022-04-05 13:40 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 非公开模块名称: |
@@ -33,18 +33,17 @@ | ||
33 | 33 | // ReductionStatus, Reducer, YSLib::map, lref, Environment, |
34 | 34 | // set, NPL::Deref, IsTyped, EnvironmentList, EnvironmentReference, tuple, |
35 | 35 | // 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; | |
40 | 39 | #include <ystdex/compose.hpp> // for ystdex::get_less; |
41 | 40 | #include <ystdex/scope_guard.hpp> // for ystdex::unique_guard; |
42 | 41 | #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; | |
43 | 46 | #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; | |
48 | 47 | |
49 | 48 | namespace NPL |
50 | 49 | { |
@@ -85,7 +84,7 @@ | ||
85 | 84 | "Invalid combination of build options found."); |
86 | 85 | |
87 | 86 | //! \since build 842 |
88 | -YB_FLATTEN inline PDefH(void, SetupNextTerm, ContextNode& ctx, TermNode& term) | |
87 | +inline PDefH(void, SetupNextTerm, ContextNode& ctx, TermNode& term) | |
89 | 88 | ImplExpr(ContextState::Access(ctx).SetNextTermRef(term)) |
90 | 89 | |
91 | 90 | // NOTE: See $2018-09 @ %Documentation::Workflow for rationale of the |
@@ -382,8 +381,8 @@ | ||
382 | 381 | // NOTE: If there is no environment set in %act.EnvGuard yet, there is |
383 | 382 | // ideally no need to save the components to the frame record list |
384 | 383 | // 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. | |
387 | 386 | if(EnvGuard.func.SavedPtr) |
388 | 387 | { |
389 | 388 | // NOTE: Operand saving is performed whether the frame compression |
@@ -444,6 +443,12 @@ | ||
444 | 443 | YB_ATTR_nodiscard YB_PURE inline |
445 | 444 | PDefH(TCOAction*, AccessTCOAction, ContextNode& ctx) ynothrow |
446 | 445 | 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 | + | |
447 | 452 | // NOTE: There is no need to check term like |
448 | 453 | // 'if(&p->GetTermPtr().get() == &term)'. It should be same to saved enclosing |
449 | 454 | // term unless a nested TCO action is needed explicitly (by following |
@@ -491,25 +496,6 @@ | ||
491 | 496 | return act; |
492 | 497 | } |
493 | 498 | # 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")) | |
513 | 499 | #endif |
514 | 500 | |
515 | 501 |
@@ -633,7 +619,7 @@ | ||
633 | 619 | |
634 | 620 | //! \since build 910 |
635 | 621 | template<typename _fCurrent, typename _fNext> |
636 | -YB_FLATTEN inline ReductionStatus | |
622 | +inline ReductionStatus | |
637 | 623 | RelayCurrentNext(ContextNode& ctx, TermNode& term, _fCurrent&& cur, |
638 | 624 | _fNext&& next) |
639 | 625 | { |
@@ -648,7 +634,7 @@ | ||
648 | 634 | } |
649 | 635 | //! \since build 926 |
650 | 636 | template<typename _fCurrent, typename _fNext> |
651 | -YB_FLATTEN inline ReductionStatus | |
637 | +inline ReductionStatus | |
652 | 638 | RelayCurrentNext(ContextNode& ctx, TermNode& term, _fCurrent&& cur, |
653 | 639 | trivial_swap_t, _fNext&& next) |
654 | 640 | { |
@@ -662,7 +648,7 @@ | ||
662 | 648 | } |
663 | 649 | //! \since build 926 |
664 | 650 | template<typename _fCurrent, typename _fNext> |
665 | -YB_FLATTEN inline ReductionStatus | |
651 | +inline ReductionStatus | |
666 | 652 | RelayCurrentNext(ContextNode& ctx, TermNode& term, trivial_swap_t, |
667 | 653 | _fCurrent&& cur, _fNext&& next) |
668 | 654 | { |
@@ -676,7 +662,7 @@ | ||
676 | 662 | } |
677 | 663 | //! \since build 926 |
678 | 664 | template<typename _fCurrent, typename _fNext> |
679 | -YB_FLATTEN inline ReductionStatus | |
665 | +inline ReductionStatus | |
680 | 666 | RelayCurrentNext(ContextNode& ctx, TermNode& term, trivial_swap_t, |
681 | 667 | _fCurrent&& cur, trivial_swap_t, _fNext&& next) |
682 | 668 | { |
@@ -691,7 +677,7 @@ | ||
691 | 677 | |
692 | 678 | //! \since build 916 |
693 | 679 | template<typename _fCurrent, typename _fNext> |
694 | -YB_FLATTEN inline ReductionStatus | |
680 | +inline ReductionStatus | |
695 | 681 | RelayCurrentNextThunked(ContextNode& ctx, TermNode& term, _fCurrent&& cur, |
696 | 682 | _fNext&& next) |
697 | 683 | { |
@@ -705,7 +691,7 @@ | ||
705 | 691 | } |
706 | 692 | //! \since build 926 |
707 | 693 | template<typename _fCurrent, typename _fNext> |
708 | -YB_FLATTEN inline ReductionStatus | |
694 | +inline ReductionStatus | |
709 | 695 | RelayCurrentNextThunked(ContextNode& ctx, TermNode& term, _fCurrent&& cur, |
710 | 696 | trivial_swap_t, _fNext&& next) |
711 | 697 | { |
@@ -719,7 +705,7 @@ | ||
719 | 705 | } |
720 | 706 | //! \since build 926 |
721 | 707 | template<typename _fCurrent, typename _fNext> |
722 | -YB_FLATTEN inline ReductionStatus | |
708 | +inline ReductionStatus | |
723 | 709 | RelayCurrentNextThunked(ContextNode& ctx, TermNode& term, trivial_swap_t, |
724 | 710 | _fCurrent&& cur, _fNext&& next) |
725 | 711 | { |
@@ -733,7 +719,7 @@ | ||
733 | 719 | } |
734 | 720 | //! \since build 926 |
735 | 721 | template<typename _fCurrent, typename _fNext> |
736 | -YB_FLATTEN inline ReductionStatus | |
722 | +inline ReductionStatus | |
737 | 723 | RelayCurrentNextThunked(ContextNode& ctx, TermNode& term, trivial_swap_t, |
738 | 724 | _fCurrent&& cur, trivial_swap_t, _fNext&& next) |
739 | 725 | { |
@@ -817,13 +803,13 @@ | ||
817 | 803 | // being captured and it is not capturable here. No %SetupNextTerm |
818 | 804 | // needs to be called here. Otherwise, %cur is not a %Contiuation and |
819 | 805 | // 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, | |
821 | 807 | // while they shall be equivalent. |
822 | 808 | #if NPL_Impl_NPLA1_Enable_Thunked |
823 | 809 | // TODO: Blocked. Use C++14 lambda initializers to simplify the |
824 | 810 | // implementation. |
825 | 811 | return A1::RelayCurrentNext(ctx, term, yforward(cur), trivial_swap, |
826 | - MakeMoveGuard(gd)); | |
812 | + MakeKeptGuard(gd)); | |
827 | 813 | #else |
828 | 814 | yunused(gd); |
829 | 815 | return A1::RelayDirect(ctx, cur, term); |
@@ -837,7 +823,7 @@ | ||
837 | 823 | { |
838 | 824 | // XXX: See %RelayNextGuarded. |
839 | 825 | #if NPL_Impl_NPLA1_Enable_Thunked |
840 | - auto act(MakeMoveGuard(gd)); | |
826 | + auto act(MakeKeptGuard(gd)); | |
841 | 827 | // TODO: Blocked. Use C++14 lambda initializers to simplify the |
842 | 828 | // implementation. |
843 | 829 | // XXX: Term reused. Call of %SetupNextTerm is not needed as the next |
@@ -868,7 +854,7 @@ | ||
868 | 854 | #if NPL_Impl_NPLA1_Enable_Thunked |
869 | 855 | // TODO: Blocked. Use C++14 lambda initializers to simplify the |
870 | 856 | // implementation. |
871 | - auto act(MakeMoveGuard(gd)); | |
857 | + auto act(MakeKeptGuard(gd)); | |
872 | 858 | |
873 | 859 | if(lift) |
874 | 860 | { |
@@ -941,7 +927,7 @@ | ||
941 | 927 | |
942 | 928 | //! \pre TCO 实现:当前动作是 TCO 动作,且其中的当前项和被规约的项相同。 |
943 | 929 | template<typename _fCurrent> |
944 | - YB_FLATTEN static inline ReductionStatus | |
930 | + static inline ReductionStatus | |
945 | 931 | RelayNextGuardedLifted(ContextNode& ctx, TermNode& term, |
946 | 932 | EnvironmentGuard&& gd, _fCurrent&& cur) |
947 | 933 | { |
@@ -1079,7 +1065,7 @@ | ||
1079 | 1065 | bound.GetContainer(), EnsureLValueReference(TermReference(ref)))) |
1080 | 1066 | |
1081 | 1067 | //! \since build 920 |
1082 | -YB_FLATTEN inline PDefH(void, SetEvaluatedValue, TermNode& term, | |
1068 | +inline PDefH(void, SetEvaluatedValue, TermNode& term, | |
1083 | 1069 | TermNode& bound, shared_ptr<Environment>& p_env) |
1084 | 1070 | ImplExpr(term.Value = TermReference(NPL::Deref(p_env).MakeTermTags(bound) |
1085 | 1071 | & ~TermTags::Unique, bound, std::move(p_env))) |
@@ -1098,6 +1084,17 @@ | ||
1098 | 1084 | char |
1099 | 1085 | ExtractSigil(string_view&); |
1100 | 1086 | |
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 | + | |
1101 | 1098 | |
1102 | 1099 | //! \since build 917 |
1103 | 1100 | //@{ |
@@ -1155,10 +1152,8 @@ | ||
1155 | 1152 | Match(nd, true); |
1156 | 1153 | }); |
1157 | 1154 | } |
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); | |
1162 | 1157 | } |
1163 | 1158 | |
1164 | 1159 | void |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPLAMath.cpp |
12 | 12 | \ingroup NPL |
13 | 13 | \brief NPLA 数学功能。 |
14 | -\version r28310 | |
14 | +\version r28330 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 930 |
17 | 17 | \par 创建时间: |
18 | 18 | 2021-11-03 12:50:49 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-02-08 22:27 +0800 | |
20 | + 2022-02-08 19:39 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -38,8 +38,9 @@ | ||
38 | 38 | #include <ystdex/exception.h> // for ystdex::unsupported, std::domain_error; |
39 | 39 | #include <ystdex/cstdint.hpp> // for std::numeric_limits, |
40 | 40 | // 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; | |
43 | 44 | // XXX: The type 'long double' may be also IEC 60559 binary64 format, e.g. in |
44 | 45 | // Microsoft VC++. |
45 | 46 | #include <cfloat> // for FLT_*, DBL_*, LDBL_*; |
@@ -129,11 +130,11 @@ | ||
129 | 130 | using has_special_value_t = ystdex::bool_<std::numeric_limits< |
130 | 131 | _type>::has_infinity && std::numeric_limits<_type>::has_quiet_NaN>; |
131 | 132 | |
132 | -static_assert(has_special_value_t<float>::value, | |
133 | +static_assert(has_special_value_t<float>(), | |
133 | 134 | "Unsupported implementation found."); |
134 | -static_assert(has_special_value_t<double>::value, | |
135 | +static_assert(has_special_value_t<double>(), | |
135 | 136 | "Unsupported implementation found."); |
136 | -static_assert(has_special_value_t<long double>::value, | |
137 | +static_assert(has_special_value_t<long double>(), | |
137 | 138 | "Unsupported implementation found."); |
138 | 139 | //@} |
139 | 140 |
@@ -579,7 +580,12 @@ | ||
579 | 580 | YB_ATTR_nodiscard yconstfn |
580 | 581 | yimpl(ystdex::exclude_self_t)<ValueObject, _tParam, _tRet> |
581 | 582 | 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 | |
582 | 585 | ynoexcept_spec(_tBase::operator()(yforward(x))) |
586 | +#else | |
587 | + ynoexcept_spec(std::declval<GUOp>()._tBase::operator()(yforward(x))) | |
588 | +#endif | |
583 | 589 | { |
584 | 590 | return _tBase::operator()(yforward(x)); |
585 | 591 | } |
@@ -598,7 +604,13 @@ | ||
598 | 604 | YB_ATTR_nodiscard yconstfn |
599 | 605 | yimpl(ystdex::exclude_self_t)<ValueObject, _tParam2, _tRet> |
600 | 606 | operator()(_tParam1&& x, _tParam2&& y) const |
607 | + // XXX: Ditto. | |
608 | +#if !YB_IMPL_GNUCPP || YB_IMPL_GNUCPP >= 90000 | |
601 | 609 | 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 | |
602 | 614 | { |
603 | 615 | return _tBase::operator()(yforward(x), yforward(y)); |
604 | 616 | } |
@@ -2963,8 +2975,8 @@ | ||
2963 | 2975 | int exp_bin, typename fp_traits<_type>::carrier_type& sig_dec, int& exp_dec) |
2964 | 2976 | ynothrow |
2965 | 2977 | { |
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; | |
2968 | 2980 | // NOTE: This algorithm requires extra 2 bits in %sig_bin to prevent |
2969 | 2981 | // overflow. Every conforming floating-point format shall meet the |
2970 | 2982 | // requirement, since the sign bit and the exponent bits shall be no less |
@@ -2974,7 +2986,7 @@ | ||
2974 | 2986 | "Invalid carrier type found."); |
2975 | 2987 | |
2976 | 2988 | // NOTE: Ditto. |
2977 | - YAssert(sig_bin >> (traits::value_bits - 2) == carrier_type(), | |
2989 | + YAssert(sig_bin >> (traits_type::value_bits - 2) == carrier_type(), | |
2978 | 2990 | "Invalid significand value found."); |
2979 | 2991 | |
2980 | 2992 | // NOTE: %shorter indicates the lower bound is closer, i.e. the "shorter |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file ChangeLog.V0.9.txt |
12 | 12 | \ingroup Documentation |
13 | 13 | \brief 版本更新历史记录 - V0.9 。 |
14 | -\version r8636 | |
14 | +\version r8973 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 800 |
17 | 17 | \par 创建时间: |
18 | 18 | 2020-10-12 17:19:23 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-17 21:08 +0800 | |
20 | + 2022-04-05 16:32 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -34,6 +34,341 @@ | ||
34 | 34 | ( |
35 | 35 | / %YBase.YStandardEx $= |
36 | 36 | ( |
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 | + ( | |
37 | 372 | / %Placement $= |
38 | 373 | ( |
39 | 374 | / DLDI "header inclusion %IteratorOperation" |
@@ -2199,7 +2534,7 @@ | ||
2199 | 2534 | ( |
2200 | 2535 | - 'YB_ATTR(always_inline)' @ "function template %destruct_in", |
2201 | 2536 | // Ditto. |
2202 | - + "workaround to avoid removing the inlineing attribute" | |
2537 | + + DLI "workaround to avoid removing the inlining attribute" | |
2203 | 2538 | @ '(YB_IMPL_GNUCPP && !YB_IMPL_CLANGPP) && \ |
2204 | 2539 | defined(NDEBUG) && __OPTIMIZE__' |
2205 | 2540 | @ "function template %construct_within" |
@@ -3414,7 +3749,7 @@ | ||
3414 | 3749 | / DLDI "simplified function %CheckEnvironmentFormal" |
3415 | 3750 | !^ 'YB_UNLIKELY', |
3416 | 3751 | / DLI "optimized member function %FormContextHandler::operator()" |
3417 | - !^ "%NPL::ToReducer", | |
3752 | + ^ "%any_ops::trivial_swap" ~ "%NPL::ToReducer", | |
3418 | 3753 | / @ "function %DefaultEvaluateLeaf" $= |
3419 | 3754 | ( |
3420 | 3755 | - "'#n' and '#null' handling", |
@@ -6753,7 +7088,7 @@ | ||
6753 | 7088 | // See $2020-10 @ %Documentation::Workflow. |
6754 | 7089 | - "variables %(LDFLAGS_DYN; LDFLAGS_DYN_BASE, \ |
6755 | 7090 | LDFLAGS_DYN_EXTRA)"; |
6756 | - - "variables %(LIBS_RPATH, LIBPFX, DSOSFX)" | |
7091 | + - "variables %(LIBS_RPATH, LIBPFX, DSOSFX)", | |
6757 | 7092 | - "variable %EXESFX" |
6758 | 7093 | $dep_from ("build command line" @ %Test) |
6759 | 7094 | ), |
@@ -11,13 +11,13 @@ | ||
11 | 11 | /*! \file NPL.txt |
12 | 12 | \ingroup Documentation |
13 | 13 | \brief NPL 规范和实现规格说明。 |
14 | -\version r26784 | |
14 | +\version r27151 | |
15 | 15 | \author FrankHB <frankhb1989@gmail.com> |
16 | 16 | \since build 304 |
17 | 17 | \par 创建时间: |
18 | 18 | 2012-04-25 10:34:20 +0800 |
19 | 19 | \par 修改时间: |
20 | - 2022-03-07 02:52 +0800 | |
20 | + 2022-03-31 08:09 +0800 | |
21 | 21 | \par 文本编码: |
22 | 22 | UTF-8 |
23 | 23 | \par 模块名称: |
@@ -238,7 +238,7 @@ | ||
238 | 238 | 正确性应蕴含完整性。 |
239 | 239 | 由 @1.5.3.1 的推论:设计应包含完整的需求响应。 |
240 | 240 | **注释** |
241 | -对通用编程语言的一个完整性要求是支持计算上的可表达性。 | |
241 | +对通用编程语言的一个完整性要求是支持计算上的可表达性(expresiveness) 。 | |
242 | 242 | 这种性质被称为可有效计算性(effective computability) ,或 Turing 完备性(Turing completeness) 。在可物理实现的计算普遍遵循 Church-Turing 论题(Church-Turing thesis) 的情形下,这同时是可计算性(computability) 。以上性质一般不加分辨。 |
243 | 243 | 特定的场合要求更弱的性质,例如类型检查等情形需要全(total) 计算而确保实现总是可终止。这种要求在完整的实现中可通过附加的设施(用户提供的标注或证明)保证,而不应通过系统设计的规则静态地排除,否则实现是不完整的。仅在作为领域特定语言时,通过从需求中排除可计算性,静态规则作为优化是被允许的。 |
244 | 244 |
@@ -252,7 +252,7 @@ | ||
252 | 252 | |
253 | 253 | @1.5.3.3 可修改性(modifiablity) : |
254 | 254 | 在满足需求的前提下,修改应尽可能少地有碍于其它的接口。 |
255 | -这是 @1.5.2.1 的推论。 | |
255 | +这是变化的自由(@1.5.2.1) 的推论。 | |
256 | 256 | |
257 | 257 | @1.5.3.4 避免抽象泄漏(abstraction leak) : |
258 | 258 | 泄漏的抽象(leaky abstraction) 指抽象的底层复杂性没有被抽象合理地隐藏(@1.5.6.3) ,而在一定程度上构成了利用抽象时的不必要的依赖。 |
@@ -270,8 +270,12 @@ | ||
270 | 270 | @1.5.3.6 开放性(openness) : |
271 | 271 | 除非另行指定,不假定实体不存在。 |
272 | 272 | 这个原则主要用于建模(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 | +开放世界不限制论域中的某个子集是封闭的。例如,论域中可能存在某个子集的所有元素通过一定方式被枚举。 | |
275 | 279 | |
276 | 280 | @1.5.4 结构和依赖原则: |
277 | 281 |
@@ -820,7 +824,7 @@ | ||
820 | 824 | 通过特定的等价关系可定义具体的不可变状态(@1.2.1.2) 的集合。 |
821 | 825 | 这些集合可用于定义以这些状态为值的实体的不可变性,进而定义不保持可变性的改变(mutation) 操作和具体的其中可能影响可观察行为(@4.1.3) 的修改(@4.1) 操作。 |
822 | 826 | 通过限定不同的修改操作,定义不同的可修改性(modifiability) 和对立的不可修改性(nonmodifiability) 。 |
823 | -通过明确不可修改性拒绝支持修改操作(例如通过通过实体的类型检查(@4.7.3) 拒绝特定的修改操作),或通过不提供修改操作(例如关于 ISO C++ 的非类且非数组类型的纯右值,尽管要求非纯右值可被视为是一种类型检查),语义规则保证实体不被修改操作改变状态。 | |
827 | +通过明确不可修改性拒绝支持修改操作(例如通过通过实体的类型检查(@4.7.4) 拒绝特定的修改操作),或通过不提供修改操作(例如关于 ISO C++ 的非类且非数组类型的纯右值,尽管要求非纯右值可被视为是一种类型检查),语义规则保证实体不被修改操作改变状态。 | |
824 | 828 | (不依赖和影响实体同一性(@4.1) 的)同一个实体上的修改操作是改变操作。只有具有可变状态(@1.2.1.2) 的实体可能支持这些操作。 |
825 | 829 | 一般地,一个实体不一定保证可区分是否具有不可变性以及具有何种不可变性(也蕴含一般不可区分可修改性),因为不可变性依赖实体的表示(@1.2.4) 进行约定。 |
826 | 830 | 潜在引起实体的一些内部状态的变化的操作可不被视为影响不可变性而不被视为实体的(整体意义上的)改变操作。这种实体具有内部可变性(interior mutability) 。 |
@@ -904,7 +908,7 @@ | ||
904 | 908 | @4.2.1.3 Kernel 中的一等对象: |
905 | 909 | 尽管没有显式指出一等实体和一等对象的区别,在 [RnRK] 中的一等对象和此处的一等实体在目的上一致。因为 Kernel 不直接支持区分对象同一性,一等实体退化为一等对象。 |
906 | 910 | 并不需要修改一等对象的判定准则(@4.1) 限定为后者并使前者依赖后者的定义,因为作为抽象,前者通常并非是后者的操作上进行限制得到(正相反,一般是通过补充约定假设得到,如 @4.1 的定义)。 |
907 | -类似的一个例子是不可修改对象(nonmodifiable object) 可以但不必要是对应的可修改对象(modifiable object) 的子类型(@4.7) 。 | |
911 | +类似的一个例子是不可修改对象(nonmodifiable object) 可以但不必要是对应的可修改对象(modifiable object) 的子类型(@4.7.2) 。 | |
908 | 912 | 作为 @1.5.5.1 的实例,一等实体避免特殊规则,和 [RnRK] 设计原则 G1a 一致。 |
909 | 913 | 除非另行指定,本节中的以下描述提供允许派生实现提供保证或假设的机制,并非要求。派生实现可附加规则改变此处对一等对象的保证或性质。 |
910 | 914 |
@@ -1085,7 +1089,7 @@ | ||
1085 | 1089 | 这同样违反了简单性; |
1086 | 1090 | 这使基于等价关系定义不同的修改(@4.1.4.2) 更困难且难以实现统一性(@1.5.5.1)(例如,需要更多的特设的基本修改操作(@4.1.4.2) ,或者如 SRFI-17 这样的为修改操作特设的上下文),乃至缺乏实际意义; |
1087 | 1091 | 若支持 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) )时它仍存在一些不容易扩展的较复杂的问题,如: | |
1089 | 1093 | https://groups.google.com/forum/?fromgroups#!topic/klisp/pLz-uqJ0WfE 。 |
1090 | 1094 | 使用 [RnRK] 的设计而在此之后引入一等引用(而不仅是一等被引用的对象)可能是对语言特性的实现的复杂的改动,但直接消除隐式的共享(@4.2.3.4) 而使用一等引用的设计并不需要这样的复杂性。 |
1091 | 1095 | 封装类型作为装箱以外仍有意义。封装类型仍可用于实现箱,但这只是实现细节,更一般的情形两者是无关的——这也符合 @1.5.2.3 和 @1.5.2.4 的要求。 |
@@ -1157,7 +1161,7 @@ | ||
1157 | 1161 | NPL 不要求预设具体的实体及对象类型的设计,因此不要求用户使用语言体现整体上的可扩展性(@1.5.5.1) 。这允许由派生实现指定类型的外延而满足 @1.5.2.1 。 |
1158 | 1162 | 特别地,NPL 不要求表达式具有预设的不同类型(@4.7) 。 |
1159 | 1163 | 除不必涉及引用(@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) 机制都相互独立。 | |
1161 | 1165 | |
1162 | 1166 | @4.2.7 例子: |
1163 | 1167 | 存在一些判定准则不符合这些要求,如 [RnRK] 中引用的 [Gu91] : |
@@ -1287,7 +1291,7 @@ | ||
1287 | 1291 | 表达式在得到规范形式后规约终止,且蕴含求值终止。 |
1288 | 1292 | 得到范式的规约步骤称为规范化(normalization) 。 |
1289 | 1293 | 若表达式规约总是能得到规范形式(求值总是能在有限规约步骤后终止),则具有强规范化(strong normalization) 性质。 |
1290 | -实现应避免引起无法保证强规范化性质的操作(如直接无条件的递归规约调用)。 | |
1294 | +实现应避免引起对象语言的语义表达以外的无法保证强规范化性质的操作(如直接无条件的递归规约调用)。 | |
1291 | 1295 | 除非派生实现另行指定,不保证强规范化性质。 |
1292 | 1296 | 保证得到范式的规约是规范化规约。 |
1293 | 1297 | 具体求值得到的范式若可作为表达式,其求值结果(@4.1) 和被求值的项等价,即仅允许恒等求值(@4.4.4) 而仍是范式;这样的项称为自求值项(self-evaluating term) 。 |
@@ -1298,7 +1302,7 @@ | ||
1298 | 1302 | 第一个子表达式(头表达式)是范式的表达式是 HNF(Head Normal Form ,头范式)。 |
1299 | 1303 | 头表达式是可直接求值为范式的表达式是 WHNF(Weak HNF,弱头范式)。 |
1300 | 1304 | 约定求值到 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) 。 | |
1302 | 1306 | WHNF 中除了操作符以外的子表达式是操作数(operand) 。 |
1303 | 1307 | 操作数以具有限定顺序或不限定顺序的数据结构表示,典型代表分别是操作数列表(operand list) 和操作数树(operand tree) 。 |
1304 | 1308 | 操作数树是有限的树形数据结构的 DAG(@4.2.4) (和 Kernel 类似),其具体构造和表示由派生实现定义。 |
@@ -1311,10 +1315,10 @@ | ||
1311 | 1315 | 表达式和子表达式之间的求值满足以下递归蕴含规则: |
1312 | 1316 | 求值依赖规则:除非另行指定,表达式被求值实质蕴含子表达式(@3.4.2) 被求值。 |
1313 | 1317 | 顺序依赖规则:子表达式值的计算先序(@4.4.3) 所在的表达式值的计算。 |
1314 | -平凡求值规则:指定一个表达式是平凡求值(@4.4.4) 实质蕴含其子表达式的求值被指定为平凡求值。 | |
1318 | +平凡求值规则:指定一个表达式是纯求值或空求值(@4.4.4) 对应实质蕴含其子表达式的求值被指定为纯求值或空求值。 | |
1315 | 1319 | **注释** |
1320 | +一般地,一些求值策略(@4.4.6.5) 可以不遵循求值依赖规则。一个具体的典型例外是 WHNF 可约定只求值部分子表达式实现这些求值策略;参见 vau 抽象(@4.5.2.3) 。 | |
1316 | 1321 | 顺序依赖规则是因果性(@4.4.3) 的具体表现之一。对不被求值的表达式,此规则不生效。构造不同的表达式进行计算可实现和直接违反此规则等效的作用,但因为是不同的表达式,实际上不违反此规则。 |
1317 | -一般地,一些求值策略(@4.4.6.5) 可以不遵循求值依赖规则。一个具体的典型例外是 WHNF 可约定只求值部分子表达式实现这些求值策略;参见 vau 抽象(@4.5.2.3) 。 | |
1318 | 1322 | 附加的顺序依赖规则可由特定的实体构成的表达式的求值隐含指定,如过程调用(@4.5.2.1) 可利用不同的求值策略(@4.4.6.5) 。 |
1319 | 1323 | |
1320 | 1324 | @4.4.6.2 严格性(strictness) : |
@@ -1462,6 +1466,9 @@ | ||
1462 | 1466 | 典型实现的函数指称过程(@4.5.2.1) ,函数调用为过程调用(procedure call) 。 |
1463 | 1467 | 若一个函数的调用仍待返回,则该函数调用是活动的(active) 。 |
1464 | 1468 | 一般地,被调用的函数及函数调用的作用的等价性通常不能被确定。一个重要的子类是不能确定具体表示的合并子的情形(@4.4.5.1) ,参见 @4.5.3.2 。其它函数一般也有类似限制。 |
1469 | +调用总是不蕴含非纯求值(@4.4.4) 的函数是纯函数(pure function) 。 | |
1470 | +**注释** | |
1471 | +另见函数调用的终止保证(@4.8.2) 。 | |
1465 | 1472 | |
1466 | 1473 | @4.5.3.2 合并子: |
1467 | 1474 | 除非另行指定,NPL 假定函数合并满足以下典型情形,即函数合并的操作符(@4.4.5.1) 求值为以下类型的合并子(combiner) 之一: |
@@ -1520,11 +1527,13 @@ | ||
1520 | 1527 | @4.6.1 求值环境: |
1521 | 1528 | 求值环境(evaluation environment) 是在求值(@4.1) 时可访问的隐式上下文,提供可通过名称解析(@4.3.3) 访问的变量的绑定(@4.1) 。 |
1522 | 1529 | 能被通过名称解析成功访问变量的绑定是可见的(visible) 。 |
1523 | -同 [RnRK] ,环境包含(contain) 局部绑定(local binding) ,即不通过环境以外的对象即保证可见的被绑定实体(@5.7.1) 。 | |
1530 | +环境包含(contain) 局部绑定(local binding) ,即不通过其它环境即保证可见的被绑定实体(@5.7.1) 。 | |
1524 | 1531 | 按绑定的定义,求值环境的局部绑定集合即变量的名称和通过声明引入的被变量表示的实体构成的映射。 |
1525 | -同 [RnRK] ,环境展示(exhibit) 可见的绑定。 | |
1532 | +环境展示(exhibit) 可见的绑定。 | |
1526 | 1533 | 不和实现环境(@1.2.3) 相混淆的情况下,求值环境简称(变量或对应的本地绑定所在的)为环境(environment) 。 |
1527 | 1534 | 一个环境是空环境(empty environment) ,当且仅当其中包含的局部绑定集合是空集。 |
1535 | +**注释** | |
1536 | +包含和展示的定义同 [RnRK] 。 | |
1528 | 1537 | |
1529 | 1538 | @4.6.1.1 实现环境提供的求值环境: |
1530 | 1539 | 实现环境可能在实现以外提供附加的求值环境(@4.6.1) 作为任务通信的机制,如环境变量。 |
@@ -1543,23 +1552,44 @@ | ||
1543 | 1552 | 典型的实例为由 ISA 约定的通用架构寄存器的状态,可能需要在函数调用(@4.5.3.1) 或任务切换过程中保存和重置。 |
1544 | 1553 | 除非派生实现另行指定,语言不提供访问互操作上下文的公开接口。 |
1545 | 1554 | |
1546 | -@4.7 类型(type) : | |
1547 | -上下文中和特定的实体直接关联或间接关联的元素,满足某个执行阶段的不变量约束(@2.4.3) 。 | |
1555 | +@4.7 类型: | |
1556 | +类型(type) 是上下文中和特定的实体直接关联或间接关联的元素,满足某个执行阶段的不变量约束(@2.4.3) 。 | |
1557 | +实体关联的类型可能被显式地指定,或通过隐式的限定规则推断确定。符合指定和限定要求的类型可有任意多个。 | |
1558 | +实体的类型是被显式指定的实体关联的类型。实体具有实体的类型以及通过其它规则限定的类型。实体是类型的实例(instance) 。 | |
1559 | +类型可用集合表示(@4.1.1) 。集合的元素是具有其表示的类型的实体。 | |
1548 | 1560 | 和表达式直接关联的类型满足起始阶段不变量约束,称为静态类型(static type) 。 |
1549 | 1561 | 和表达式的值(@4.1) 关联的类型满足运行阶段(@2.4.1) 的不变量约束,称为动态类型(dynamic type) 。 |
1550 | 1562 | 其它可能存在类型或实现执行阶段的扩展由派生实现定义。 |
1551 | 1563 | 除非另行指定,对象的类型是对象的值(@4.1) 的类型。 |
1552 | 1564 | NPL 对象类型和存储的值(@4.1.1) 的类型之间的关联未指定。 |
1565 | + | |
1566 | +@4.7.1 类型系统和类型机制: | |
1553 | 1567 | 称为类型的具体实体和之间的关联由派生实现的类型系统(type system) 规则指定。 |
1554 | 1568 | 默认类型系统不附加约束,所有表达式或关联的项都没有指定类型(untyped) ,为退化的平凡类型系统(trivial type system) 或单一类型系统(unityped system) ,实质上是动态类型。 |
1555 | 1569 | 对类型系统的分类中,类型也指确定类型的过程称为类型机制(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) 。 | |
1560 | 1580 | 名义类型之间没有隐含的等价关系。结构化类型之间的等价关系由实现定义。 |
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) : | |
1563 | 1593 | 根据是否需要特定的文法元素指定和项关联的类型即类型标注,对确定类型的机制可进行分类。 |
1564 | 1594 | 类型系统可使用显式类型(explicit typing) ,即要求类型标注。 |
1565 | 1595 | 不使用类型标注确定的类型是隐式类型(implicit typing) 。 |
@@ -1567,31 +1597,48 @@ | ||
1567 | 1597 | 不使用清单类型而使隐式引入的实体(如值(@4.1) )关联具体类型的机制称为潜在类型(latent typing) 。 |
1568 | 1598 | 清单类型是显式类型的实例;除此之外,显式类型还包括铸型(casting) ,即显式指定表达式求值的结果应具有的类型。 |
1569 | 1599 | 潜在类型是隐式类型的实例;除此之外,隐式类型还包括类型推断(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) 。 | |
1573 | 1601 | 语言可能个别指定引入这些类型相关的规则,在保持逻辑相容的前提下可混合使用。 |
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) : | |
1579 | 1614 | 类型检查解答程序是否满足类型规则的判定性问题。 |
1580 | 1615 | 使用语义分析或运行时(@2.4.1) 的类型检查分别为静态类型检查和动态类型检查。 |
1581 | 1616 | 静态类型检查规则是可诊断语义规则(@2.5.1) 。 |
1582 | 1617 | 语言可能个别指定引入类型检查相关的规则,在保持逻辑相容的前提下可混合使用。 |
1583 | -注意静态类型检查和静态类型(@4.7.2) 以及动态类型检查和动态类型(@4.7.2) 的区别。类型检查和类型机制是不同的规则,不必然包含蕴含关系。 | |
1618 | +注意静态类型检查和静态类型(@4.7.3) 以及动态类型检查和动态类型(@4.7.3) 的区别。类型检查和类型机制是不同的规则,不必然包含蕴含关系。 | |
1584 | 1619 | 类型检查失败引起的错误(@2.5.2) 称为类型错误(type error) 。 |
1585 | 1620 | |
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) 。派生语言可指定不同的规则。 | |
1589 | 1625 | 除非派生实现另行指定,程序的用户不能依赖语言规则的限定枚举类型全集中的所有类型。 |
1590 | 1626 | |
1591 | -@4.7.5 类型谓词(type predicate) : | |
1627 | +@4.7.6 类型谓词(type predicate) : | |
1592 | 1628 | 判断值是否满足类型居留(inhabitant) 的谓词是类型谓词。 |
1593 | 1629 | 和 [RnRK] 的基本类型谓词不同,类型谓词定义为只接受一个参数。 |
1594 | 1630 | |
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 | + | |
1595 | 1642 | @4.8 程序的控制执行条件(execution condition) : |
1596 | 1643 | 和 @4.5.2.1 类似,程序的控制状态(@4.1) 决定求值使用的续延。 |
1597 | 1644 | 更一般地,规约规则指定语言的实现决定程序行为时使用的(对程序不保证可见的)续延,这种在实现中对应的控制状态称为控制执行条件。 |
@@ -1599,7 +1646,7 @@ | ||
1599 | 1646 | 除非另行指定,仅由默认规约规则决定的执行条件是正常(normal) 的。 |
1600 | 1647 | 一个主要实例:以当前续延返回的合并子调用返回(@4.5.3.3) 是正常执行的。 |
1601 | 1648 | 改变程序的正常的控制要求存在控制作用(@4.1) ,此时,控制执行条件是非正常(abnormal) 的。 |
1602 | -一个主要实例:除非另行指定,隐含在默认规约规则确定的函数应用外的续延调用(@4.5.3.3) 是非正常的。 | |
1649 | +一个主要实例:除非另行指定,隐含在默认规约规则确定的函数应用(@4.5.3) 外的续延调用(@4.5.3.3) 是非正常的。 | |
1603 | 1650 | 具有规约语义的语言总是支持正常控制条件。NPL 中,非正常的控制条件的支持是可选的。 |
1604 | 1651 | |
1605 | 1652 | @4.8.1 异常(exception) : |
@@ -1689,17 +1736,17 @@ | ||
1689 | 1736 | 名称解析失败(@4.3.3) 可被忽略而不终止(@4.4.5) 实现演绎; |
1690 | 1737 | 保证名称表达式求值的强规范化(@4.4.5)。 |
1691 | 1738 | 不要求提供命名空间(@4.3.4) 实现的可变实体。 |
1692 | -不保证求值都是纯求值;非特殊形式使用热情求值;其它情形使用热情求值或惰性求值(@2.5.2) 由具体特殊形式约定。 | |
1739 | +不保证求值都是纯求值(@4.4.4) ;非特殊形式使用热情求值;其它情形使用热情求值或惰性求值(@2.5.2) 由具体特殊形式约定。 | |
1693 | 1740 | 对象语言的函数(@4.5.2) 默认为过程(@4.5.2.1) 。过程默认实现为子例程(@4.5.2.1) 。过程指定的计算结果和函数表达式最终返回结果的关联(@4.5.2.1) 是过程调用(@4.5.3.1) 结果的一一映射。 |
1694 | 1741 | 除非另行指定,实现函数(@4.5.2) 的宿主数据结构生存期要求默认同宿主语言;循环引用(@4.2.4) 可能行为未定义(@5.4)(另见内存泄漏(@5.6.4) )。 |
1695 | 1742 | 除非另行指定,按值传递(@4.4.6.5) 支持复制初始化(@5.8.2) 对象的一等作用(@4.2.2.2) 。这和宿主语言的对应语义也保持兼容。 |
1696 | 1743 | 派生实现使用的项不满足特定的表示(@6.10.11) 。 |
1697 | 1744 | |
1698 | 1745 | @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) 的需要,使用的类型检查规则和宿主语言一致。 | |
1703 | 1750 | 宿主语言对象的值描述状态且宿主语言要求的对 volatile 左值的操作也属于可观察行为(@4.1.3) 。 |
1704 | 1751 | |
1705 | 1752 | @5.3 互操作(@1.2.3) 支持: |
@@ -1747,8 +1794,8 @@ | ||
1747 | 1794 | 对象语言的值(@4.1) 被对象语言的实体类型表示蕴含它被映射的宿主类型表示,反之亦然。 |
1748 | 1795 | 类型映射可以是非空的多对一、一对多或一一映射。 |
1749 | 1796 | 若类型映射是一一映射,其类型等价性同宿主语言的语义规则;否则,由类型的语义规则约定。 |
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) 。 | |
1752 | 1799 | 因需提供与作为宿主语言的 C++ 的互操作(@5.3) 支持,所以明确约定实现中部分实体类型对应的 C++ 类型: |
1753 | 1800 | 用于条件判断的单一值的宿主类型是 bool 。 |
1754 | 1801 | 字符串(@3.2) 及和字符串的子集一一对应的词素(@3.3.1) 的宿主类型都是 string 类型(@6.1.5) 。 |
@@ -1899,25 +1946,31 @@ | ||
1899 | 1946 | 被求值的表达式使用被规约项(@6.2) 作为内部表示。 |
1900 | 1947 | |
1901 | 1948 | @5.8.1 值类别(@4.2.2.1) : |
1902 | -和 ISO C++17(由提案 [WG21 P0135R1] 引入的特性)类似,表达式归类为具有以下值类别之一: | |
1949 | +表达式归类为具有以下值类别之一: | |
1903 | 1950 | 泛左值(glvalue) :求值用于决定被表示的对象的同一性(@4.1) 的表达式; |
1904 | 1951 | 纯右值(prvalue) :求值不用于决定对象同一性(而仅用于初始化(@5.8.2) 临时对象(@5.8.5) 或计算对象中存储的值(@4.1.1) )的表达式。 |
1905 | 1952 | 一个泛左值可能被标记为消亡值(xvalue) 或附加其它元数据(如通过间接的引用(@4.2.3.3.1) ),以提供基于不同的所有权的行为。 |
1953 | +**注释** 这和 ISO C++17(由提案 [WG21 P0135R1] 引入的特性)类似。 | |
1906 | 1954 | 纯右值蕴含对象在可观察行为(@4.1.3) 的意义上不被共享,类似不被别名的引用的被引用对象(@4.2.3) 不被共享(@4.2.3.4.3) 。 |
1907 | 1955 | 左值(lvalue) 是除了消亡值外的泛左值。 |
1908 | 1956 | 右值(rvalue) 是消亡值或纯右值。 |
1909 | 1957 | 求值涉及表达式的值类别仅在必要时约定。 |
1910 | 1958 | 值类别根据是否只关心表达式关联的(对象的或非对象的)值,在需要对象时提供区分两类一等对象(@4.2) 的机制,同时避免在仅需要表达式关联的值时引入不必要的其它对象。 |
1911 | -和 ISO C++ 类似,表达式的值类别是上下文相关的,相同表达式构造在不同的上下文可能具有不同的值类别。 | |
1912 | -和 ISO C++ 不同,NPLA 表达式允许在源语言语法之外的形式被间接构造,这些表达式同样具有值类别。 | |
1959 | +表达式的值类别是上下文相关的,相同表达式构造在不同的上下文可能具有不同的值类别。 | |
1960 | +**注释** 这和 ISO C++ 类型。 | |
1961 | +NPLA 表达式允许在源语言语法之外的形式被间接构造,这些表达式同样具有值类别。 | |
1962 | +**注释** 这和 ISO C++ 不同。 | |
1913 | 1963 | 求值规约(@4.4.1) 可能重写一个表达式为具有不同值类别的其它形式的表达式。 |
1914 | -和 ISO C++ 不同,一般地,NPLA 的表达式不限定从源代码(@4.1.1) 的翻译(@2.4.1) 确定,且一个表达式的求值结果不排除继续构成表达式而被求值,因此表达式的值也普遍具有值类别。 | |
1964 | +一般地,NPLA 的表达式不限定从源代码(@4.1.1) 的翻译(@2.4.1) 确定,且一个表达式的求值结果不排除继续构成表达式而被求值,因此表达式的值也普遍具有值类别。 | |
1965 | +**注释** 这和 ISO C++ 不同。 | |
1915 | 1966 | 除非另行指定,若一个 NPLA 表达式没有指定未被求值,则其值类别是其求值结果(@4.1) 的值类别。 |
1916 | 1967 | 另见引用(@4.2.3) 对一等状态(@4.2.2.1) 的支持(@4.2.3.3.2) 。 |
1917 | 1968 | |
1918 | 1969 | @5.8.1.1 类型系统和表示: |
1919 | -值类别在 NPLA 中被作为一种内建的类型系统,参见 @4.2.6 。 | |
1970 | +NPLA 中,值类别作为实体类型(@4.2.6) ,被作为一种内建的类型系统。 | |
1920 | 1971 | 内部表示允许存在附加的相关性(@6.3.1) 。 |
1972 | +**注释** | |
1973 | +这和 ISO C++ 不同。ISO C++ 的“类型”的定义排除值类别,尽管值类别具有类型论意义上所有可作为类型讨论的对象的性质。 | |
1921 | 1974 | |
1922 | 1975 | @5.8.1.2 设计原理: |
1923 | 1976 | 按已有的规则,NPLA 约定求值至少需要支持以下的重要的性质: |
@@ -1930,7 +1983,8 @@ | ||
1930 | 1983 | 第三,求值满足递归蕴含规则(@4.4.6.1) ; |
1931 | 1984 | 第四,允许对象的操作保持资源局域性,满足一等对象不依赖引用的抽象(@4.2.3) 同时允许按需区分是否依赖引用的两类一等对象; |
1932 | 1985 | 第五,避免需要假设存在外部具有被引用对象的所有权的所有者(@5.6.4) 。 |
1933 | -第三条性质保证表达式的作用(@1.2.4) 是可组合的并允许求值表达为树规约(@6.2) ,还保证能总是通过子表达式的值类别决定表达式的值类别。因为被求值的表达式是有限的,判定过程是总是能终止,即便求值不满足强规范化性质(@4.4.5) 。 | |
1986 | +第三条性质保证求值表达式的作用(@1.2.4) 是可组合的,并允许求值表达为树规约(@6.2) ,还保证能总是通过子表达式的特定的性质判定表达式的对应性质而不需求值。 | |
1987 | +因为被求值的表达式是有限的,判定过程是总是能终止,即便求值不满足强规范化性质(@4.4.5) 。 | |
1934 | 1988 | 第四条性质要求提供泛左值总是能作为纯右值使用的机制和通过纯右值引入对象的机制,详见值类别转换(@5.8.4) 。 |
1935 | 1989 | 第五条性质要求在表达式之外不存在地位相同的对象的存储资源(@5.6) 的所有者,限定了被决定同一性的对象的外延;存储由环境提供(@5.6) ,其中不需要保存对象的引用。 |
1936 | 1990 | 不像 ISO C++ 一样仅限于静态类型系统范畴,NPLA 值类别的作用机制和 ISO C++ 存在一些明确的不同: |
@@ -1989,14 +2043,14 @@ | ||
1989 | 2043 | **注释** 这避免在可能访问引用值的操作随意引入具有潜在未定义行为风险。 |
1990 | 2044 | 访问无效的引用值不满足内存安全(@5.6.3) 而引起未定义行为。 |
1991 | 2045 | **注释** |
1992 | -更一般的其它的实体有效性参见间接值(@6.4.4) 的有效性。 | |
2046 | +更一般的其它的实体有效性参见间接值的有效性(@6.4.4) 。 | |
1993 | 2047 | |
1994 | 2048 | @5.8.3.2 多重引用: |
1995 | 2049 | 和宿主语言不同,被引用对象也可以是引用值。 |
1996 | 2050 | 被引用对象不是引用值的引用值是完全折叠(fully collapsed) 的。 |
1997 | 2051 | 除非另行指定,未折叠的引用值指未完全折叠的引用值。 |
1998 | 2052 | **注释** |
1999 | -引用值是被引用对象的间接值(@6.4.4) 。 | |
2053 | +引用值是被引用对象的间接值(@6.4) 。 | |
2000 | 2054 | |
2001 | 2055 | @5.8.3.3 引用值的属性(property) : |
2002 | 2056 | 引用值可以具有属性,保存其作为一等对象的状态。 |
@@ -2019,7 +2073,7 @@ | ||
2019 | 2073 | 和 ISO C++ 核心语言(但不是 [res.on.arguments] 中的标准库绑定到右值引用实际参数的约定)的右值引用类似,唯一引用不总是表示被引用对象不被共享。 |
2020 | 2074 | 接受唯一引用的操作可能只假定被引用对象的子对象(@5.6.6) 不被共享,也可能完全不进行假定,这依赖具体操作的语义。若需要和具体操作无关的无条件非共享假定,使用纯右值(@5.8.1) 而非作为左值的唯一引用。 |
2021 | 2075 | 和宿主语言的 const 限定类型类似,不可修改引用仅针对特定左值的访问;通过共享的其它未被限定的引用仍可修改对象。 |
2022 | -违反不可修改引用引入的假定的错误可能通过类型检查(@4.7.3) 或其它方式引起。 | |
2076 | +违反不可修改引用引入的假定的错误可能通过类型检查(@4.7.4) 或其它方式引起。 | |
2023 | 2077 | 临时对象引用类似 ISO C++ 的转发引用(forwarding reference) 中保留在表达式声明中的类型信息。 |
2024 | 2078 | 因为 NPLA 不支持声明元数据,这些信息保存在对象的表示中,且在初始化时被引用值保存;也因此 NPLA 的这可跟随一等对象传递(@6.2.2.2) 。 |
2025 | 2079 | 这也和宿主语言不同。在宿主语言中: |
@@ -2036,7 +2090,7 @@ | ||
2036 | 2090 | 但是,消除引用不一定总是预期这种性质,特别当折叠不被预期时。 |
2037 | 2091 | 例如,ISO C++ 内建指针的不同级 const 不会被隐式转换直接折叠合并。消除间接的指针值不是隐式的(而依赖内建一元 * 操作符),这是因为指针作为类型构造器自身的类型安全需要;是否消除 const 限定符仍然需要基于其它理由考虑。 |
2038 | 2092 | 而当被引用对象实现子对象时,修饰被指向的类型的 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) 。 | |
2040 | 2094 | 在 NPLA 这样没有要求显式类型编码是否可变的语言中,首先要求总是具有不可修改的传播性质会显著增加规则形式上的复杂性。若具体操作需要传播不可修改性,仍可进一步约定。 |
2041 | 2095 | 不涉及引用值的消除时,NPLA 对象的子对象的不可修改性一般也直接依赖具体元素(如不可修改引用作为列表元素)。除不可修改引用蕴含的对直接引用的被引用对象不可修改外,是否满足需要传播不可修改性依赖具体操作的语义。 |
2042 | 2096 | **注释** |
@@ -2144,11 +2198,11 @@ | ||
2144 | 2198 | 这和初始化非引用值类似,但实现需区分是否初始化的是延长生存期的临时对象,以确保之后能区分是否按引用绑定。 |
2145 | 2199 | |
2146 | 2200 | @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) 。 | |
2148 | 2202 | NPLA 表达式的类型是表达式求值结果(@4.1) 的类型。 |
2149 | 2203 | 空求值(@4.4.4) 的求值结果要求未求值的合式的(@2.5.1) 表达式应具有和语法分析(@6.1.6) 的输出兼容的类型,参见 @6.2.1 和 @6.8.1 。 |
2150 | 2204 | 注意表达式的类型和 [R7RS] 的 expression type 无关,后者是语法形式(@3.4.3) 的约定(在 [R5RS] 和 [R7RS] 中称为 form );因为存在合并子(@4.5.3.2) 作为一等对象(@4.1) 的类型,不需要这种约定。 |
2151 | -实现对特定的上下文的表达式可使用类型推断(@4.7.2) 。由此确定的类型类似宿主语言的表达式的类型。 | |
2205 | +实现对特定的上下文的表达式可使用类型推断(@4.7.3) 。由此确定的类型类似宿主语言的表达式的类型。 | |
2152 | 2206 | 和 Scheme 和 Kernel 不同而类似宿主语言,表达式具有值类别(@5.8.1) 。 |
2153 | 2207 | 值类别的指派是类型系统的一部分(@5.8.1.1) 。但除非另行指定,类似宿主语言,值类别和表达式的类型正交,以简化操作涉及类型的指定。 |
2154 | 2208 | 和宿主语言不同,语言机制不限制值类别和其它类型的判断操作和结果(@4.1) 被作为一等对象(@4.2.5) ,尽管不一定直接提供。 |
@@ -2382,9 +2436,9 @@ | ||
2382 | 2436 | TermNode 中保存子节点的容器。TermNode 可作为规约的输入,即被规约项;此时,TermNode 的子节点对象是子项(@4.1) 的表示(@1.2.4) 。 |
2383 | 2437 | TermNode 的 Value 数据成员称为值数据成员(value data member) ,是 ValueObject 类型的对象。用于表示对象语言( NPLA 实现)中表达式或对象储存的值,或者包装的中间值(@4.4.6.2) 。 |
2384 | 2438 | ValueObject 保存不同动态类型的目标对象(target object) ,可在对象语言程序运行时确定宿主语言对应的对象类型,结合类型映射(@5.5) 实现潜在类型(@5.8.6) 。 |
2385 | -**注释** ValueObject::GetObject 是无检查访问目标对象的基本接口。其它通常使用类型擦除(type erasure) 实现的类型,如 std::function 的实例,也有类似的目标对象的概念。 | |
2439 | +**注释** ValueObject::GetObject 是无检查访问目标对象的基本接口。其它通常使用类型擦除(@4.7.3) 实现的类型,如 std::function 的实例,也有类似的目标对象的概念。 | |
2386 | 2440 | TermNode 对子节点和值数据成员具有直接的独占所有权。 |
2387 | -作为子节点的容器,TermNode 子节点的迭代器、指针和引用保持稳定,即移除子节点时不无效化(invalidate) 其它项的迭代器、指针和引用。 | |
2441 | +作为子节点的容器,TermNode 子节点的迭代器、指针和引用保持稳定,即移除子节点时保持其它项的迭代器、指针和引用有效而不被无效化(invalidated) 使访问引起宿主语言的未定义行为(@5.4) 。 | |
2388 | 2442 | TermNode 子节点的稳定性也支持项被转移时(如维护临时对象的内部存储(@5.6.2) )不需要附加操作维护已在其它位置被引用的子项有效性。 |
2389 | 2443 | 针对 TermNode 的一些操作如移除第一个子节点的 RemoveHead 一般使用 ADL 方式调用。 |
2390 | 2444 | 树规约可按需添加或删除 TermNode 的子节点。具体添加或删除的时机未指定,取决于具体的规约算法。 |
@@ -2498,14 +2552,23 @@ | ||
2498 | 2552 | 作为 NPL 语义(@4) 的扩展,作用于非表达式项上的规约规则不是求值规则。 |
2499 | 2553 | |
2500 | 2554 | @6.3.1 值类别: |
2501 | -表达式的值类别(@5.8.1) 也在适用表达式项(@6.3) 及在表达式求值的规约中得到的项。 | |
2555 | +表达式项(@6.3) 及在表达式求值的规约中得到的项具有表达式的值类别(@5.8.1) 。 | |
2502 | 2556 | 作为名称表达式(@4.5.1) 明确引用直接存储在环境中的对象及其包含的(数据成员)子对象(@5.6.6) 的项都是左值。 |
2503 | 2557 | 右值一般存储在环境以外的项上;必要时,实现可能存储右值在特定的环境中。 |
2504 | 2558 | 在类型的值的内部表示上,值类别和特定的类型相关: |
2505 | -当前 NPLA 支持的左值都是项引用(@6.8.3) ,这同时表示引用值(@6.3.4) 。 | |
2559 | +NPLA 支持的左值都是项引用(@6.8.3) ,这同时表示引用值(@6.3.4) 。 | |
2506 | 2560 | NPLA 一等对象的值的类型和它作为表达式项的值类别存在以下一一对应关系: |
2507 | 2561 | 若类型是引用,则对应的值类别是泛左值;否则,对应的值类别是右值。 |
2508 | 2562 | 引用值的不同类型指定更精确的具体对应关系(@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 不要求在语言的全局设计中引入变量类型。 | |
2509 | 2572 | |
2510 | 2573 | @6.3.1.1 值的标签: |
2511 | 2574 | 因为 NPLA 使用潜在类型(@5.2.6) ,表达式不保证提供静态类型(@4.7) 能编码值类别和类型信息;作为替代,这些信息通过规约范式中的项的标签保存。其中: |
@@ -2750,7 +2813,7 @@ | ||
2750 | 2813 | NPL::NPLException 是 NPL 实现的异常基类。 |
2751 | 2814 | 其它 NPL 异常都从此类直接或间接派生。 |
2752 | 2815 | 这些异常类在 NPLA 模块中以 Doxygen 命令标识为 \ingroup exceptions 。 |
2753 | -一般地,使用确切的异常类型以明确更具体的错误条件,如列表类型(@6.2.1) 错误代替一般的类型错误(@4.7.3) 。 | |
2816 | +一般地,使用确切的异常类型以明确更具体的错误条件,如列表类型(@6.2.1) 错误代替一般的类型错误(@4.7.4) 。 | |
2754 | 2817 | 除非规约节点操作(@6.15.3.1) 外,NPLA 实现可能抛出这些异常或派生实现定义的派生这些类的其它异常。 |
2755 | 2818 | NPLA 实现可能抛出标准库异常。 |
2756 | 2819 | 一般地,不通过对象语言构造(而仅通过互操作(@5.3) 或实现缺陷)引起的异常(如检查宿主值(@6.3) 的值域(@6.3) ),不使用 NPL 异常基类。 |
@@ -2820,7 +2883,7 @@ | ||
2820 | 2883 | Environment::EnsureValid(@6.8.1) ,失败时抛出异常; |
2821 | 2884 | shared_ptr<Environment> 值作为当前环境(@5.7.3) 的来源,同时提供指定为断言和具有异常的接口,参见上下文类(@6.11.3.1) ; |
2822 | 2885 | 由 NPL_NPLA_CheckParentEnvironment(@6.1.2.1) 配置的可选的附加检查,失败时同 Environment::EnsureValid 。 |
2823 | -对象语言的动态类型的值需要进行环境类型的类型检查(@4.7.3) 时,只区分对象语言中的类型,不特别排除具有宿主类型的非宿主值,因此不断言失败; | |
2886 | +对象语言的动态类型的值需要进行环境类型的类型检查(@4.7.4) 时,只区分对象语言中的类型,不特别排除具有宿主类型的非宿主值,因此不断言失败; | |
2824 | 2887 | 其余情形可能静态确定不存在来自可能通过互操作(@5.3) 来源取得的非空值,此时一般使用断言而不是异常。 |
2825 | 2888 | |
2826 | 2889 | @6.8.2.2 循环环境引用和环境引用所有权: |
@@ -3175,8 +3238,8 @@ | ||
3175 | 3238 | 一次规约后,被规约项中的值数据成员或子项仍然可保留其它状态而非范式;对表示求值的情况,也不是正规表示(@6.10.7) 。 |
3176 | 3239 | 因为规范化规约可能存在副作用(@6.10.3.2),NPLA 约定求值得到正规表示的规范化规约在抽象机(@2.6) 的意义上总是被进行,称为正规化操作。 |
3177 | 3240 | 纯值规约(@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) 。 | |
3180 | 3243 | 由纯值规约的语义,被清理的子项和被清除的标签不应影响项作为求值结果的表示。若应清理的子项存在,则清理前为平凡(trivial) 的非正规表示。 |
3181 | 3244 | 若需避免子项的生存期扩展到所在的项,需确保对平凡非正规表示总是存在可预期的清理操作。 |
3182 | 3245 | 纯值规约中存在的清理、修改标签和修改值数据成员的作用仍非决定性有序(@6.2) ,因此一个项中的清理可能先序(@4.4.3) 最终确定作为值的表示的对值数据成员的修改操作。 |
@@ -3351,14 +3414,14 @@ | ||
3351 | 3414 | @6.11.4.2 上下文切换: |
3352 | 3415 | NPL::EnvironmentSwitcher 类型用于切换上下文(@6.11.3.1) 中的当前环境(@6.11.3) 。 |
3353 | 3416 | |
3354 | -@6.12 异步规约(asynchronized reduction) : | |
3417 | +@6.12 异步规约(asynchronous reduction) : | |
3355 | 3418 | 直接式(direct style) 的语言实现利用宿主语言的函数调用实现图规约(@6.2) ,满足规约函数(@6.10.5) 的调用包含子项的规约,即同步规约(synchronized reduction) 。 |
3356 | 3419 | 非同步规约称为异步规约。异步规约允许在宿主语言中分离部分规约(@6.10.1) 的不同部分的实现,使之公开为一等对象表示控制状态(@4.1) 及被调度。 |
3357 | 3420 | NPLA 支持同步规约和异步规约中立(neutral) 的接口设计(@6.12.5) 。具体的异步规约在派生实现使用。 |
3358 | 3421 | NPLA 实现支持异步规约,以避免 C++ 本机(@2.4.1) 资源限制下不限定数量(@5.10.2) 调用深度时可能引起的宿主语言的未定义行为(@5.4) 。 |
3359 | 3422 | 若资源分配失败,应满足常规宿主资源分配要求(@5.4.1) 。 |
3360 | 3423 | |
3361 | -@6.12.1 异步规约动作(@6.11.3) : | |
3424 | +@6.12.1 异步规约动作: | |
3362 | 3425 | NPLA 中,这通过以规约动作(@6.11.3) 作为 CPS(@6.11.3) 的被跳板(trampoline) 执行的中间值(@4.4.6.2) 例程的机制实现。此时,当前动作(@6.11.3) 是尾动作(@6.11.3) 。 |
3363 | 3426 | 跳板例程设置的规约操作将被异步调用(@6.11.3) ,即当前动作(@6.11.3) 设置为异步动作,在跳板中被循环调用。上下文的重写循环(@6.11.3) 提供一个跳板调用当前动作(@6.11.3) 。 |
3364 | 3427 | 这种方式支持嵌套调用安全(@5.11) 。其它方式实现可能具有更好的性能,但存在限制而不被使用,如: |
@@ -3474,7 +3537,7 @@ | ||
3474 | 3537 | 除非另行指定,特定不精确数的具体的误差是未指定的。 |
3475 | 3538 | 数值的绝对精度(precision) 是其内部表示蕴含的误差的上界的倒数。对确定使用进位制的表示,精度也指精确表示的数值位数。 |
3476 | 3539 | 数值的内部表示中能以实数描述的度量应至少具有整数数量级精度,即误差不大于 1 。 |
3477 | -精确数和不精确数在数值上可能相等,而类型不同。 | |
3540 | +精确数和不精确数在数值上可能相等,而类型不同(@4.7.2) 。 | |
3478 | 3541 | 宿主类型中的本机整数和浮点类型是数值类型的子类型,分别称为 fixnum 和 flonum 。这些类型在项的内部表示(在值数据成员(@6.2) 中)预期直接占据本机存储而不需要动态分配。 |
3479 | 3542 | Fixnum 总是精确数;flonum 总是不精确数。 |
3480 | 3543 | **注释** 当前实现中,所有数值是 fixnum 或 flonum 之一。两者分别是宿主的整数类型(排除字符和 bool 类型)以及浮点数类型。 |
@@ -3485,7 +3548,7 @@ | ||
3485 | 3548 | 对 fixnum ,+0 和 -0 是相等的数值。Flonum 不同符号的零值在值的表示中可以不同,但在数学意义上相等,表示同一个数。实现中的其它和具体表示无关的等价谓词(@4.1.4) 是否表现这种不同是未指定的。 |
3486 | 3549 | 如其它大多数类型,数值类型符合正规表示(@6.10.7) ,只使用值数据成员(@6.2) 表示有效的数据。 |
3487 | 3550 | **注释** |
3488 | -NPLA 的不同的名义(@4.7.1) 数值类型的集合到宿主类型的集合的类型映射(@5.5) 是满射,即本节指定的宿主类型总是关联至少一个能表示它的值的 NPLA 数值类型。 | |
3551 | +NPLA 的不同的名义(@4.7.2) 数值类型的集合到宿主类型的集合的类型映射(@5.5) 是满射,即本节指定的宿主类型总是关联至少一个能表示它的值的 NPLA 数值类型。 | |
3489 | 3552 | 同时,NPLA 数值类型可以映射到其它类型,特别地,NPLA 整数的类型映射目标是宿主语言整数类型和包含整数值的非典型宿主类型(@5.5) 的并。所以,NPLA 数值整体的类型映射不构成简单的一对多或多对多关系。 |
3490 | 3553 | 特殊值同 LIA-1 定义,引用 IEC 60559 的具体值,仅适用于浮点数。 |
3491 | 3554 | 无限大值在数学上属于超实数(hyperreal number) ,在浮点数实现中属于扩展实数(extended real number) 。 |
@@ -3510,7 +3573,7 @@ | ||
3510 | 3573 | 其中,计算结果依赖影响计算结果的操作数,并依赖至少一个数值操作数。 |
3511 | 3574 | 除非另行指定: |
3512 | 3575 | 在数学上有意义的前提下,数值操作同时支持以上尽可能多的数值类型的操作数。 |
3513 | -数值操作对预期的数值操作数进行类型检查(@4.7.3) ,失败时出错,抛出 NPL::TypeError 异常(@6.5) 。 | |
3576 | +数值操作对预期的数值操作数进行类型检查(@4.7.4) ,失败时出错,抛出 NPL::TypeError 异常(@6.5) 。 | |
3514 | 3577 | 数值操作不区分数值操作数中对应的真值相等的精确数或不精确数。 |
3515 | 3578 | 可假定数值操作数和计算过程中不出现 SNaN(signaling NaN) 值; |
3516 | 3579 | 若作为操作数的精确数决定计算结果在数学上未定义,则引起错误。 |
@@ -3564,7 +3627,7 @@ | ||
3564 | 3627 | NumberNode 传递 NPL::ResolvedArg<>(@6.9.4) ,包含被 NPL::ResolveTerm(@6.9.4) 解析后的节点和访问引用的指针( NPL::ResolvedTermReferencePtr(@6.9.4) 类型的值)。 |
3565 | 3628 | **注释** 后者可以对引用中的标签(@6.8.3.1) 进行处理。 |
3566 | 3629 | |
3567 | -@6.14.4 数值类型谓词(@4.7.5) : | |
3630 | +@6.14.4 数值类型谓词(@4.7.6) : | |
3568 | 3631 | 以下一元函数判断 const ValueObject& 参数是否表示特定的数值类型的值: |
3569 | 3632 | IsExactValue |
3570 | 3633 | IsInexactValue |
@@ -3738,7 +3801,7 @@ | ||
3738 | 3801 | @7.1.1 对象语言约定: |
3739 | 3802 | NPLA1 仅使用宿主语言的类型和值作为状态。 |
3740 | 3803 | 在 NPLA 的基础上,NPLA1 要求对象语言支持以一等对象作为表达式并被求值(@6.3) 。 |
3741 | -类型等价性基于类型映射(@5.5) 及其实现(@6.1.7) ,由 C++ 的语义规则定义。 | |
3804 | +类型等价性(@4.7.2) 基于类型映射(@5.5) 及其实现(@6.1.7) ,由 C++ 的语义规则定义。 | |
3742 | 3805 | 值等价性由宿主实现的 == 表达式的结果定义。 |
3743 | 3806 | 除非另行指定,所有类型的外部表示(@4.1.1) 都是允许从作为内部表示的项节点结构确定的同宿主类型(@5.5) 的空字符结尾的字符串(即 ISO C++ 的 NTCTS )。 |
3744 | 3807 | 引入过程(@4.5.2.1) 的具体形式参见 @8.4.5 ,也可约定通过特定的本机函数(@5.3) 等其它方式提供。 |
@@ -3778,7 +3841,7 @@ | ||
3778 | 3841 | 除非另行指定,为在表达式的求值结果中取得折叠的引用值: |
3779 | 3842 | 当被引用对象是被绑定对象时,引用值被折叠一次(@6.8.4.2) ; |
3780 | 3843 | 否则,引用值通过蕴含一次引用值提升转换(@5.8.4) 的方式被消除(@5.8.3.4) 一次。 |
3781 | -除非另行指定,违反不可修改引用引入的假定的修改操作引起类型错误(@4.7.3) 。 | |
3844 | +除非另行指定,违反不可修改引用引入的假定的修改操作引起类型错误(@4.7.4) 。 | |
3782 | 3845 | **原理** |
3783 | 3846 | 访问不作为被绑定对象的子对象时,通常并非如宿主语言为支持推断参数类型的方式使用引用折叠,构造折叠的引用值默认不直接使用引用折叠(除临时对象标签外同 ISO C++ )的规则(@5.8.3.5) ,而直接由被引用对象确定。 |
3784 | 3847 | 对访问列表中的子项构成的子对象引用,这也和宿主语言的涉及成员访问的表达式类似(@6.4.6.1) :除以下不同: |
@@ -4020,7 +4083,7 @@ | ||
4020 | 4083 | 其中,函数成员 operator() 直接提供核心变换算法。 |
4021 | 4084 | 变换包含递归和非递归变换。 |
4022 | 4085 | 变换结果可被进一步规约。 |
4023 | -另见 @7.9.2 。 | |
4086 | +另见异步规约实现(@7.9.2) 。 | |
4024 | 4087 | |
4025 | 4088 | @7.5.3 叶节点分析 : |
4026 | 4089 | 叶节点(@6.2.1) 分析 API 作为标记器(@7.8) 的实现转换词素到包含记号的项,包括: |
@@ -4085,7 +4148,7 @@ | ||
4085 | 4148 | @7.6.2 助手 API : |
4086 | 4149 | NPLA1 提供枚举 WrappingKind 标识初始化 A1::FormContextHandler 需要的包装数(@7.6.1.1) 。 |
4087 | 4150 | 基于 NPL::EmplaceLeaf(@6.11.4.1) ,NPLA1 提供注册处理器事件的助手函数模板,无需显式嵌套构造 A1::ContextHandler 和其它处理器(@7.6.1.1) 类型: |
4088 | -A1::RegisterHandler | |
4151 | +A1::RegisterFormHandler | |
4089 | 4152 | A1::RegisterForm |
4090 | 4153 | A1::RegisterStrict |
4091 | 4154 | 助手函数的模板传入被包装的处理器和检查例程。 |
@@ -4156,7 +4219,7 @@ | ||
4156 | 4219 | 绑定操作(@7.7.3) API 。 |
4157 | 4220 | |
4158 | 4221 | @7.7.2 求值规约操作: |
4159 | -基于 NPLA 合并动作(@6.12.7) ,NPLA1 提供动作合并规约操作: | |
4222 | +基于 NPLA 合并动作(@6.12.7) ,NPLA1 提供动作合并规约操作 API : | |
4160 | 4223 | 类型 A1::EnvironmentGuard |
4161 | 4224 | 函数 A1::RelayForEval 直接求值(@4.1) 规约:规约并传递求值的结果。 |
4162 | 4225 | 函数 A1::RelayForCall 函数调用规约( β 规约(@4.5.3) ):规约并传递词法闭包规则(@4.6.1.2) 下求值的结果。 |
@@ -4164,6 +4227,10 @@ | ||
4164 | 4227 | 为确保嵌套函数调用时允许引用绑定到非直接主调函数的临时对象(@5.8.5) 形式参数,已被匹配作为绑定目标的项所在的不表示操作数的项(参见 @7.7.3 )可能被转移以保证其子项生存期足够长。 |
4165 | 4228 | 按 @6.4.7.2 ,此时被作为绑定目标的子项不需被修改(因此自动符合 @6.4.8 的要求)。 |
4166 | 4229 | 间接调用 A1::RelayForEval 或 A1::RelayForCall 的操作是求值规约操作。 |
4230 | +求值规约操作附带绑定操作匹配被绑定的操作数树(@4.4.5.1) 。 | |
4231 | +求值规约操作中可能具有提升操作(@7.1.4) 。 | |
4232 | +忽略提升操作可允许对象语言的函数调用返回(@4.5.3.1) 引用值(@6.3.4) 。 | |
4233 | +**注释** | |
4167 | 4234 | 等效间接调用 A1::RelayForEval 的求值规约操作包括: |
4168 | 4235 | Forms::Eval(@8.4.4.1) |
4169 | 4236 | Forms::EvalRef(@8.4.4.1) |
@@ -4171,9 +4238,6 @@ | ||
4171 | 4238 | Forms::EvalStringRef(@8.4.4.1) |
4172 | 4239 | Forms::Apply(@8.4.9) |
4173 | 4240 | 间接调用 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) 。 | |
4177 | 4241 | |
4178 | 4242 | @7.7.2.1 求值规约提升项: |
4179 | 4243 | 关于提升项的基本用例,参见 @6.4.6.3 。 |
@@ -4370,6 +4434,17 @@ | ||
4370 | 4434 | 函数 A1::BindParameterWellFormed |
4371 | 4435 | 函数 A1::BindSymbol |
4372 | 4436 | |
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 | + | |
4373 | 4448 | @7.8 REPL API : |
4374 | 4449 | NPLA1 提供 API 通过解释器的各个子模块组装 REPL(read-eval-print loop) 的交互式界面。 |
4375 | 4450 | 函数 A1::SetupDefaultInterpretation 初始化默认解释,包括一般事件处理例程。 |
@@ -4379,8 +4454,6 @@ | ||
4379 | 4454 | 别名模板 A1::GTokenizer 提供标记器,即和解析器的解析结果类型中的元素类型为参数的转换为 TermNode 的例程。对应解析器类型提供以下实例: |
4380 | 4455 | 类型 A1::Tokenizer |
4381 | 4456 | 类型 A1::SourcedTokenizer |
4382 | -NPLA1 提供一些辅助 REPL 诊断的 API 以 Doxygen 命令标识为 \ingroup NPLDiagnostics(@6.13) 。 | |
4383 | -函数 A1::QueryContinuationName 查询续延名称。 | |
4384 | 4457 | 类 A1::REPLContext 可直接作为 REPL 原型。 |
4385 | 4458 | A1::REPLContext 以 ContextNode::EvaluateList(@6.11.3.1) 为第二参数调用 A1::SetupDefaultInterpretation 初始化,之后默认解释配置 ContextNode 具有以下能在 A1::ReduceOnce(@7.4.4) 运行的规约算法: |
4386 | 4459 | A1::REPLContext 使用 @7.5.3 的 API 实现标记器。 |
@@ -4456,8 +4529,8 @@ | ||
4456 | 4529 | 以 # 、+ 或 - 起始的不能构成标识符的词素是 NPLA 扩展字面量(@5.2.3.2) 。 |
4457 | 4530 | 一些求值算法的性质在实现上由 NPLA1 间接值使用规则(@7.1.3) 、A1::EvaluateIdentifier(@7.6.4) 和 A1::REPLContext 配置的求值遍保证。 |
4458 | 4531 | |
4459 | -@7.9 异步(@6.12.1) 规约支持: | |
4460 | -基本概念参见异步规约(@6.12) 。 | |
4532 | +@7.9 异步规约支持: | |
4533 | +异步规约(@6.12) 支持基于异步规约动作(@6.12.1) 实现。 | |
4461 | 4534 | 在 NPLA1 API 中同时提供对应的完全非异步规约的实现,不满足 TCO 要求(@5.10.4) ,仅供参考。 |
4462 | 4535 | NPLA1 API 的异步规约依赖上下文中的当前动作(@6.11.3) 。 |
4463 | 4536 | 以下讨论的由上下文(@6.11.3) 支持的 NPLA API 使用方式和 NPLA1 相关。其它非 NPLA1 的派生实现可使用其它方式或选择不支持。 |
@@ -4475,6 +4548,7 @@ | ||
4475 | 4548 | 部分 NPLA1 API 提供同步和异步的不同实现。一些 API 明确支持异步规约。 |
4476 | 4549 | 启用异步规约的实现定义为实现选项(@7.1) NPL_Impl_NPLA1_Enable_Thunked 。 |
4477 | 4550 | 此外,启用分隔符中缀变换(@7.5.2) 的异步的实现定义为实现选项 NPL_Impl_NPLA1_Enable_ThunkedSeparatorPass ,使用单独的异步实现保证嵌套调用安全(@6.1.3) 。 |
4551 | +NPL::NPLA1Forms(@8.1) 等同时支持同步和异步规约的 API 可基于这些 API 实现。 | |
4478 | 4552 | |
4479 | 4553 | @7.9.2.1 异步规约基本支持: |
4480 | 4554 | 以下 NPLA1 API 可保存当前动作,支持和实现异步规约: |
@@ -4776,7 +4850,7 @@ | ||
4776 | 4850 | 作为原型实现(@5.1) ,相较性能,NPLA1 在语言设计和实现上都强调扩展性。 |
4777 | 4851 | 即使替换扩展实现实现对象语言语义等价但削弱 NPLA(@6) 和 NPLA1 API 的变体,一般也不能预期能和常见其它典型动态语言的解释器实现。这主要是由于: |
4778 | 4852 | 使用了解释开销较大的 AST 解释器(@6.1.2) ; |
4779 | -缺乏清单类型(@4.7.2) 等提供支持更明确执行路径的元数据; | |
4853 | +缺乏清单类型(@4.7.3) 等提供支持更明确执行路径的元数据; | |
4780 | 4854 | 使用异步规约(@6.12) 和局部的异步调用保证嵌套调用安全(@7.11.7) ; |
4781 | 4855 | 为支持合并子可能具有的动态环境(@4.6.1.2) ,规约合并时(@7.6.4.2) 没有优化,可能有不必要的重复开销。 |
4782 | 4856 | 进一步优化的一般目标是寻找更多可消除的解释开销,这一般可通过以下方法(可能不再使用 NPLA ): |
@@ -4857,7 +4931,7 @@ | ||
4857 | 4931 | (begin (define f (lambda () (f))) (f)) |
4858 | 4932 | |
4859 | 4933 | @7.11.6 续延名称: |
4860 | -模块 NPL::NPLA1 提供特定可由函数 A1::QueryContinuationName(@7.8) 查询得到的续延名称。 | |
4934 | +模块 NPL::NPLA1 提供特定可由函数 A1::QueryContinuationName(@7.7.5) 查询得到的续延名称。 | |
4861 | 4935 | 其中查询结果的名称和 A1::Continuation::Handler 或 Reducer 的特定目标类型关联。 |
4862 | 4936 | 除非另行指定,关联的类型是非公开的内部类型。 |
4863 | 4937 | 在 NPLA1 实现中引入的这些名称包括: |
@@ -5007,7 +5081,7 @@ | ||
5007 | 5081 | Forms::SetRestRef |
5008 | 5082 | |
5009 | 5083 | @8.4.4 环境和求值操作: |
5010 | -Forms 提供环境相关的操作,以支持对象语言中的环境(@9.9.3 ) 。 | |
5084 | +Forms 提供环境相关的操作,以支持对象语言中的环境(@9.9.3) 。 | |
5011 | 5085 | |
5012 | 5086 | @8.4.4.1 求值操作: |
5013 | 5087 | Forms 提供以下函数实现对象语言中的求值(@6.15.1) : |
@@ -5112,7 +5186,7 @@ | ||
5112 | 5186 | Forms::Wrap 包装(@4.5.3.2) 合并子为应用子(@4.5.3.2) 。 |
5113 | 5187 | Forms::WrapOnce 包装操作子为应用子。 |
5114 | 5188 | Forms::Unwrap 解包装(@4.5.3.2) 应用子为合并子。 |
5115 | -其中后两者会对合并子按 @7.6.1 的约定进行类型检查(@4.7.3) ,若不满足条件则抛出异常。 | |
5189 | +其中后两者会对合并子按 @7.6.1 的约定进行类型检查(@4.7.4) ,若不满足条件则抛出异常。 | |
5116 | 5190 | 当被解包装的应用子参数是引用时,构造具有非平凡非正规表示(@6.10.11.1) 的子对象引用(@6.8.3.3) 。 |
5117 | 5191 | |
5118 | 5192 | @8.4.7 错误检查: |
@@ -5239,8 +5313,10 @@ | ||
5239 | 5313 | 在模块 NPL::Dependency 中引入的续延名称包括: |
5240 | 5314 | eval-$binds1?-env |
5241 | 5315 | eval-lazy-parent |
5316 | +get-module-return | |
5242 | 5317 | load-external |
5243 | 5318 | promise-handle-result |
5319 | +require-return | |
5244 | 5320 | 在模块 NPL::Dependency 中,若特定对象语言操作使用非本机实现(@5.3) ,可使用不同的续延名称。 |
5245 | 5321 | |
5246 | 5322 | @8.6.2 和 klisp 续延名称的关系: |
@@ -5255,17 +5331,18 @@ | ||
5255 | 5331 | combine-return :非 TCO 的规约合并求值(@7.6.4.2) 实现中设置规约合并最后的返回结果。 |
5256 | 5332 | equal-subterm :Forms::EqualTermValue(@8.4.1) 比较直接子项。 |
5257 | 5333 | 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 。 | |
5260 | 5335 | 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 嵌套调用抽象列表余下的元素的公共操作。 | |
5262 | 5337 | eval-accl-accl :Forms::AccL 递归调用。 |
5263 | 5338 | eval-accl-tail :Forms::AccL 调用取抽象列表余下的元素的函数。 |
5264 | 5339 | eval-accr-accr :Forms::AccR 递归调用。 |
5265 | 5340 | eval-accr-sum :Forms::AccR 调用取抽象列表部分和的函数。 |
5266 | 5341 | eval-bindings-to-env :Forms::BindingsWithParentToEnvironment(@8.4.9) 在创建的环境中绑定变量并返回。 |
5267 | 5342 | 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) 加载翻译单元时以守卫保护当前环境。 | |
5269 | 5346 | eval-lazy-parent :惰性求值指定父环境求值。 |
5270 | 5347 | eval-let-parent :Forms::LetWithEnvironment 和 Forms::LetWithEnvironmentRef 中求值过程抽象(@8.4.5) 的父环境的表达式。 |
5271 | 5348 | eval-letrec-bind :Forms::LetRec 和 Forms::LetRecRef 中的变量绑定。 |
@@ -5274,14 +5351,16 @@ | ||
5274 | 5351 | eval-map1-appv :Forms::Map1(@8.4.9) 应用子调用。 |
5275 | 5352 | eval-tail :TCO 动作尾上下文求值(和 klisp 不同,不依赖 GC(@5.6.4) 而在 TCO 动作单独保存资源(@7.10.5.1) )。 |
5276 | 5353 | eval-vau-parent :Forms::LambdaWithEnvironment 和 Forms::VauWithEnvironment(@8.4.5) 创建操作子时求值过程抽象的父环境的表达式。 |
5277 | -注意 klisp 的 $vau 不具有应保证被求值的实际参数,不需要异步调用,没有对应的续延。 | |
5354 | +get-module-return :异步规约的 LoadModule_std_io 实现 get-module 返回结果。 | |
5278 | 5355 | load-external :A1::RelayToLoadExternal(@8.5.2) 加载外部翻译单元。 |
5279 | 5356 | match-ptree-recursive :支持延迟递归绑定( Forms::DefineWithRecursion 和 Forms::SetWithRecursion(@8.4.4.3) )。 |
5280 | -promise-handle-result :求值代理结果。 | |
5281 | 5357 | provide-let-return :Forms::ProvideLet(@8.4.9) 返回环境。 |
5358 | +require-return :异步规约的 LoadModule_std_module 实现 require 加载翻译单元后设置和返回结果。 | |
5282 | 5359 | sequence-return :非 TCO 的 A1::ReduceOrdered(@7.4.6) 实现中设置规约合并最后的返回结果。 |
5283 | 5360 | **注释** |
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 共用部分本机实现。 | |
5285 | 5364 | |
5286 | 5365 | @9 NPLA1 核心语言设计: |
5287 | 5366 | NPL 是独立设计的,但其派生语言和其它一些语言有类似之处;这些语言和 NPL 方言之间并不具有派生关系。但为简化描述,部分地引用这些现有语言规范(@1.2.1.2) 中的描述,仅强调其中的不同。 |
@@ -5406,7 +5485,6 @@ | ||
5406 | 5485 | <test> :类似 <object> ,通常预期为 <boolean> ,作为条件。 |
5407 | 5486 | 和 Scheme 类似但和 Kenerl 不同,非 #t 的值在决定分支时视同 #f ,以允许在 <boolean> 外自然扩展的逻辑代数操作。 |
5408 | 5487 | 和 Common Lisp 不同,不使用空列表(或符号 nil )代替 #f ,以避免需要特设的规则以特定的其它类型的值(如 Common Lisp 的符号 t )表示逻辑真(这在逻辑非操作中不可避免)。 |
5409 | -**原理** 和 [RnRK] 的理由不同,允许布尔代数以外扩展的比较判断在此不认为是易错的,而是有意的设计(by design) 。这避免预设地假定类型的名义语用作用(“角色(role)” ),也避免限定语言和派生语言的类型全集(@4.7.4) 。 | |
5410 | 5488 | <combiner> :合并子。 |
5411 | 5489 | <applicative> :应用子(@4.5.3.2) 。 |
5412 | 5490 | <predicate> :谓词,是应用操作数的求值结果的值为 <test> 的 <applicative> 。通常实现结果是 <boolean> 的纯求值(@4.4.4) 。 |
@@ -5418,6 +5496,8 @@ | ||
5418 | 5496 | 此外,支持的数值操作数参见 NPLA 数值类型(@6.14.1) 。 |
5419 | 5497 | **原理** |
5420 | 5498 | <object> 等求值得到的操作数不保证是语法意义上连续的词法组合,不能由多个表达式构成,因此即便出现在元素末尾,也不能如 <body> 一样减少括号。 |
5499 | +<object> 表示类型全集(@4.7.5) ,其元素可被断言在论域(@1.5.3.6) 内,即任何其它类型都是 <object> 的子类型(@9.5.4.1) 。 | |
5500 | +和 [RnRK] 的理由不同,允许布尔代数以外扩展的比较判断在此不认为是易错的,而是有意的设计(by design) 。这避免预设地假定类型的名义语用作用(“角色(role)” ),也避免限制语言和派生语言的类型全集(@4.7.5) 的设计。 | |
5421 | 5501 | |
5422 | 5502 | @9.2.2.4 文法形式补充约定: |
5423 | 5503 | 除非另行指定,以 <symbols> 指定的值被作为 <definiend> 或 <formals> 使用时不引起错误。注意 <symbols> 在被其它上下文使用时仍可能引起错误。 |
@@ -5453,7 +5533,7 @@ | ||
5453 | 5533 | 由 NPLA 约定和 NPLA 记号值(@6.8.1) 的约定,字符串字面量的类型为 <string>(@9.2.2.2) 。 |
5454 | 5534 | 基于 NPLA 数值类型(@6.14.1) ,数值字面量的类型为 <number> 。 |
5455 | 5535 | 除非另行指定,数值的具体宿主类型未指定。 |
5456 | -**注释** 部分数值可指定具体的子类型(@4.7) 。 | |
5536 | +**注释** 部分数值可指定具体的子类型(@4.7.2) 。 | |
5457 | 5537 | |
5458 | 5538 | @9.3.1.2 数值字面量: |
5459 | 5539 | 基于 NPLA 数值类型(@6.14.1) ,NPLA1 支持 <integer> 类型的精确数数值字面量和 flonum(@6.14.1) 不精确数数值字面量。 |
@@ -5653,16 +5733,16 @@ | ||
5653 | 5733 | 函数操作的语义可单独指定检查,具体形式由具体操作指定。 |
5654 | 5734 | |
5655 | 5735 | @9.5.4.1 类型检查: |
5656 | -基于名义类型(@4.7.1) ,对象语言实现应具有语义规则指定的类型检查(@4.7.3) ,以确保程序的执行符合操作的必要前置条件。 | |
5736 | +基于名义类型(@4.7.2) ,对象语言实现应具有语义规则指定的类型检查(@4.7.4) ,以确保程序的执行符合操作的必要前置条件。 | |
5657 | 5737 | 操作的语义可要求以下的类型检查: |
5658 | 5738 | 对求值得到的操作数(@9.2.2.2) ,按文法约定(@9.2.2) 的约束进行类型检查; |
5659 | 5739 | 根据特定对象状态,指定动态类型(如要求对象可修改)的检查。 |
5660 | 5740 | 实现可能添加其它不违反语义要求的类型检查。 |
5661 | 5741 | 基于表达式的类型(@5.8.6) ,对应对象语言表达式的表示实体的元素可指定操作数上明确的类型要求。 |
5662 | -部分实体从属于其它实体类型而构成子类型(subtyping) 关系;部分的规约操作取得求值结果保证结果中的值可能具有的特定类型集合,这些类型也一并在以下描述中给出;其它情形不指定类型。 | |
5742 | +部分实体从属于其它实体类型而构成子类型(@4.7.2) 关系;部分的规约操作取得求值结果保证结果中的值可能具有的特定类型集合,这些类型也一并在以下描述中给出;其它情形不指定类型。 | |
5663 | 5743 | 规约预期符合约束。若违反由项的值对应的动态类型(@6.3) 不匹配导致,则求值失败;否则,行为未指定。 |
5664 | 5744 | 除非另行指定,不同类型检查的作用的顺序未指定。 |
5665 | -类型错误(@4.7.3) 引发错误对象(@9.5.1) 。 | |
5745 | +类型错误(@4.7.4) 引发错误对象(@9.5.1) 。 | |
5666 | 5746 | |
5667 | 5747 | @9.6 外部表示: |
5668 | 5748 | 同 @4.1.1 ,外部表示由派生实现约定。 |
@@ -5746,8 +5826,10 @@ | ||
5746 | 5826 | 函数调用(@4.5.3.1) 中,(使用 $vau 和 $lambda 等合并子抽象引入的)合并子(@9.9.5) 的调用(注意不包括其它途径引入的外部函数的调用); |
5747 | 5827 | 对 <body>(@9.2.2.1) 的求值; |
5748 | 5828 | 对 <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> 的求值。 | |
5751 | 5833 | 函数 apply(@11.4.1) 对 <applicative> 和 <object> 在 <environment> 或默认的动态环境中的应用; |
5752 | 5834 | 函数 accl(@11.4.1) 、map-reverse(@11.4.3) 和 for-each-ltr(@11.4.3) 蕴含的尾上下文的递归调用。 |
5753 | 5835 |
@@ -5767,7 +5849,7 @@ | ||
5767 | 5849 | @9.7.4.4 限制影响: |
5768 | 5850 | 在不保证支持 PTC 时,实现循环可控制结构的程序应注意避免无限递归。如以下 Kernel 程序: |
5769 | 5851 | ($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) ): | |
5771 | 5853 | $def! (f g) (list ($lambda (x) (g (- x 1))) ($lambda (x) (f (- x 1)))) |
5772 | 5854 | 使用任意数值作为参数调用 f 时,若实现不支持 TCO ,以数值调用函数 f 或 g 可引起宿主语言的未定义行为(非异步规约(@7.9) )或最终无法分配存储(异步规约)而不是保持不终止求值。 |
5773 | 5855 | 实际实现当前支持这个例子的 TCO 。 |
@@ -5840,7 +5922,7 @@ | ||
5840 | 5922 | 复制赋值在自赋值时的源操作数复制仍可能出错。使用基本提升操作(@6.9.5.1) 实现时,一般使用 NPL::LiftTermOrCopy 而不是检查自赋值的 NPL::LiftOtherOrCopy 。 |
5841 | 5923 | 类似宿主语言,除非另行指定,赋值操作不保留源操作数的值类别(@9.7.2) 和可修改性。 |
5842 | 5924 | **注释** |
5843 | -赋值不保证子对象(@9.8.2) 的同一性不被改变。 | |
5925 | +赋值不保证子对象(@9.8.2) 的同一性不被改变;子对象的引用仍可能被赋值无效化(@9.8.6) 。 | |
5844 | 5926 | |
5845 | 5927 | @9.8.3.2 转移: |
5846 | 5928 | 转移可导致被转移对象的外部可见的修改。 |
@@ -5865,18 +5947,21 @@ | ||
5865 | 5947 | 为了满足不依赖引用的一等对象(@4.1) ,区分引用和被引用的对象都作为一等实体,这是必要的。 |
5866 | 5948 | 一般地,谓词 eq? 用于判断两个参数对象的同一性(@4.1) 。 |
5867 | 5949 | |
5868 | -@9.8.6 无效化(@6.3.4) : | |
5950 | +@9.8.6 无效化: | |
5869 | 5951 | 若对象的引用值保持有效(@9.9.1) ,则指称的左值的对象同一性(@4.1) 不变。 |
5870 | -作为引用值(@6.3.4) 的派生实现,对象语言中的引用值的无效化包括以下情形: | |
5952 | +作为引用值(@6.3.4) 的派生实现,对象语言中的引用值的无效化(@6.4.4) 包括以下情形: | |
5871 | 5953 | 被引用的对象存储期已结束(此时引用值是悬空引用(@9.4.3.2) ); |
5872 | 5954 | 对象被除通过重绑定(@9.9.3.7) 、赋值(@9.8.3.1) 和另行指定的情形以外的方式修改(@9.8.3) ,而引起对象同一性的改变。 |
5873 | -对项的赋值(@9.8.3.1) 仍可能因为对子项的修改而使其表示的对象的引用值无效化。 | |
5955 | +**注释** | |
5956 | +对项的重绑定或赋值仍可能因为对子项的修改蕴含被替换的对象的销毁,引起子对象的生存期结束,而使其表示的对象的引用值无效化。 | |
5874 | 5957 | |
5875 | 5958 | @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) 等库函数的依赖。 | |
5879 | 5962 | 和 Kernel 不同,列表类型只包括真列表(@9.9.4) 。列表节点的实现的表示详见 @6.2.1 。 |
5963 | +**原理** | |
5964 | +不要求分区这避免全局地假定类型全集(@4.7.5) 的具体表示,并支持开放的类型映射(@5.5) 。 | |
5880 | 5965 | |
5881 | 5966 | @9.9 对象语言数据结构: |
5882 | 5967 | 本节指定在 NPLA1 允许以一等实体(@4.2) 被使用的基本元素。 |
@@ -5931,9 +6016,10 @@ | ||
5931 | 6016 | 语言实现可提供环境对象以外的非一等环境。总是不能被对象语言以一等对象访问的环境是隐藏环境(hidden environment) 。 |
5932 | 6017 | 一般地,隐藏环境是某一个(非隐藏的)一等环境的直接或间接父环境(而能通过求值等间接操作被访问)。 |
5933 | 6018 | |
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) 。 | |
5937 | 6023 | 创建新环境的一个例子是 Vau 抽象(@8.4.5) 实现过程调用。 |
5938 | 6024 | |
5939 | 6025 | @9.9.3.3 环境的宿主值和所有权: |
@@ -5948,7 +6034,7 @@ | ||
5948 | 6034 | 违反关于环境稳定性的要求的程序具有未定义行为(@9.1.4) 。 |
5949 | 6035 | 当前要求确保的稳定性包括: |
5950 | 6036 | 隐藏环境(@9.9.3.1) 的绑定有效稳定性(@9.9.3.7) 和值稳定性(@9.9.3.8) ; |
5951 | -从构造时即明确要求的一等环境(@10.2.1) 的稳定性。 | |
6037 | +从构造时即明确要求的一等环境的稳定性(@10.2.2) 。 | |
5952 | 6038 | 一般地,环境的稳定性要求构造环境时不能依赖非特定的动态环境(作为被名称解析(@6.11.1) 访问的父环境),因为这些环境的绑定可能具有在构造环境之后确定的绑定,而不能确保环境中的名称具有可预知的含义。 |
5953 | 6039 | 环境的稳定性简化分析程序的推理过程,也在许多上下文中允许程序更易被优化。 |
5954 | 6040 | 和 Kernel 不同,环境(@11.3.7) 和合并子操作(@11.3.8) 及基于这些操作的一些派生操作(@11.4) 的操作数树(@7.7.3) 构造时不检查其中的非列表项是否都为符号或 #ignore(而延迟到匹配时检查);匹配时不检查符号重复;若形式参数中的符号重复,则绑定的目标未指定。 |
@@ -5963,7 +6049,12 @@ | ||
5963 | 6049 | 要求隐藏环境稳定允许实现共享隐藏环境作为父环境而提供标准环境(@11.4.1) 。 |
5964 | 6050 | |
5965 | 6051 | @9.9.3.5 环境生存期: |
5966 | -引用环境中的名称时,应确保环境在生存期内。 | |
6052 | +对象语言的实现提供给用户程序(@9.1) 使用的初始环境的环境对象及其中的子对象满足: | |
6053 | +其创建先序于用户程序的对象的创建; | |
6054 | +除非提供为不满足环境稳定性(@9.9.3.4) 的环境中的被绑定对象,其销毁后序于用户程序的对象的销毁。 | |
6055 | +程序引用环境中的名称时,应确保环境在生存期内。 | |
6056 | +**注释** | |
6057 | +不满足环境稳定性的环境的被绑定对象可能被修改。若这个对象是一个环境的唯一强引用,则对应的环境对象在替换为其它值时被销毁。 | |
5967 | 6058 | 特别地,应注意使用函数时引入父环境的生存期。 |
5968 | 6059 | 当前实现对环境引用(@6.11.1) 是 NPL::EnvironmentReference(@6.11.1.1) 的情形提供一定程度的检查(@6.1.2.1) ,这通过引用环境时检查 NPL::EnvironmentReference 中的弱引用是否可被实现。 |
5969 | 6060 | 特别地,这可针对在 vau 抽象(@8.4.5) 中捕获的静态环境。 |
@@ -5974,7 +6065,7 @@ | ||
5974 | 6065 | 其它情形不受检查。 |
5975 | 6066 | 注意正常的操作不应有生存期错误。 |
5976 | 6067 | 引起这些错误时应已导致未定义行为如循环引用(@9.9.1.1) ,因此程序行为不应依赖检查(@8.4.5.2) 。 |
5977 | -另见 @9.4 。 | |
6068 | +另见对象语言内存安全保证(@9.4) 。 | |
5978 | 6069 | |
5979 | 6070 | @9.9.3.6 环境中的绑定(@4.1) : |
5980 | 6071 | 和 Kernel 不同,环境中的绑定的抽象不依赖对象语言中表达的引用的概念,允许直接关联一个没有引用的值。另见 @4.2.3 和 @6.11.1 。 |
@@ -5985,13 +6076,15 @@ | ||
5985 | 6076 | |
5986 | 6077 | @9.9.3.7 重绑定(rebinding) : |
5987 | 6078 | 和 Scheme 类似,环境中允许变量以相同的名称被重新绑定,简称重绑定。 |
5988 | -对象的引用(@9.9.1) 不因其引用的对象被重绑定操作替换值而被无效化(@9.8.6) 。 | |
6079 | +被绑定对象(@5.7.1) 的引用(@9.9.1) 不因其引用的对象被重绑定操作替换值而被无效化(@9.8.6) 。 | |
5989 | 6080 | 重绑定替换被绑定对象的值,不改变对象的同一性(@9.8.1) 。若其中存在子对象(@9.8.2) ,则子对象被销毁,任何子对象的引用值被无效化。 |
5990 | 6081 | 特别地,若继续访问已被求值(@9.7.1) 指称的引用值引用的对象,则超出生存期访问而引起 NPLA 未定义行为(@5.4) 。 |
5991 | 6082 | 因为设计原则禁止无法避免的实现开销,这里没有检查而直接指定为无法满足保证所有权条件的未定义行为(@6.4.6.2) 。 |
5992 | 6083 | 任意隐藏环境(@9.9.3.1) e 应满足以下绑定有效稳定性: |
5993 | 6084 | 通过引用值间接访问 e 中绑定的对象时绑定保持有效(蕴含不被移除或重绑定),保持被绑定对象的生存期和 e 对其的所有权。 |
5994 | 6085 | 这避免因为上述访问违反内存安全(@5.6.3) 而引起 NPLA 未定义行为。 |
6086 | +**注释** | |
6087 | +类似赋值(@9.8.3.1) ,重绑定不保证子对象(@9.8.2) 的同一性不被改变;子对象的引用仍可能被重绑定无效化(@9.8.6) 。 | |
5995 | 6088 | |
5996 | 6089 | @9.9.3.8 被绑定对象的值和可观察行为(@4.1.3) : |
5997 | 6090 | 任意隐藏环境(@9.9.3.1) 的 e 的任意同一(@9.8.1) 被绑定对象 o 应满足以下的值稳定性: |
@@ -6106,19 +6199,28 @@ | ||
6106 | 6199 | 除非另行指定,根环境和作为库特性的一等环境对象初始化后在用户程序访问前被冻结(@9.9.3.9) 。 |
6107 | 6200 | 用户程序初始的当前环境(@6.11.3) 是一个包含基础环境作为直接或间接父环境的空环境(@4.6.1) 。 |
6108 | 6201 | 实现可选地提供基础环境以外的根环境,允许派生实现定义在用户程序中修改其中的绑定的机制(而不一定是隐藏环境),直至被实现初始化参考环境的特定用户程序封装或冻结而避免进一步修改。 |
6109 | -如有必要,用户程序可通过派生实现定义的方式引入其它根环境。这些方式一般依赖本机实现。 | |
6202 | +如有必要,用户程序可通过派生实现定义的方式引入其它根环境。 | |
6203 | +**注释** | |
6204 | +特性设计注记: | |
6205 | + 为避免依赖逻辑上复杂的形式,一些特性当前在当前设计中排除。 | |
6206 | + 例如,依赖一阶算术的操作、其硬件加速形式的 ISA 表示的整数操作及依赖这些操作实现的数值塔(numerical tower) 被整体忽略。 | |
6207 | + 上述忽略的特性可由派生实现补充(如以类似 @11.4 的形式),在派生根环境后按需进行 AOT(ahead-of-time) 优化(如 Kernel 的 $let-safe! 中包含的内容,其中引用基础环境的符号不再可变),然后组成类似基础环境。 | |
6208 | +通过派生实现定义的方式一般依赖本机实现。 | |
6110 | 6209 | |
6111 | 6210 | @10.1.1 初始化: |
6112 | 6211 | 通过 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 | +在环境中定义另一个环境中的同名变量,使被定义的变量是后者的引用值或值的副本,则称指定此变量名的符号在后者被导入前者。 | |
6118 | 6220 | |
6119 | 6221 | @10.2 模块: |
6120 | 6222 | 同 [RnRK] ,以绑定提供的语言特性被分组归类为模块(@1.5.6.4) 。 |
6121 | -模块的源(source) 提供特性的实现,可以是本机实现或者 NPLA1 程序。 | |
6223 | +模块的源(source) 提供特性的实现,可以是本机实现或者 NPLA1 程序。对应的模块分别是本机模块和源程序(@1.2.4) 模块。 | |
6122 | 6224 | 模块的源可以是实现内建的,或位于实现环境提供的外部资源(如文件系统)。 |
6123 | 6225 | 因为模块以绑定的集合的形式提供,需被包含在可访问的环境,或包含环境作为子对象的其它对象中。 |
6124 | 6226 | 以环境对象作为模块的源的模块化方式称为环境作为模块(environment as module) 。[RnRK] 的 get-module 的结果和参考实现扩展环境(@12) 的模块是这种方式的例子。 |
@@ -6128,13 +6230,31 @@ | ||
6128 | 6230 | 一般地,模块和加载模块得到的环境对象没有直接对应关系:一个模块的绑定可以由一个或多个环境提供,一个已被加载的环境可能提供零个或多个程序可见的模块。但除非另行指定,一个模块的绑定不在超过一个的不相交的环境(之间没有直接或间接父环境关系)中提供。 |
6129 | 6231 | 程序可通过加载外部模块来源取得模块。除非另行指定,这种模块以一个一等环境对象(可包含作为环境的直接或间接子对象)中的绑定提供。 |
6130 | 6232 | |
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 模块稳定性: | |
6132 | 6252 | 同 [RnRK] 的 get-module 的约定,提供模块绑定的环境依赖已知来源的绑定而确保稳定(@9.9.3) 。 |
6133 | 6253 | 对标准库模块,这一般表示其中的特性不能依赖用户程序运行时的非特定的当前环境,而可依赖从基础环境(@10.1) 及从基础环境派生的新环境(@9.9.3) 。 |
6134 | 6254 | 除非另行指定,模块中的特性依赖提供模块绑定的环境的生存期。例如,其中的合并子(@9.9.5) 可具有静态环境是上述环境的子对象的合并子的实现。 |
6135 | 6255 | 除非另行指定,标准库实现应确保其中的模块在程序的生存期中可用。这一般要求标准库实现在初始化后保存环境强引用(@6.11.1) 。 |
6136 | 6256 | 用户程序应自行保证加载的其它模块具有足够的生存期,以避免访问悬空引用(@9.4.3.2) 导致的未定义行为,特别是使用在环境中保留引用值的操作(@10.8.4) 的情形。 |
6137 | -另见可能破坏环境稳定性的操作(@10.8.6) 。 | |
6257 | +另见可能破坏环境稳定性的操作(@10.8.7) 。 | |
6138 | 6258 | |
6139 | 6259 | @10.3 库接口约定: |
6140 | 6260 | 基础环境的特性在根环境(@10.1) 中提供,统称根环境特性,详见 NPLA1 根环境特性(@11) 。 |
@@ -6152,9 +6272,17 @@ | ||
6152 | 6272 | 操作的结果是对应的函数调用的求值结果,即函数值(@4.5.3.1) 。 |
6153 | 6273 | 根据操作的功能描述,对应的函数可能具有非正常的控制条件(@4.8) 。此时,函数调用不取得函数值,操作不具有结果。 |
6154 | 6274 | 除非另行指定,函数调用时具有的错误条件(@9.5.3) 是非正常的控制条件;其中,以异常(@9.5.2) 实现错误条件的情形具有异常条件(@4.8.1) 。 |
6275 | +特定的操作约定对应的函数是终止函数(@4.8.2) 或全函数(@4.8.2) ;这不适用于错误条件。 | |
6276 | +特定的操作约定对应的函数具有复杂度要求。除非另行指定: | |
6277 | +这些复杂度是任意避免符合错误条件的方式调用时求值蕴含的渐进(asymptotic) 时间复杂度; | |
6278 | +若指定边界,明确的输入规模以哑变量(dummy) n 表示; | |
6279 | +指定复杂度的函数是终止函数。 | |
6155 | 6280 | 本章其余各节适用 NPLA1 对象语言中的这些操作。 |
6156 | 6281 | 这些操作中的大部分具有特定的名称。这些名称符合函数名称约定(@10.7) ;其分类详见函数分类(@11.2) 。 |
6157 | 6282 | 其它操作不具有特定名称,可由上述操作间接地提供,如蕴含在某些操作涉及的函数值(@4.5.3) 中。 |
6283 | +**注释** | |
6284 | +渐进复杂度常以 O 记号指定上界。 | |
6285 | +若函数调用总是取得值,指定复杂度的函数同时是全函数。 | |
6158 | 6286 | |
6159 | 6287 | @10.3.2 库特性实现分类: |
6160 | 6288 | 库特性分为基本的和派生的(同 NPLA1 实现的支持(@8.1) )。 |
@@ -6313,12 +6441,12 @@ | ||
6313 | 6441 | NPLA1 中谓词是返回类型为 <boolean> 的函数。 |
6314 | 6442 | 除非另行指定,以下引入的对象语言中的谓词调用时没有副作用。 |
6315 | 6443 | 为提供库的描述,约定以下谓词的典型实例: |
6316 | - 类型谓词(@4.7.5) :接受一个 <object> 参数,判断参数是特定的类型的对象。 | |
6444 | + 类型谓词(@4.7.6) :接受一个 <object> 参数,判断参数是特定的类型的对象。 | |
6317 | 6445 | 调用这些类型谓词不引起错误。 |
6318 | 6446 | 仅当参数指定的对象具有对应类型时结果是 #t 。 |
6319 | 6447 | 除非另行指定,这些类型谓词忽略值类别的差异。 |
6320 | 6448 | 等价谓词(@4.1.4) :接受两个参数,判断参数是否属于同一个等价类。 |
6321 | -因为 <boolean> 是 <test> 的子类型,按照返回值为 <test> 的函数可在不严格要求 <boolean> 的上下文中起类似的作用,视为广义谓词(general predicate) 。 | |
6449 | +因为 <boolean> 是 <test> 的子类型(@9.5.4.1) ,按照返回值为 <test> 的函数可在不严格要求 <boolean> 的上下文中起类似的作用,视为广义谓词(general predicate) 。 | |
6322 | 6450 | 谓词是广义谓词的子类型。 |
6323 | 6451 | **注释** |
6324 | 6452 | 大多数上下文接受 <test> 而不严格要求 <boolean> 。这和 [RnRK] 不同(@9.2.2.2) 。 |
@@ -6378,15 +6506,15 @@ | ||
6378 | 6506 | 结尾的引用标记字符用于强调无法总是保证内存安全的危险操作(@1.5.5.2) 。 |
6379 | 6507 | 不带有引用标记字符结尾的操作通过避免保留引用值(@10.4.3) 提供一定的内存安全保证,而带有引用标记字符结尾的操作较容易引起注意。 |
6380 | 6508 | 这符合易预测性(@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) 的可被访问; | |
6383 | 6511 | 这个环境在调用后通常被销毁,若使用带有引用标记字符结尾的操作关于对应的函数返回引用值的语义,在返回值中保留引用值,返回为悬空引用(@9.4.3.2) ,容易误用。 |
6384 | 6512 | 一般地,仅在明确需要引用值时使用引用标记字符结尾的操作,而避免返回悬空引用(这类似宿主语言函数的 auto 而非 auto&& 的返回类型,但宿主语言中返回非引用类型的表达式两者含义不同)。 |
6385 | 6513 | |
6386 | 6514 | @10.7.3.3 保留引用值(@10.4.3) 的约定: |
6387 | 6515 | 可能直接保留引用值(@10.4.3) 的操作中,不带有引用标记字符的操作转发参数(@10.5.2) 。 |
6388 | 6516 | 可能直接保留引用值的操作包括容器构造器或访问器(@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) 的条件确保安全性;在此不作要求。 | |
6390 | 6518 | 对构造器及部分修改操作区分引用标记字符结尾可强调一些非预期保留引用值的容易误用情形: |
6391 | 6519 | 尽管总是返回非引用值,因转发参数而被保留的引用值(@10.4.3) 不会被返回值转换(@10.4.2) 或类似的操作影响,在构造的容器对象作为非引用值返回仍会保留引用值。 |
6392 | 6520 | (对应宿主语言中,可有更显著的差异,如构造器对应的 std::tuple 的 std::make_tuple 和 std::forward_as_tuple 。) |
@@ -6411,7 +6539,7 @@ | ||
6411 | 6539 | 函数名不带有标记字符结尾的访问器属于参数转发操作(@10.5.2) 和函数值转发操作(@10.5.4) 。 |
6412 | 6540 | |
6413 | 6541 | @10.7.4.3 可能使对象中包含引用值的修改操作(@9.8.3) : |
6414 | -修改对象或对象的子对象(@9.8.2) 可无效化引用值(@6.8.5) 而影响内存安全。 | |
6542 | +修改对象或对象的子对象(@9.8.2) 可无效化引用值(@6.4.4) 而影响内存安全。 | |
6415 | 6543 | 对可能保留参数中的引用值(@9.4.4.2) 的操作,内存安全也依赖这些操作的指定修改后的值的内存安全性。 |
6416 | 6544 | 在判定内存安全(@9.4) 的意义上,以下操作的所有参数都可能是被保留的间接值(@9.4.4) : |
6417 | 6545 | 简单赋值(simple assignment)(包含于赋值操作(@9.8.3.1) ); |
@@ -6489,8 +6617,8 @@ | ||
6489 | 6617 | 类似可提供以引用标记字符结尾变体的对应操作(@10.7.4.4) ,部分不带有引用标记字符的操作可能间接保留引用值(@10.4.3) 。 |
6490 | 6618 | 这包括由类型为合并子的参数(而非 <body> 或 <expressions> )决定是否保留引用值同时对其它参数进行转发的操作。 |
6491 | 6619 | |
6492 | -@10.8 不安全操作(@9.4.5) 约定: | |
6493 | -除非另行指定,执行时包含以下操作的操作是不安全操作: | |
6620 | +@10.8 不安全操作约定: | |
6621 | +除非另行指定,执行时蕴含以下操作的操作是不安全操作(@9.4.5) : | |
6494 | 6622 | 以下不具有内存安全保证(@9.4) 的操作: |
6495 | 6623 | 使用函数名称约定(@10.7) 的命名的带有后缀的操作(@10.7.4) ; |
6496 | 6624 | 其它保留间接值的操作(@9.4.4) ; |
@@ -6505,15 +6633,17 @@ | ||
6505 | 6633 | |
6506 | 6634 | @10.8.1 在返回值中保留引用值(@10.4.3) 的操作: |
6507 | 6635 | 在返回值中保留引用值的操作可引起之后的不安全引用值访问(@9.4.2) 。 |
6508 | -例如,赋值操作(@9.8.3.1) 可无效化引用值(@9.8.6) 。 | |
6509 | 6636 | 这包括按函数名称约定(@10.7) 具有引用标记字符结尾的操作。 |
6510 | 6637 | 直接保留引用值(@10.4.3) 操作可配合带有返回值转换(@6.4.6.4) 的操作,指定个别函数参数不再保留引用值。 |
6638 | +**注释** | |
6639 | +一些修改操作无效化(@9.8.6) 引用值。这些引用值若被保留且被访问,可引起未定义行为。 | |
6640 | +不引起被绑定对象无效的修改操作不被视为不安全操作,即便它们无效化子对象的引用值。 | |
6511 | 6641 | |
6512 | 6642 | @10.8.2 在返回值中保留环境引用的操作: |
6513 | 6643 | 环境引用(@6.4.3.1) 被返回时,总是被保留(@10.4.4) 。 |
6514 | 6644 | 创建环境强引用的操作(@6.11.1) 是在返回值中保留环境引用的操作。 |
6515 | 6645 | 这些对象可能因为没有及时保存环境引用使环境对象和其中的绑定一并被销毁,而使引用值访问其中的对象的程序具有未定义行为: |
6516 | -注意直接返回有效的环境弱引用的操作不引起环境失效,不在此列。 | |
6646 | +注意直接返回有效的环境弱引用(@6.11.1) 的操作不引起环境失效,不在此列。 | |
6517 | 6647 | |
6518 | 6648 | @10.8.3 在返回值中保留其它间接值的操作: |
6519 | 6649 | 特定的支持强递归绑定(@9.7.3.1) 而在返回值中保留其它间接值,可能是无效的间接值(@9.4.3.3) 。 |
@@ -6523,21 +6653,37 @@ | ||
6523 | 6653 | @10.8.4 在环境中保留环境引用的操作: |
6524 | 6654 | 环境中的被绑定对象可具有环境引用子对象(@9.8.2) ,间接地在环境中保留环境引用。 |
6525 | 6655 | 这些操作使当前环境或参数指定的环境(而不是合并子调用时创建的新环境(@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 可能引入循环引用的操作: | |
6531 | 6677 | 可能通过非引用的形式引入环境循环引用的操作可破坏环境的资源所有权。 |
6532 | 6678 | 自赋值(@9.8.3.1) 可能引入循环引用值。 |
6533 | 6679 | |
6534 | -@10.8.6 可能破坏环境稳定性的操作: | |
6680 | +@10.8.7 可能破坏环境稳定性的操作: | |
6535 | 6681 | 通过引用值进行的修改操作(@9.8.3) 可因破坏环境稳定性(@9.9.3) 而引起扩展 NPLA 未定义行为(@9.1.4)(不一定违反内存安全)。 |
6536 | 6682 | 这包括以下可无效化对象包含的引用值而使可通过环境访问的某个子对象的同一性被改变,从而破坏环境稳定性(@9.9.3) 的操作: |
6537 | 6683 | 对可能具有对象语言中可访问的子对象的对象的赋值操作(@9.8.3.1) ; |
6538 | 6684 | 可修改被绑定对象(@6.11.1) 的操作(包括重绑定(@9.9.3.7) )。 |
6539 | 6685 | |
6540 | -@10.8.7 安全操作子集: | |
6686 | +@10.8.8 安全操作子集: | |
6541 | 6687 | 作为对象语言安全性保证(@9.4.6) 的一部分,用户程序通过限制或避免依赖特定的不安全操作,在特定情形下可实现对象语言内存安全保证(@9.4) ,而不需要分析具体操作的语义: |
6542 | 6688 | 不依赖操作命名(@10.7) 约定的带有后缀的操作; |
6543 | 6689 | 若使用保留环境引用的操作,如在返回值中保留环境引用的操作(@10.8.2) 和在环境中保留环境引用的操作(@10.8.4) ,总是保存被依赖的环境以确保相关的环境对象及其中的被绑定对象(@6.11.1) 在间接访问对象时不被销毁; |
@@ -6604,8 +6750,9 @@ | ||
6604 | 6750 | 在返回值中保留引用值的不安全操作(@10.8.1) 已被操作命名(@10.7) 归纳和函数分类(@11.2) 枚举,此处从略。 |
6605 | 6751 | 不安全操作中,在参数以外直接引入间接值的操作仅有以下的在返回值中保留引用值的不安全操作: |
6606 | 6752 | 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) 环境保证而提供静态的证明。 | |
6609 | 6756 | |
6610 | 6757 | @11.1.1 在返回值中保留环境引用的操作(@10.8.2) : |
6611 | 6758 | 包括基本操作(@11.3.7) : |
@@ -6627,17 +6774,21 @@ | ||
6627 | 6774 | $provide/let! |
6628 | 6775 | $provide! |
6629 | 6776 | |
6630 | -@11.1.4 可能引入循环引用的操作(@10.8.5) : | |
6777 | +@11.1.4 无效化被绑定对象的操作(@10.8.5) : | |
6778 | +当前不提供此类操作。 | |
6779 | +这可包含直接移除变量绑定的操作。 | |
6780 | + | |
6781 | +@11.1.5 可能引入循环引用的操作(@10.8.6) : | |
6631 | 6782 | 包括可能引入环境循环引用的基本操作(@11.3.7) : |
6632 | 6783 | copy-environment |
6633 | 6784 | lock-environment |
6634 | 6785 | 包括可能引入环境循环引用的派生操作(@11.4.1) : |
6635 | 6786 | lock-current-environment |
6636 | -包括可能自赋值(@10.8.5) 引入循环引用值的基本操作(@11.2.1.3) : | |
6787 | +包括可能自赋值(@10.8.6) 引入循环引用值的基本操作(@11.2.1.3) : | |
6637 | 6788 | assign@! |
6638 | 6789 | assign%! |
6639 | 6790 | |
6640 | -@11.1.5 可能破坏环境稳定性的操作(@10.8.6) : | |
6791 | +@11.1.6 可能破坏环境稳定性的操作(@10.8.7) : | |
6641 | 6792 | 对可能具有对象语言中可访问的子对象的对象的赋值操作(@9.8.3.1) 包括: |
6642 | 6793 | 简单赋值(@10.7.4.3) 。 |
6643 | 6794 | 可直接修改被绑定对象(@6.11.1) 的操作包括: |
@@ -6817,39 +6968,48 @@ | ||
6817 | 6968 | 操作: |
6818 | 6969 | eq? <object1> <object2> :判断参数同一。 |
6819 | 6970 | 当且仅当两个参数是指定同一对象时,比较结果是 #t 。 |
6971 | +eq? 的复杂度是 O(1) 。 | |
6820 | 6972 | eql? <object1> <object2> :判断表示参数的项的值数据成员(@6.2) 相等。 |
6821 | 6973 | 当且仅当表示两个参数是的项的值数据成员相等时,比较结果是 #t 。 |
6822 | -值数据成员相等蕴含参数的动态类型(@4.7) 相等。 | |
6974 | +值数据成员相等蕴含参数的动态类型(@4.7) 相同(@4.7.2) 。 | |
6823 | 6975 | eqr? <object1> <object2> :判断表示参数的项的数据成员同一。 |
6824 | 6976 | 当且仅当表示两个参数是的项的值数据成员指定宿主语言中的同一对象(即引用相等)时,比较结果是 #t 。 |
6825 | 6977 | eqv? <object1> <object2> :判断非枝节点(@6.2.1) 表示的值相等。 |
6826 | -当表示参数的项都是枝节点时,同 eq? ; | |
6978 | +若参数是引用值,则被比较的值是它的被引用对象(@4.2.3) 。 | |
6979 | +当表示值的项都是枝节点时,同 eq? ; | |
6980 | +否则,若这两个参数的类型不同(@4.7.2) ,则结果是 #f ; | |
6827 | 6981 | 否则,若这两个参数的 eql? 比较结果是 #t ,则结果是 #t 。 |
6828 | 6982 | 若两个参数的 eqv? 比较结果是 #f ,则这两个参数以 eq? 比较结果总是 #f 。 |
6829 | -不等价(@4.5.3.2) 的函数的 eqv? 比较结果是 #f 。 | |
6983 | +除非互操作(@1.2.3) (参见以下描述)或派生实现另行指定,不等价(@4.5.3.2) 的函数的 eqv? 比较结果是 #f 。 | |
6830 | 6984 | 除以上规则确定的结果外,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? 以外的谓词比较的求值应保证能终止。 | |
6833 | 6987 | **原理** |
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) 提供直接的支持。 | |
6836 | 6990 | NPLA1 提供默认相等为抽象相等(@4.1.4) ,对任意的值适用。 |
6837 | 6991 | NPLA1 还提供对一等对象保证结果有意义的引用相等(@4.1.4) 操作。非一等实体的引用相等关系未指定。 |
6838 | 6992 | 当前 NPLA1 不支持 EGAL(@4.1.4) ,因为 EGAL 要求存在分辨任意对象的值是否可被修改的元数据。 |
6839 | 6993 | 因为对应等价的不变性(@1.2.1.1) 关系不具有唯一性(@4.1.4.3) ,且可能允许不唯一的方式产生副作用(如缓存),和 Kernel 不同,不以基本操作提供 equal? 对任意对象提供一般的相等操作。 |
6840 | 6994 | 未指定 eq? 的比较结果可允许实现复用存储右值的驻留(@9.8.4) 对象。 |
6841 | 6995 | eql? 实际比较宿主值的相等。允许 eqv? 和 eql? 的不同可允许一对多的类型映射(@5.5.1) 下比较对象语言的值的相等。(而多对一的类型映射 eql? 和 eqv? 可一致地比较。) |
6842 | -但是,当前实现中,大多数一对多映射的类型(如环境)都没有引起使 eql? 和 eqv? 不同的比较实现,因为不同宿主值类型的对象具有足够显著的差异,在大多数上下文不通过一些具有不可忽略开销的转换机制(如锁定环境弱引用转换为环境强引用),无法直接相互替换而保证行为差异可被忽略,因此逻辑上不适合定义为相等的。 | |
6996 | +但是,当前实现中,大多数一对多映射的类型(如环境)都没有引起使 eql? 和 eqv? 不同的比较实现,因为不同宿主值类型的对象具有足够显著的差异,在大多数上下文不通过一些具有不可忽略开销的转换机制(如锁定环境弱引用(@6.11.1) 转换为环境强引用(@11.3.7) ),无法直接相互替换而保证行为差异可被忽略,因此逻辑上不适合定义为相等的。 | |
6843 | 6997 | 而基于性能等理由,等其它一对多映射的类型(特别是可能基于宿主类型的值的子集的,如 NPLA 数值类型(@6.14.1) 中的 <integer> )的值的比较也没有特别的处理,而引起 eqv? 和 eql? 的不同。 |
6844 | 6998 | 这些类型可能需要其它针对特定类型的等价谓词(如 =?(@12.2) )进行相等性的比较。 |
6999 | +类似 [RnRS] ,不同类型决定 eqv? 的结果是 #f ,但此处类型相同的含义不通过类型分区(@9.8.7) 定义。 | |
7000 | +类似 [RnRS] ,行为不等价的函数的 eqv? 结果原则上应为 #f ,但这种等价性一般不可证明而无法保证,特别在关于语言实现以外的调用上。 | |
7001 | +为支持互操作使用本机实现(@5.3) 及避免限制合并子的子类型的开放性(@1.5.3.6) ,允许这些实现另行指定规则,假定引起程序可观察行为差异的函数调用调用名义等价。 | |
6845 | 7002 | **注释** |
7003 | +通常,等价谓词比较的求值应保证能终止且对非列表项和 n 个子项的列表分别具有 O(1) 和 O(n) 平摊复杂度。这是依赖数据结构实现的细节;语言不需要约束这个性质。 | |
6846 | 7004 | 实现中,值数据成员的相等由 ValueObject 的 == 操作定义。这对应 C++ 意义上的对象相等。 |
6847 | -对仅由值数据成员决定值的表示的情形(@6.10.7) ,动态类型相等即值数据成员的目标类型相等。 | |
7005 | +对仅由值数据成员决定值的表示的情形(@6.10.7) ,动态类型相等(@4.7.2) 即值数据成员的目标类型相等。 | |
6848 | 7006 | 这种情形使用 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? 等价关系下视为相同的对象。 | |
6850 | 7010 | 使用 eqr? 判断宿主值 TermNode 的值数据成员表示的左值(@5.8.1) 的标识,当且仅当为操作数比较同一个对象时比较结果是 #t 。 |
6851 | -由 @6.10.10 ,所有列表在 TermNode 的值数据成员中的表示都一致,在 eqr? 等价关系下视为相同的对象。 | |
6852 | 7011 | 关于合并子相等,另见 TCO 实现(@7.11.5) 中关于函数右值去重(@7.10.7.1) 的说明。 |
7012 | +因为枝节点和其它节点不共享除公共超类型(@4.7.7) <object> 外具有相同类型的表示,任意一个被比较的值是枝节点时,eqv? 的结果也同 eq? 。 | |
6853 | 7013 | |
6854 | 7014 | @11.3.3 控制: |
6855 | 7015 | $if <test> <consequent> <alternate> :条件分支,按条件成立与否返回 <consequent> 或 <alternative> 之一,可能是引用值。 |
@@ -6922,7 +7082,7 @@ | ||
6922 | 7082 | assign@! <reference> <object> :赋值(@9.8.3.1) 被引用的对象为指定对象的值,且 <object> 不隐含左值到右值转换且不被折叠。 |
6923 | 7083 | 检查 <reference> 是可修改的左值。 |
6924 | 7084 | 赋值对象直接修改(@9.8.3) 被引用的对象,但不无效化(@9.8.6) 参数指定的引用。 |
6925 | -Scheme 的 set! 在 SRFI-17 提供具有类似作用的支持,但第一操作数限于 set! 且为特定的过程调用;Kernel 没有类似的操作。 | |
7085 | +**注释** 被赋值替换的子对象的引用可被无效化。Scheme 的 set! 在 SRFI-17 提供具有类似作用的支持,但第一操作数限于 set! 且为特定的过程调用;Kernel 没有类似的操作。 | |
6926 | 7086 | |
6927 | 7087 | @11.3.5 列表: |
6928 | 7088 | cons <object> <list> :构造两个元素的列表。 |
@@ -6973,7 +7133,7 @@ | ||
6973 | 7133 | make-environment <environment>... :创建以参数为父环境的环境。 |
6974 | 7134 | 和 Kernel 不同,除对象类型外,没有对列表和绑定的附加检查。 |
6975 | 7135 | 结果是新创建的环境,是环境强引用,具有宿主值类型 shared_ptr<Environment> 。 |
6976 | -weaken-environment <environment> :使用环境强引用创建环境弱引用。 | |
7136 | +weaken-environment <environment> :使用环境强引用创建环境弱引用(@6.11.1) 。 | |
6977 | 7137 | 检查参数类型是环境强引用,若失败则引起类型错误(@9.5.4.1) 。 |
6978 | 7138 | **原理** 因为 NPLA1 需要精确控制所有权而不依赖 GC(@5.2) ,这可用于派生实现某些操作(如 $sequence(@11.4.1) 必要的)。 |
6979 | 7139 | **注释** 实现检查操作数的宿主值类型(@6.11.1.1) 是 shared_ptr<Environment> ,结果的宿主类型 NPL::EnvironmentReference 。 |
@@ -7077,7 +7237,7 @@ | ||
7077 | 7237 | 本节约定以下求值得到的操作数: |
7078 | 7238 | <box> :箱(@4.2.3.5.3) 。 |
7079 | 7239 | 操作: |
7080 | -() get-current-environment :取当前环境:取当前环境的环境弱引用。 | |
7240 | +() get-current-environment :取当前环境:取当前环境的弱引用(@6.11.1) 。 | |
7081 | 7241 | 结果具有宿主值类型 NPL::EnvironmentReference 。派生需要非派生实现的 vau/e 。 |
7082 | 7242 | () lock-current-environment :锁定当前环境:取当前环境的环境强引用。 |
7083 | 7243 | 结果具有宿主值类型 shared_ptr<Environment> 。 |
@@ -7098,9 +7258,10 @@ | ||
7098 | 7258 | forward! <object> :转发(@10.5.4) 可能是引用的值(@11.2.2.4) 。 |
7099 | 7259 | 转移可修改的右值操作数(包括消亡值和临时对象)。 |
7100 | 7260 | 其中,需转移时,使用使用项的转移(@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) 。 | |
7104 | 7265 | list% <object>... :同 list ,但每个参数都不隐含左值到右值转换,在结果中保留参数的引用值(@10.7.3.3) 。 |
7105 | 7266 | rlist <list> :转换参数为引用列表元素的列表。 |
7106 | 7267 | 若参数是左值,则结果是参数的元素的左值引用值构成的列表;否则,结果同 idv 。 |
@@ -7135,10 +7296,9 @@ | ||
7135 | 7296 | collapse <object> :折叠可能是引用的值。 |
7136 | 7297 | forward <object> :转发可能是引用的非临时对象的值。 |
7137 | 7298 | 同 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) 可简化操作。 | |
7142 | 7302 | assign%! <reference> <object> :同 assign@!(@11.3.4) ,但 <object> 是引用值时赋值的源操作数是 <object> 折叠后的值。 |
7143 | 7303 | assign! <reference> <object> :同 assign%! ,但 <object> 隐含左值到右值转换。 |
7144 | 7304 | **注释** 因为左值到右值转换,即便 <object> 指定的值来自 <reference> ,也可赋值而不因此引起未定义行为。 |
@@ -7198,6 +7358,7 @@ | ||
7198 | 7358 | check-environment <object> :检查环境(@9.9.3) 。 |
7199 | 7359 | 若参数是 <environment> 则检查通过,结果是转发的参数; |
7200 | 7360 | 否则,引发错误对象(@9.5.2) 。 |
7361 | +**注释** 当前实现中其它要求 <enviornment> 参数的操作中类型检查(@9.5.4.1) 失败和 check-environment 失败的行为一致。 | |
7201 | 7362 | check-parent <object> :检查作为环境的父环境(@9.9.3) 的对象。 |
7202 | 7363 | 若参数是可以作为合并子环境的 <parent> 则检查通过,结果是转发的参数;否则,引发错误对象。 |
7203 | 7364 | 检查环境通过的条件同创建合并子时的检查(@11.3.8) 。 |
@@ -7216,7 +7377,7 @@ | ||
7216 | 7377 | $and <test>... :逻辑与。 |
7217 | 7378 | 顺序短路求值。操作数为空时结果是 #t ;参数求值存在 #f 时结果是 #f ;否则结果是最后一个参数的值。 |
7218 | 7379 | 结果保留引用值。 |
7219 | -**注释** 和 Kernel 的 $and? 不同,不检查类型,也不保证结果类型是 <boolean>(所以命名不以 ? 结尾(@10.7.2.1) )。这同时允许直接的 PTC 实现。 | |
7380 | +**注释** 和 Kernel 的 $and? 不同,不检查类型,也不保证结果类型是 <boolean>(所以命名不以 ? 结尾(@10.7.2.1) )。和 [RnRK] 中的原理描述的不同,这同时允许直接的满足对 PTC 要求(@9.7.4.1) 的派生实现。 | |
7220 | 7381 | $or <test>... :逻辑或。 |
7221 | 7382 | 顺序短路求值。操作数为空时结果是 #f ,参数求值存在不是 #f 的值时结果是第一个这样的值;否则结果是 #t 。 |
7222 | 7383 | 结果保留引用值。 |
@@ -7278,7 +7439,7 @@ | ||
7278 | 7439 | 注意以上 $let 等函数的 <body> 形式和 Kernel 不同。 |
7279 | 7440 | derive-current-environment <environment>... :创建当前环境的派生环境:以参数指定的环境和当前环境为父环境的空环境。 |
7280 | 7441 | 当前环境以外的父环境顺序同参数顺序。当前环境是最后一个父环境。 |
7281 | -() make-standard-environment :创建标准环境(standard environment) :以基础环境(@10.1) 为父环境的空环境。 | |
7442 | +() make-standard-environment :创建新(@9.9.3.2) 标准环境(standard environment) :以基础环境(@10.1) 为父环境的空环境。 | |
7282 | 7443 | 同 Kernel 的 make-standard-kernel-environment ,但创建的环境基于 NPLA1 基础环境。 |
7283 | 7444 | **注释** 标准环境同 [RnRK] 约定的定义。 |
7284 | 7445 | derive-environment <environment>... :创建基础环境的派生环境:以参数指定的环境和基础环境为父环境的空环境。 |
@@ -7293,7 +7454,7 @@ | ||
7293 | 7454 | 使用 make-environment(@11.3.7) 而不是 $let/e(@11.4.1) 等绑定构造实现。 |
7294 | 7455 | $bindings->environment <binding>... :转换绑定列表为没有父环境的具有这些绑定的环境。 |
7295 | 7456 | **注释** 类似 Kernel 的同名操作,但因为要求对内部父环境环境所有权,使用 $binding/p->environment 而不是 $let/e 等绑定构造派生。 |
7296 | -symbols->imports <symbol>... :转换符号列表为未求值的适合初始化符号导入列表的初值符列表。 | |
7457 | +symbols->imports <symbol>... :转换符号列表为未求值的适合初始化符号导入(@10.1.2) 列表的初值符列表。 | |
7297 | 7458 | 结果是包含同 desigil(@11.3.6) 的方式移除标记字符(@9.2.2.4) 后的参数作为间接子项的列表。 |
7298 | 7459 | 求值这个列表,结果是同 forward!(@11.4.1) 的方式转发每个符号的列表,其元素顺序和 <symbols>... 中的值的顺序对应。 |
7299 | 7460 | 结果的结构和使用满足以下约定(可允许更有效的本机实现): |
@@ -7309,13 +7470,13 @@ | ||
7309 | 7470 | 蕴含 $let <bindings> <body> ,在求值 <body> 后以结果作为操作数绑定到 <symbols> 的符号。 |
7310 | 7471 | <symbols> 应能被作为 <definiend> 使用。 |
7311 | 7472 | 结果是对这些绑定具有所有权的环境强引用。 |
7312 | -需要导入符号(即 <symbols>... 具有至少一个实际参数)时,以同 symbols->imports(@11.4.1) 的方式确定初值符。其中,等效的 symbols->imports 的求值次数未指定。 | |
7473 | +需要导入符号(@10.1.2) ,即 <symbols>... 具有至少一个实际参数时,以同 symbols->imports(@11.4.1) 的方式确定初值符。其中,等效的 symbols->imports 的求值次数未指定。 | |
7313 | 7474 | **注释** 绑定后的符号可通过作为 vau 抽象的父环境(@9.9.3.5) 等形式依赖这个环境,因此用户需适当保存返回值使其生存期(@9.9.3.5) 覆盖在被使用的绑定符号指称的对象生存期。 |
7314 | 7475 | $provide! <symbols> <body> :在当前环境中提供绑定。 |
7315 | 7476 | 同 $provide/let! 但不指定单独的 <bindings> 。 |
7316 | 7477 | 作用同 <bindings> 为空列表的 $provide/let! 。 |
7317 | 7478 | 结果是创建的环境的强引用。 |
7318 | -且需要导入符号时,以同 symbols->imports 的方式确定初值符。其中,等效的 symbols->imports 的求值次数未指定。 | |
7479 | +需要导入符号时,以同 symbols->imports 的方式确定初值符。其中,等效的 symbols->imports 的求值次数未指定。 | |
7319 | 7480 | **注释** 类似 Kernel 的同名操作,但结果是创建的环境的强引用,且确定初值符的方式被显式要求。 |
7320 | 7481 | 仅当 <symbols> 类型检查通过时求值 <body> 。 |
7321 | 7482 | 检查当前环境可修改(@9.9.3.9) 失败时的副作用和以上任一等效求值 symbols->imports 应用子的结果可能具有的副作用非决定性有序(@4.4.3) 。 |
@@ -7336,8 +7497,13 @@ | ||
7336 | 7497 | 作为传递操作,保留引用值。 |
7337 | 7498 | 以上 4 个函数除引用标记字符(@10.7) 对应处理引用值的差异外,功能和使用方式对应类似 SRFI-111 的 3 个过程 box 、box? 和 unbox 。 |
7338 | 7499 | 类型分区(@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% 更低效(因为包含了无用的检查和更多的非本机实现)。 | |
7339 | 7505 | **注释** |
7340 | -用 list 、list% 和 first 可代替 box 、box% 和 unbox 。 | |
7506 | +不考虑封装性时,用 list 、list% 和 first 可代替 box 、box% 和 unbox 。 | |
7341 | 7507 | 和 http://community.schemewiki.org/?scheme-faq-language 关于装箱的描述不同,这样的代替不一定保证有更好的性能。 |
7342 | 7508 | 以上这些函数可使用 make-encapsulation-type(@11.3.10) 实现。 |
7343 | 7509 | 和 Scheme 等不同,箱具有被装箱对象的所有权,因此使用 box% 和 unbox 时,需注意保存被构造的箱或被箱中引用值引用的对象。 |
@@ -7494,12 +7660,12 @@ | ||
7494 | 7660 | 和数值操作约定不同,幂等操作要求超过一次应用时,结果和参数的宿主类型也相同。 |
7495 | 7661 | |
7496 | 7662 | @12.2.1 除法约定: |
7497 | -二元除法或者取余数的操作中,<number1> 是被除数,<number2> 是除数。 | |
7663 | +二元除法或者取余数的操作中,第一个参数是被除数,第二个参数是除数。 | |
7498 | 7664 | 当除数是不精确数 0 时: |
7499 | -若被除数是非零有限数值或无限大值,则商的符号同 <number1> ; | |
7665 | +若被除数是非零有限数值或无限大值,则商的符号同被除数的符号; | |
7500 | 7666 | 否则,商的符号未指定。 |
7501 | 7667 | 当被除数是不精确数时,若除数是精确数 0 ,则结果除符号外同除数是不精确数 0 的情形。 |
7502 | -同时计算商和余数的函数的结果的结果是商和余数构成的列表。 | |
7668 | +同时计算商和余数的操作的结果是商和余数构成的列表。 | |
7503 | 7669 | **原理** |
7504 | 7670 | Scheme 方言及实现中普遍存在不同的除零错误的条件。 |
7505 | 7671 | [R5RS] 的过程 / 除以零的条件没有被明确约定。 |
@@ -7583,17 +7749,26 @@ | ||
7583 | 7749 | 类似 klisp 的同名操作。类似地,不使用 klisp 的 find-required-filename 机制,直接以宿主的运行环境为基准使用路径。 |
7584 | 7750 | 和 klisp 不同,在尾上下文中求值被加载后读取的对象,并以其求值结果作为表达式的求值结果。 |
7585 | 7751 | [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) 到内部表示等替代,这些实现通常不应被要求为总是具有本机派生实现而降低实现质量。 | |
7587 | 7763 | **注释** |
7588 | 7764 | 关于 newline 、put 和 puts : |
7589 | 7765 | 实现使用 REPLContext::GetOutputStreamRef(@7.8.1) 。 |
7590 | 7766 | 在使用前,一般应初始化 REPLContext::OutputStreamPtr(@7.8.1) 指向特定的 std::ostream 对象;否则,总是失败,引起错误(@9.5.1) 。 |
7591 | -关于 load : | |
7767 | +关于 load 和 get-module : | |
7768 | +参数一般指定视为外部翻译单元的文件名。 | |
7592 | 7769 | 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 的预期语义。 | |
7597 | 7772 | |
7598 | 7773 | @12.5 系统: |
7599 | 7774 | 通过初始化基础上下文后调用 Forms::LoadModule_std_system(@8.5.2) 初始化,默认加载为根环境下的 std.system 环境。 |
@@ -7637,30 +7812,36 @@ | ||
7637 | 7812 | 操作: |
7638 | 7813 | registered-requirement? <string> :判断参数是否是已在本模块注册的需求字符串。 |
7639 | 7814 | register-requirement! <string> :在本模块注册参数为需求字符串。 |
7815 | +若已被注册,则引起错误;否则,在内部创建新标准环境。 | |
7816 | +结果是创建的环境的弱引用(@6.11.1) 。 | |
7640 | 7817 | unregister-requirement! <string> :在本模块注册解除参数为需求字符串。 |
7641 | 7818 | find-requirement-filename <string> :查找需求字符串对应的文件名。 |
7642 | -在需求字符串模板中顺序地搜索字符串,返回匹配字符串的结果。若不存在这样的结果,则引起错误。 | |
7819 | +在需求字符串模板中顺序地搜索字符串。若不存在这样的结果,则引起错误;否则,结果是匹配字符串的搜索结果。 | |
7643 | 7820 | 判断需求字符串模板中的每一个字符串是否能被需求字符串匹配时,首先替换字符串中的单字符子串 "?" 为需求字符串,取得替换结果,再判断它是否为可读的文件的文件名。 |
7644 | 7821 | 替换字符串时,每一个子串被同时一次替换;不对替换结果进一步递归地替换。 |
7645 | -require <string> :按需加载需求字符串对应的模块。 | |
7646 | -若参数指定的需求字符串没有注册,则注册需求字符串并加载同调用 find-requirement-filename 等价的方式搜索得到的结果;否则没有作用。 | |
7822 | +require <string> :按需在新(@9.9.3.2) 标准环境加载需求字符串对应的模块。 | |
7823 | +若参数指定的需求字符串没有注册,则注册需求字符串并加载同调用 find-requirement-filename 等价的方式搜索得到的结果,并保存加载的结果;否则不进行加载。 | |
7824 | +结果是保存的加载的结果。 | |
7647 | 7825 | **原理** |
7648 | 7826 | 为避免名称污染等问题,不提供显式指定命名环境的模块创建操作,如 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 。 | |
7650 | 7832 | **注释** |
7651 | 7833 | 当前实现依赖可用的 std.strings(@12.3) 、std.io(@12.4) 和 std.system(@12.5) 环境。 |
7652 | -操作的设计同 klisp 的 ports 模块中的同名操作。 | |
7653 | -当前实现中,加载操作同 std.io 模块中的 load(@12.4) 。 | |
7654 | 7834 | 通过不经过本模块的操作、重复字符串模板的重复项、符号链接和字符串大小写不敏感的文件名等可能绕过本模块的注册机制而重复加载同一个外部文件。本模块的操作不对这些情形进行任何检查。 |
7655 | 7835 | |
7656 | 7836 | @13 SHBuild 实现环境: |
7657 | 7837 | SHBuild 实现环境是派生 NPLA1 参考实现扩展环境(@12) 的用于 SHBuild 和外部脚本的构建的初始环境。 |
7838 | +SHBuild 实现环境的初始化(@10.1.1) 可加载模块(@10.2) ,这些模块的加载适用和标准库实现相同的要求和假定(@10.2.1) 。 | |
7658 | 7839 | 关于 SHBuild 的调用方式说明,详见 https://frankhb.github.io/YSLib-book/SHBuild.zh-CN.html 。 |
7659 | 7840 | 操作约定同 @10 ,包括 @10.6 和 @10.7 ;但除此之外,遵循 NPLA1 用户程序(@9.1) 的约定。 |
7660 | 7841 | |
7661 | 7842 | @13.1 NPL::Dependency 派生特性: |
7662 | 7843 | 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) 初始化。 | |
7664 | 7845 | SHBuild 的基础环境(@10.1) 的子环境中提供对象引用 env_SHBuild_ ,其中包含这些特性。 |
7665 | 7846 | 部分 SHBuild 互操作特性(@13.1.1) 在 YFramework 的 NPL::Dependency 实现中提供。 |
7666 | 7847 | 此外,NPLA1 在默认实现的 SHBuild_BaseTerminalHook_(@13.1.1) 的实现被覆盖,以使 SHBuild_EchoVar(@13.1.1) 等和 SHBuild 的其它输出兼容。 |