我正在为乐趣和练习编写一个lambda演算解释器。我通过添加ctype
facet来将标点符号定义为空格,从而使iostream正确地标记化标识符:
struct token_ctype : ctype<char> {
mask t[ table_size ];
token_ctype()
: ctype<char>( t ) {
for ( size_t tx = 0; tx < table_size; ++ tx ) {
t[tx] = isalnum( tx )? alnum : space;
}
}
};
(classic_table()
可能更干净,但在OS X上不起作用!)
然后在我点击标识符时交换方面:
locale token_loc( in.getloc(), new token_ctype );
…
locale const &oldloc = in.imbue( token_loc );
in.unget() >> token;
in.imbue( oldloc );
网上似乎有一些令人惊讶的lambda演算代码。到目前为止,我发现的大部分内容都是unicode λ
个字符。所以我想尝试添加Unicode支持。
但ctype<wchar_t>
与ctype<char>
完全不同。没有主表;有四种方法do_is
x2,do_scan_is
和do_scan_not
。所以我这样做了:
struct token_ctype : ctype< wchar_t > {
typedef ctype<wchar_t> base;
bool do_is( mask m, char_type c ) const {
return base::do_is(m,c)
|| (m&space) && ( base::do_is(punct,c) || c == L'λ' );
}
const char_type* do_is
(const char_type* lo, const char_type* hi, mask* vec) const {
base::do_is(lo,hi,vec);
for ( mask *vp = vec; lo != hi; ++ vp, ++ lo ) {
if ( *vp & punct || *lo == L'λ' ) *vp |= space;
}
return hi;
}
const char_type *do_scan_is
(mask m, const char_type* lo, const char_type* hi) const {
if ( m & space ) m |= punct;
hi = do_scan_is(m,lo,hi);
if ( m & space ) hi = find( lo, hi, L'λ' );
return hi;
}
const char_type *do_scan_not
(mask m, const char_type* lo, const char_type* hi) const {
if ( m & space ) {
m |= punct;
while ( * ( lo = base::do_scan_not(m,lo,hi) ) == L'λ' && lo != hi )
++ lo;
return lo;
}
return base::do_scan_not(m,lo,hi);
}
};
(平面格式的道歉;预览以不同方式转换标签。)
代码不那么优雅。我更好地表达了这样一种观念,即只有标点符号才是额外的空格,但如果我有classic_table
,原始文件就会很好。
有更简单的方法吗?我真的需要所有那些重载吗? (测试显示do_scan_not
在这里是无关紧要的,但我的想法更广泛。)我是否首先滥用方面?以上是否正确?实现更少的逻辑会更好吗?
答案 0 :(得分:3)
(这是一年没有实质性的答案,我在此期间学到了很多关于iostreams的信息......)
自定义构面专门用于提供字符串提取运算符in >> token
。对于下一个可用的输入字符 c ,该运算符按use_facet< ctype< wchar_t > >( in.getloc() ).is( ios::space, c )
“定义。” (§21.3.7.9)ctype::is
只是ctype::do_is
的存根,所以似乎do_is
就足够了。
尽管如此,最近版本的GCC标准库确实在operator>>
方面实现了scan_is
。问题在于do_scan_is
被实现为对do_is
,虚拟调度和所有调用的一系列调用。头文件将do_scan_is
描述为用户优化的钩子。
因此,as-if规则似乎保留了仅提供第一次覆盖的实现。
请注意,检索掩码值的第二个覆盖是奇怪的。它可以通过逐位低效地构建掩码来实现。在GCC中,它是根据系统调用实现的,无需逐个地构建掩码,每个字符有15个调用。这似乎牺牲了性能和兼容性。幸运的是,似乎没有人使用它。
无论如何,这一切都很好,但只需使用streambuf_iterator<wchar_t>
编写一个标记化器就更容易,更易于扩展,并简化了异常处理。
答案 1 :(得分:2)
恕我直言,你发布的代码很好。如果你想要更简单的代码(可能以牺牲效率为代价),你可以使用其他方法实现一些方法,但是你做的方式还可以。
这种差异是基于这样一个事实:人们不希望他们的UNICODE计划中有几兆字节的表。