libsyntax: De-@str literal strings in the AST
This commit is contained in:
committed by
Huon Wilson
parent
70c5a0fbf7
commit
8e52b85d5a
@@ -722,7 +722,7 @@ pub type Lit = Spanned<Lit_>;
|
||||
|
||||
#[deriving(Clone, Eq, Encodable, Decodable, IterBytes)]
|
||||
pub enum Lit_ {
|
||||
LitStr(@str, StrStyle),
|
||||
LitStr(InternedString, StrStyle),
|
||||
LitBinary(@[u8]),
|
||||
LitChar(u32),
|
||||
LitInt(i64, IntTy),
|
||||
|
||||
@@ -17,6 +17,7 @@ use codemap::BytePos;
|
||||
use diagnostic::SpanHandler;
|
||||
use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
|
||||
use parse::token::InternedString;
|
||||
use parse::token;
|
||||
use crateid::CrateId;
|
||||
|
||||
use std::hashmap::HashSet;
|
||||
@@ -34,7 +35,7 @@ pub trait AttrMetaMethods {
|
||||
* Gets the string value if self is a MetaNameValue variant
|
||||
* containing a string, otherwise None.
|
||||
*/
|
||||
fn value_str(&self) -> Option<@str>;
|
||||
fn value_str(&self) -> Option<InternedString>;
|
||||
/// Gets a list of inner meta items from a list MetaItem type.
|
||||
fn meta_item_list<'a>(&'a self) -> Option<&'a [@MetaItem]>;
|
||||
|
||||
@@ -42,16 +43,18 @@ pub trait AttrMetaMethods {
|
||||
* If the meta item is a name-value type with a string value then returns
|
||||
* a tuple containing the name and string value, otherwise `None`
|
||||
*/
|
||||
fn name_str_pair(&self) -> Option<(InternedString, @str)>;
|
||||
fn name_str_pair(&self) -> Option<(InternedString,InternedString)>;
|
||||
}
|
||||
|
||||
impl AttrMetaMethods for Attribute {
|
||||
fn name(&self) -> InternedString { self.meta().name() }
|
||||
fn value_str(&self) -> Option<@str> { self.meta().value_str() }
|
||||
fn value_str(&self) -> Option<InternedString> {
|
||||
self.meta().value_str()
|
||||
}
|
||||
fn meta_item_list<'a>(&'a self) -> Option<&'a [@MetaItem]> {
|
||||
self.node.value.meta_item_list()
|
||||
}
|
||||
fn name_str_pair(&self) -> Option<(InternedString, @str)> {
|
||||
fn name_str_pair(&self) -> Option<(InternedString,InternedString)> {
|
||||
self.meta().name_str_pair()
|
||||
}
|
||||
}
|
||||
@@ -65,11 +68,11 @@ impl AttrMetaMethods for MetaItem {
|
||||
}
|
||||
}
|
||||
|
||||
fn value_str(&self) -> Option<@str> {
|
||||
fn value_str(&self) -> Option<InternedString> {
|
||||
match self.node {
|
||||
MetaNameValue(_, ref v) => {
|
||||
match v.node {
|
||||
ast::LitStr(s, _) => Some(s),
|
||||
ast::LitStr(ref s, _) => Some((*s).clone()),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
@@ -84,7 +87,7 @@ impl AttrMetaMethods for MetaItem {
|
||||
}
|
||||
}
|
||||
|
||||
fn name_str_pair(&self) -> Option<(InternedString, @str)> {
|
||||
fn name_str_pair(&self) -> Option<(InternedString,InternedString)> {
|
||||
self.value_str().map(|s| (self.name(), s))
|
||||
}
|
||||
}
|
||||
@@ -92,11 +95,11 @@ impl AttrMetaMethods for MetaItem {
|
||||
// Annoying, but required to get test_cfg to work
|
||||
impl AttrMetaMethods for @MetaItem {
|
||||
fn name(&self) -> InternedString { (**self).name() }
|
||||
fn value_str(&self) -> Option<@str> { (**self).value_str() }
|
||||
fn value_str(&self) -> Option<InternedString> { (**self).value_str() }
|
||||
fn meta_item_list<'a>(&'a self) -> Option<&'a [@MetaItem]> {
|
||||
(**self).meta_item_list()
|
||||
}
|
||||
fn name_str_pair(&self) -> Option<(InternedString, @str)> {
|
||||
fn name_str_pair(&self) -> Option<(InternedString,InternedString)> {
|
||||
(**self).name_str_pair()
|
||||
}
|
||||
}
|
||||
@@ -119,8 +122,10 @@ impl AttributeMethods for Attribute {
|
||||
fn desugar_doc(&self) -> Attribute {
|
||||
if self.node.is_sugared_doc {
|
||||
let comment = self.value_str().unwrap();
|
||||
let meta = mk_name_value_item_str(InternedString::new("doc"),
|
||||
strip_doc_comment_decoration(comment).to_managed());
|
||||
let meta = mk_name_value_item_str(
|
||||
InternedString::new("doc"),
|
||||
token::intern_and_get_ident(strip_doc_comment_decoration(
|
||||
comment.get())));
|
||||
mk_attr(meta)
|
||||
} else {
|
||||
*self
|
||||
@@ -130,7 +135,7 @@ impl AttributeMethods for Attribute {
|
||||
|
||||
/* Constructors */
|
||||
|
||||
pub fn mk_name_value_item_str(name: InternedString, value: @str)
|
||||
pub fn mk_name_value_item_str(name: InternedString, value: InternedString)
|
||||
-> @MetaItem {
|
||||
let value_lit = dummy_spanned(ast::LitStr(value, ast::CookedStr));
|
||||
mk_name_value_item(name, value_lit)
|
||||
@@ -157,8 +162,9 @@ pub fn mk_attr(item: @MetaItem) -> Attribute {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn mk_sugared_doc_attr(text: @str, lo: BytePos, hi: BytePos) -> Attribute {
|
||||
let style = doc_comment_style(text);
|
||||
pub fn mk_sugared_doc_attr(text: InternedString, lo: BytePos, hi: BytePos)
|
||||
-> Attribute {
|
||||
let style = doc_comment_style(text.get());
|
||||
let lit = spanned(lo, hi, ast::LitStr(text, ast::CookedStr));
|
||||
let attr = Attribute_ {
|
||||
style: style,
|
||||
@@ -191,14 +197,14 @@ pub fn contains_name<AM: AttrMetaMethods>(metas: &[AM], name: &str) -> bool {
|
||||
}
|
||||
|
||||
pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str)
|
||||
-> Option<@str> {
|
||||
-> Option<InternedString> {
|
||||
attrs.iter()
|
||||
.find(|at| at.name().equiv(&name))
|
||||
.and_then(|at| at.value_str())
|
||||
}
|
||||
|
||||
pub fn last_meta_item_value_str_by_name(items: &[@MetaItem], name: &str)
|
||||
-> Option<@str> {
|
||||
-> Option<InternedString> {
|
||||
items.rev_iter()
|
||||
.find(|mi| mi.name().equiv(&name))
|
||||
.and_then(|i| i.value_str())
|
||||
@@ -247,7 +253,7 @@ pub fn find_linkage_metas(attrs: &[Attribute]) -> ~[@MetaItem] {
|
||||
pub fn find_crateid(attrs: &[Attribute]) -> Option<CrateId> {
|
||||
match first_attr_value_str_by_name(attrs, "crate_id") {
|
||||
None => None,
|
||||
Some(id) => from_str::<CrateId>(id),
|
||||
Some(id) => from_str::<CrateId>(id.get()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +337,7 @@ pub fn test_cfg<AM: AttrMetaMethods, It: Iterator<AM>>
|
||||
/// Represents the #[deprecated="foo"] (etc) attributes.
|
||||
pub struct Stability {
|
||||
level: StabilityLevel,
|
||||
text: Option<@str>
|
||||
text: Option<InternedString>
|
||||
}
|
||||
|
||||
/// The available stability levels.
|
||||
@@ -346,7 +352,8 @@ pub enum StabilityLevel {
|
||||
}
|
||||
|
||||
/// Find the first stability attribute. `None` if none exists.
|
||||
pub fn find_stability<AM: AttrMetaMethods, It: Iterator<AM>>(mut metas: It) -> Option<Stability> {
|
||||
pub fn find_stability<AM: AttrMetaMethods, It: Iterator<AM>>(mut metas: It)
|
||||
-> Option<Stability> {
|
||||
for m in metas {
|
||||
let level = match m.name().get() {
|
||||
"deprecated" => Deprecated,
|
||||
|
||||
@@ -17,6 +17,7 @@ use codemap::Span;
|
||||
use ext::base;
|
||||
use ext::base::*;
|
||||
use parse;
|
||||
use parse::token::InternedString;
|
||||
use parse::token;
|
||||
|
||||
enum State {
|
||||
@@ -43,7 +44,7 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
cx.cfg(),
|
||||
tts.to_owned());
|
||||
|
||||
let mut asm = @"";
|
||||
let mut asm = InternedString::new("");
|
||||
let mut asm_str_style = None;
|
||||
let mut outputs = ~[];
|
||||
let mut inputs = ~[];
|
||||
@@ -191,7 +192,7 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
MRExpr(@ast::Expr {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: ast::ExprInlineAsm(ast::InlineAsm {
|
||||
asm: asm,
|
||||
asm: asm.get().to_managed(),
|
||||
asm_str_style: asm_str_style.unwrap(),
|
||||
clobbers: cons.to_managed(),
|
||||
inputs: inputs,
|
||||
|
||||
@@ -16,7 +16,7 @@ use ext;
|
||||
use ext::expand;
|
||||
use parse;
|
||||
use parse::token;
|
||||
use parse::token::{ident_to_str, intern, str_to_ident};
|
||||
use parse::token::{InternedString, ident_to_str, intern, str_to_ident};
|
||||
use util::small_vector::SmallVector;
|
||||
|
||||
use std::hashmap::HashMap;
|
||||
@@ -407,11 +407,11 @@ impl<'a> ExtCtxt<'a> {
|
||||
/// Extract a string literal from `expr`, emitting `err_msg` if `expr`
|
||||
/// is not a string literal. This does not stop compilation on error,
|
||||
/// merely emits a non-fatal error and returns None.
|
||||
pub fn expr_to_str(cx: &ExtCtxt, expr: @ast::Expr,
|
||||
err_msg: &str) -> Option<(@str, ast::StrStyle)> {
|
||||
pub fn expr_to_str(cx: &ExtCtxt, expr: @ast::Expr, err_msg: &str)
|
||||
-> Option<(InternedString, ast::StrStyle)> {
|
||||
match expr.node {
|
||||
ast::ExprLit(l) => match l.node {
|
||||
ast::LitStr(s, style) => return Some((s, style)),
|
||||
ast::LitStr(s, style) => return Some(((*s).clone(), style)),
|
||||
_ => cx.span_err(l.span, err_msg)
|
||||
},
|
||||
_ => cx.span_err(expr.span, err_msg)
|
||||
@@ -424,7 +424,9 @@ pub fn expr_to_str(cx: &ExtCtxt, expr: @ast::Expr,
|
||||
/// compilation should call
|
||||
/// `cx.parse_sess.span_diagnostic.abort_if_errors()` (this should be
|
||||
/// done as rarely as possible).
|
||||
pub fn check_zero_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree],
|
||||
pub fn check_zero_tts(cx: &ExtCtxt,
|
||||
sp: Span,
|
||||
tts: &[ast::TokenTree],
|
||||
name: &str) {
|
||||
if tts.len() != 0 {
|
||||
cx.span_err(sp, format!("{} takes no arguments", name));
|
||||
|
||||
@@ -19,6 +19,7 @@ use fold::Folder;
|
||||
use opt_vec;
|
||||
use opt_vec::OptVec;
|
||||
use parse::token::special_idents;
|
||||
use parse::token;
|
||||
|
||||
pub struct Field {
|
||||
ident: ast::Ident,
|
||||
@@ -134,13 +135,13 @@ pub trait AstBuilder {
|
||||
fn expr_vec(&self, sp: Span, exprs: ~[@ast::Expr]) -> @ast::Expr;
|
||||
fn expr_vec_uniq(&self, sp: Span, exprs: ~[@ast::Expr]) -> @ast::Expr;
|
||||
fn expr_vec_slice(&self, sp: Span, exprs: ~[@ast::Expr]) -> @ast::Expr;
|
||||
fn expr_str(&self, sp: Span, s: @str) -> @ast::Expr;
|
||||
fn expr_str_uniq(&self, sp: Span, s: @str) -> @ast::Expr;
|
||||
fn expr_str(&self, sp: Span, s: InternedString) -> @ast::Expr;
|
||||
fn expr_str_uniq(&self, sp: Span, s: InternedString) -> @ast::Expr;
|
||||
|
||||
fn expr_some(&self, sp: Span, expr: @ast::Expr) -> @ast::Expr;
|
||||
fn expr_none(&self, sp: Span) -> @ast::Expr;
|
||||
|
||||
fn expr_fail(&self, span: Span, msg: @str) -> @ast::Expr;
|
||||
fn expr_fail(&self, span: Span, msg: InternedString) -> @ast::Expr;
|
||||
fn expr_unreachable(&self, span: Span) -> @ast::Expr;
|
||||
|
||||
fn pat(&self, span: Span, pat: ast::Pat_) -> @ast::Pat;
|
||||
@@ -589,10 +590,10 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||
fn expr_vec_slice(&self, sp: Span, exprs: ~[@ast::Expr]) -> @ast::Expr {
|
||||
self.expr_vstore(sp, self.expr_vec(sp, exprs), ast::ExprVstoreSlice)
|
||||
}
|
||||
fn expr_str(&self, sp: Span, s: @str) -> @ast::Expr {
|
||||
fn expr_str(&self, sp: Span, s: InternedString) -> @ast::Expr {
|
||||
self.expr_lit(sp, ast::LitStr(s, ast::CookedStr))
|
||||
}
|
||||
fn expr_str_uniq(&self, sp: Span, s: @str) -> @ast::Expr {
|
||||
fn expr_str_uniq(&self, sp: Span, s: InternedString) -> @ast::Expr {
|
||||
self.expr_vstore(sp, self.expr_str(sp, s), ast::ExprVstoreUniq)
|
||||
}
|
||||
|
||||
@@ -620,7 +621,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||
self.expr_path(none)
|
||||
}
|
||||
|
||||
fn expr_fail(&self, span: Span, msg: @str) -> @ast::Expr {
|
||||
fn expr_fail(&self, span: Span, msg: InternedString) -> @ast::Expr {
|
||||
let loc = self.codemap().lookup_char_pos(span.lo);
|
||||
self.expr_call_global(
|
||||
span,
|
||||
@@ -631,13 +632,16 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||
],
|
||||
~[
|
||||
self.expr_str(span, msg),
|
||||
self.expr_str(span, loc.file.name),
|
||||
self.expr_str(span,
|
||||
token::intern_and_get_ident(loc.file.name)),
|
||||
self.expr_uint(span, loc.line),
|
||||
])
|
||||
}
|
||||
|
||||
fn expr_unreachable(&self, span: Span) -> @ast::Expr {
|
||||
self.expr_fail(span, @"internal error: entered unreachable code")
|
||||
self.expr_fail(span,
|
||||
InternedString::new(
|
||||
"internal error: entered unreachable code"))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) ->
|
||||
// expression is a literal
|
||||
ast::ExprLit(lit) => match lit.node {
|
||||
// string literal, push each byte to vector expression
|
||||
ast::LitStr(s, _) => {
|
||||
for byte in s.bytes() {
|
||||
ast::LitStr(ref s, _) => {
|
||||
for byte in s.get().bytes() {
|
||||
bytes.push(cx.expr_u8(expr.span, byte));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use ast;
|
||||
use codemap;
|
||||
use ext::base;
|
||||
use ext::build::AstBuilder;
|
||||
use parse::token;
|
||||
|
||||
pub fn expand_syntax_ext(cx: &mut base::ExtCtxt,
|
||||
sp: codemap::Span,
|
||||
@@ -28,8 +29,10 @@ pub fn expand_syntax_ext(cx: &mut base::ExtCtxt,
|
||||
match e.node {
|
||||
ast::ExprLit(lit) => {
|
||||
match lit.node {
|
||||
ast::LitStr(s, _) | ast::LitFloat(s, _)
|
||||
| ast::LitFloatUnsuffixed(s) => {
|
||||
ast::LitStr(ref s, _) => {
|
||||
accumulator.push_str(s.get());
|
||||
}
|
||||
ast::LitFloat(s, _) | ast::LitFloatUnsuffixed(s) => {
|
||||
accumulator.push_str(s);
|
||||
}
|
||||
ast::LitChar(c) => {
|
||||
@@ -55,5 +58,5 @@ pub fn expand_syntax_ext(cx: &mut base::ExtCtxt,
|
||||
}
|
||||
}
|
||||
}
|
||||
return base::MRExpr(cx.expr_str(sp, accumulator.to_managed()));
|
||||
base::MRExpr(cx.expr_str(sp, token::intern_and_get_ident(accumulator)))
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ use codemap::Span;
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use parse::token::InternedString;
|
||||
use parse::token;
|
||||
|
||||
pub fn expand_deriving_decodable(cx: &ExtCtxt,
|
||||
span: Span,
|
||||
@@ -82,10 +84,15 @@ fn decodable_substructure(cx: &ExtCtxt, trait_span: Span,
|
||||
cx.expr_uint(span, field),
|
||||
lambdadecode])
|
||||
});
|
||||
cx.expr_method_call(trait_span, decoder, cx.ident_of("read_struct"),
|
||||
~[cx.expr_str(trait_span, cx.str_of(substr.type_ident)),
|
||||
cx.expr_uint(trait_span, nfields),
|
||||
cx.lambda_expr_1(trait_span, result, blkarg)])
|
||||
cx.expr_method_call(trait_span,
|
||||
decoder,
|
||||
cx.ident_of("read_struct"),
|
||||
~[
|
||||
cx.expr_str(trait_span,
|
||||
token::get_ident(substr.type_ident.name)),
|
||||
cx.expr_uint(trait_span, nfields),
|
||||
cx.lambda_expr_1(trait_span, result, blkarg)
|
||||
])
|
||||
}
|
||||
StaticEnum(_, ref fields) => {
|
||||
let variant = cx.ident_of("i");
|
||||
@@ -95,7 +102,8 @@ fn decodable_substructure(cx: &ExtCtxt, trait_span: Span,
|
||||
let rvariant_arg = cx.ident_of("read_enum_variant_arg");
|
||||
|
||||
for (i, &(name, v_span, ref parts)) in fields.iter().enumerate() {
|
||||
variants.push(cx.expr_str(v_span, cx.str_of(name)));
|
||||
variants.push(cx.expr_str(v_span,
|
||||
token::get_ident(name.name)));
|
||||
|
||||
let decoded = decode_static_fields(cx,
|
||||
v_span,
|
||||
@@ -120,9 +128,14 @@ fn decodable_substructure(cx: &ExtCtxt, trait_span: Span,
|
||||
let result = cx.expr_method_call(trait_span, blkdecoder,
|
||||
cx.ident_of("read_enum_variant"),
|
||||
~[variant_vec, lambda]);
|
||||
cx.expr_method_call(trait_span, decoder, cx.ident_of("read_enum"),
|
||||
~[cx.expr_str(trait_span, cx.str_of(substr.type_ident)),
|
||||
cx.lambda_expr_1(trait_span, result, blkarg)])
|
||||
cx.expr_method_call(trait_span,
|
||||
decoder,
|
||||
cx.ident_of("read_enum"),
|
||||
~[
|
||||
cx.expr_str(trait_span,
|
||||
token::get_ident(substr.type_ident.name)),
|
||||
cx.lambda_expr_1(trait_span, result, blkarg)
|
||||
])
|
||||
}
|
||||
_ => cx.bug("expected StaticEnum or StaticStruct in deriving(Decodable)")
|
||||
};
|
||||
@@ -135,7 +148,7 @@ fn decode_static_fields(cx: &ExtCtxt,
|
||||
trait_span: Span,
|
||||
outer_pat_ident: Ident,
|
||||
fields: &StaticFields,
|
||||
getarg: |Span, @str, uint| -> @Expr)
|
||||
getarg: |Span, InternedString, uint| -> @Expr)
|
||||
-> @Expr {
|
||||
match *fields {
|
||||
Unnamed(ref fields) => {
|
||||
@@ -143,7 +156,10 @@ fn decode_static_fields(cx: &ExtCtxt,
|
||||
cx.expr_ident(trait_span, outer_pat_ident)
|
||||
} else {
|
||||
let fields = fields.iter().enumerate().map(|(i, &span)| {
|
||||
getarg(span, format!("_field{}", i).to_managed(), i)
|
||||
getarg(span,
|
||||
token::intern_and_get_ident(format!("_field{}",
|
||||
i)),
|
||||
i)
|
||||
}).collect();
|
||||
|
||||
cx.expr_call_ident(trait_span, outer_pat_ident, fields)
|
||||
@@ -152,7 +168,9 @@ fn decode_static_fields(cx: &ExtCtxt,
|
||||
Named(ref fields) => {
|
||||
// use the field's span to get nicer error messages.
|
||||
let fields = fields.iter().enumerate().map(|(i, &(name, span))| {
|
||||
cx.field_imm(span, name, getarg(span, cx.str_of(name), i))
|
||||
cx.field_imm(span,
|
||||
name,
|
||||
getarg(span, token::get_ident(name.name), i))
|
||||
}).collect();
|
||||
cx.expr_struct_ident(trait_span, outer_pat_ident, fields)
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ use codemap::Span;
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use parse::token;
|
||||
|
||||
pub fn expand_deriving_encodable(cx: &ExtCtxt,
|
||||
span: Span,
|
||||
@@ -125,10 +126,17 @@ fn encodable_substructure(cx: &ExtCtxt, trait_span: Span,
|
||||
Struct(ref fields) => {
|
||||
let emit_struct_field = cx.ident_of("emit_struct_field");
|
||||
let mut stmts = ~[];
|
||||
for (i, &FieldInfo { name, self_, span, .. }) in fields.iter().enumerate() {
|
||||
for (i, &FieldInfo {
|
||||
name,
|
||||
self_,
|
||||
span,
|
||||
..
|
||||
}) in fields.iter().enumerate() {
|
||||
let name = match name {
|
||||
Some(id) => cx.str_of(id),
|
||||
None => format!("_field{}", i).to_managed()
|
||||
Some(id) => token::get_ident(id),
|
||||
None => {
|
||||
token::intern_and_get_ident(format!("_field{}", i))
|
||||
}
|
||||
};
|
||||
let enc = cx.expr_method_call(span, self_, encode, ~[blkencoder]);
|
||||
let lambda = cx.lambda_expr_1(span, enc, blkarg);
|
||||
@@ -141,10 +149,15 @@ fn encodable_substructure(cx: &ExtCtxt, trait_span: Span,
|
||||
}
|
||||
|
||||
let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
|
||||
cx.expr_method_call(trait_span, encoder, cx.ident_of("emit_struct"),
|
||||
~[cx.expr_str(trait_span, cx.str_of(substr.type_ident)),
|
||||
cx.expr_uint(trait_span, fields.len()),
|
||||
blk])
|
||||
cx.expr_method_call(trait_span,
|
||||
encoder,
|
||||
cx.ident_of("emit_struct"),
|
||||
~[
|
||||
cx.expr_str(trait_span,
|
||||
token::get_ident(substr.type_ident.name)),
|
||||
cx.expr_uint(trait_span, fields.len()),
|
||||
blk
|
||||
])
|
||||
}
|
||||
|
||||
EnumMatching(idx, variant, ref fields) => {
|
||||
@@ -167,7 +180,8 @@ fn encodable_substructure(cx: &ExtCtxt, trait_span: Span,
|
||||
}
|
||||
|
||||
let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
|
||||
let name = cx.expr_str(trait_span, cx.str_of(variant.node.name));
|
||||
let name = cx.expr_str(trait_span,
|
||||
token::get_ident(variant.node.name));
|
||||
let call = cx.expr_method_call(trait_span, blkencoder,
|
||||
cx.ident_of("emit_enum_variant"),
|
||||
~[name,
|
||||
@@ -175,11 +189,14 @@ fn encodable_substructure(cx: &ExtCtxt, trait_span: Span,
|
||||
cx.expr_uint(trait_span, fields.len()),
|
||||
blk]);
|
||||
let blk = cx.lambda_expr_1(trait_span, call, blkarg);
|
||||
let ret = cx.expr_method_call(trait_span, encoder,
|
||||
let ret = cx.expr_method_call(trait_span,
|
||||
encoder,
|
||||
cx.ident_of("emit_enum"),
|
||||
~[cx.expr_str(trait_span,
|
||||
cx.str_of(substr.type_ident)),
|
||||
blk]);
|
||||
~[
|
||||
cx.expr_str(trait_span,
|
||||
token::get_ident(substr.type_ident.name)),
|
||||
blk
|
||||
]);
|
||||
cx.expr_block(cx.block(trait_span, ~[me], Some(ret)))
|
||||
}
|
||||
|
||||
|
||||
@@ -185,6 +185,7 @@ use codemap;
|
||||
use codemap::Span;
|
||||
use opt_vec;
|
||||
use parse::token::InternedString;
|
||||
use parse::token;
|
||||
|
||||
use std::vec;
|
||||
|
||||
@@ -398,7 +399,9 @@ impl<'a> TraitDef<'a> {
|
||||
self.span,
|
||||
cx.meta_name_value(self.span,
|
||||
InternedString::new("doc"),
|
||||
ast::LitStr(@"Automatically derived.", ast::CookedStr)));
|
||||
ast::LitStr(token::intern_and_get_ident(
|
||||
"Automatically derived."),
|
||||
ast::CookedStr)));
|
||||
cx.item(
|
||||
self.span,
|
||||
::parse::token::special_idents::clownshoes_extensions,
|
||||
|
||||
@@ -14,6 +14,7 @@ use codemap::Span;
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use parse::token::InternedString;
|
||||
|
||||
pub fn expand_deriving_from_primitive(cx: &ExtCtxt,
|
||||
span: Span,
|
||||
@@ -73,13 +74,13 @@ fn cs_from(name: &str, cx: &ExtCtxt, trait_span: Span, substr: &Substructure) ->
|
||||
match *substr.fields {
|
||||
StaticStruct(..) => {
|
||||
cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs");
|
||||
return cx.expr_fail(trait_span, @"");
|
||||
return cx.expr_fail(trait_span, InternedString::new(""));
|
||||
}
|
||||
StaticEnum(enum_def, _) => {
|
||||
if enum_def.variants.is_empty() {
|
||||
cx.span_err(trait_span,
|
||||
"`FromPrimitive` cannot be derived for enums with no variants");
|
||||
return cx.expr_fail(trait_span, @"");
|
||||
return cx.expr_fail(trait_span, InternedString::new(""));
|
||||
}
|
||||
|
||||
let mut arms = ~[];
|
||||
@@ -91,7 +92,8 @@ fn cs_from(name: &str, cx: &ExtCtxt, trait_span: Span, substr: &Substructure) ->
|
||||
cx.span_err(trait_span,
|
||||
"`FromPrimitive` cannot be derived for \
|
||||
enum variants with arguments");
|
||||
return cx.expr_fail(trait_span, @"");
|
||||
return cx.expr_fail(trait_span,
|
||||
InternedString::new(""));
|
||||
}
|
||||
let span = variant.span;
|
||||
|
||||
@@ -117,7 +119,8 @@ fn cs_from(name: &str, cx: &ExtCtxt, trait_span: Span, substr: &Substructure) ->
|
||||
cx.span_err(trait_span,
|
||||
"`FromPrimitive` cannot be derived for enums \
|
||||
with struct variants");
|
||||
return cx.expr_fail(trait_span, @"");
|
||||
return cx.expr_fail(trait_span,
|
||||
InternedString::new(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ use codemap::Span;
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use parse::token::InternedString;
|
||||
use parse::token;
|
||||
|
||||
pub fn expand_deriving_to_str(cx: &ExtCtxt,
|
||||
span: Span,
|
||||
@@ -47,18 +49,20 @@ pub fn expand_deriving_to_str(cx: &ExtCtxt,
|
||||
// doesn't invoke the to_str() method on each field. Hence we mirror
|
||||
// the logic of the repr_to_str() method, but with tweaks to call to_str()
|
||||
// on sub-fields.
|
||||
fn to_str_substructure(cx: &ExtCtxt, span: Span,
|
||||
substr: &Substructure) -> @Expr {
|
||||
fn to_str_substructure(cx: &ExtCtxt, span: Span, substr: &Substructure)
|
||||
-> @Expr {
|
||||
let to_str = cx.ident_of("to_str");
|
||||
|
||||
let doit = |start: &str, end: @str, name: ast::Ident,
|
||||
let doit = |start: &str,
|
||||
end: InternedString,
|
||||
name: ast::Ident,
|
||||
fields: &[FieldInfo]| {
|
||||
if fields.len() == 0 {
|
||||
cx.expr_str_uniq(span, cx.str_of(name))
|
||||
cx.expr_str_uniq(span, token::get_ident(name.name))
|
||||
} else {
|
||||
let buf = cx.ident_of("buf");
|
||||
let start = cx.str_of(name) + start;
|
||||
let init = cx.expr_str_uniq(span, start.to_managed());
|
||||
let start = token::intern_and_get_ident(cx.str_of(name) + start);
|
||||
let init = cx.expr_str_uniq(span, start);
|
||||
let mut stmts = ~[cx.stmt_let(span, true, buf, init)];
|
||||
let push_str = cx.ident_of("push_str");
|
||||
|
||||
@@ -70,38 +74,52 @@ fn to_str_substructure(cx: &ExtCtxt, span: Span,
|
||||
|
||||
for (i, &FieldInfo {name, span, self_, .. }) in fields.iter().enumerate() {
|
||||
if i > 0 {
|
||||
push(cx.expr_str(span, @", "));
|
||||
push(cx.expr_str(span, InternedString::new(", ")));
|
||||
}
|
||||
match name {
|
||||
None => {}
|
||||
Some(id) => {
|
||||
let name = cx.str_of(id) + ": ";
|
||||
push(cx.expr_str(span, name.to_managed()));
|
||||
push(cx.expr_str(span,
|
||||
token::intern_and_get_ident(name)));
|
||||
}
|
||||
}
|
||||
push(cx.expr_method_call(span, self_, to_str, ~[]));
|
||||
}
|
||||
push(cx.expr_str(span, end));
|
||||
|
||||
cx.expr_block(cx.block(span, stmts, Some(cx.expr_ident(span, buf))))
|
||||
cx.expr_block(cx.block(span, stmts, Some(cx.expr_ident(span,
|
||||
buf))))
|
||||
}
|
||||
};
|
||||
|
||||
return match *substr.fields {
|
||||
Struct(ref fields) => {
|
||||
if fields.len() == 0 || fields[0].name.is_none() {
|
||||
doit("(", @")", substr.type_ident, *fields)
|
||||
doit("(",
|
||||
InternedString::new(")"),
|
||||
substr.type_ident,
|
||||
*fields)
|
||||
} else {
|
||||
doit("{", @"}", substr.type_ident, *fields)
|
||||
doit("{",
|
||||
InternedString::new("}"),
|
||||
substr.type_ident,
|
||||
*fields)
|
||||
}
|
||||
}
|
||||
|
||||
EnumMatching(_, variant, ref fields) => {
|
||||
match variant.node.kind {
|
||||
ast::TupleVariantKind(..) =>
|
||||
doit("(", @")", variant.node.name, *fields),
|
||||
doit("(",
|
||||
InternedString::new(")"),
|
||||
variant.node.name,
|
||||
*fields),
|
||||
ast::StructVariantKind(..) =>
|
||||
doit("{", @"}", variant.node.name, *fields),
|
||||
doit("{",
|
||||
InternedString::new("}"),
|
||||
variant.node.name,
|
||||
*fields),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ use codemap::Span;
|
||||
use ext::base::*;
|
||||
use ext::base;
|
||||
use ext::build::AstBuilder;
|
||||
use parse::token;
|
||||
|
||||
use std::os;
|
||||
|
||||
@@ -52,7 +53,11 @@ pub fn expand_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
Some((v, _style)) => v
|
||||
};
|
||||
let msg = match exprs.len() {
|
||||
1 => format!("environment variable `{}` not defined", var).to_managed(),
|
||||
1 => {
|
||||
token::intern_and_get_ident(format!("environment variable `{}` \
|
||||
not defined",
|
||||
var))
|
||||
}
|
||||
2 => {
|
||||
match expr_to_str(cx, exprs[1], "expected string literal") {
|
||||
None => return MacResult::dummy_expr(),
|
||||
@@ -65,12 +70,12 @@ pub fn expand_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
}
|
||||
};
|
||||
|
||||
let e = match os::getenv(var) {
|
||||
let e = match os::getenv(var.get()) {
|
||||
None => {
|
||||
cx.span_err(sp, msg);
|
||||
cx.span_err(sp, msg.get());
|
||||
cx.expr_uint(sp, 0)
|
||||
}
|
||||
Some(s) => cx.expr_str(sp, s.to_managed())
|
||||
Some(s) => cx.expr_str(sp, token::intern_and_get_ident(s))
|
||||
};
|
||||
MRExpr(e)
|
||||
}
|
||||
|
||||
@@ -416,7 +416,7 @@ impl<'a> Context<'a> {
|
||||
let result = arm.result.iter().map(|p| {
|
||||
self.trans_piece(p)
|
||||
}).collect();
|
||||
let s = arm.selector.to_managed();
|
||||
let s = token::intern_and_get_ident(arm.selector);
|
||||
let selector = self.ecx.expr_str(sp, s);
|
||||
self.ecx.expr_struct(sp, p, ~[
|
||||
self.ecx.field_imm(sp,
|
||||
@@ -492,8 +492,12 @@ impl<'a> Context<'a> {
|
||||
|
||||
match *piece {
|
||||
parse::String(s) => {
|
||||
self.ecx.expr_call_global(sp, rtpath("String"),
|
||||
~[self.ecx.expr_str(sp, s.to_managed())])
|
||||
let s = token::intern_and_get_ident(s);
|
||||
self.ecx.expr_call_global(sp,
|
||||
rtpath("String"),
|
||||
~[
|
||||
self.ecx.expr_str(sp, s)
|
||||
])
|
||||
}
|
||||
parse::CurrentArgument => {
|
||||
let nil = self.ecx.expr_lit(sp, ast::LitNil);
|
||||
@@ -763,8 +767,9 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
|
||||
// Be sure to recursively expand macros just in case the format string uses
|
||||
// a macro to build the format expression.
|
||||
let expr = cx.ecx.expand_expr(efmt);
|
||||
let fmt = match expr_to_str(cx.ecx, expr,
|
||||
"format argument must be a string literal.") {
|
||||
let fmt = match expr_to_str(cx.ecx,
|
||||
expr,
|
||||
"format argument must be a string literal.") {
|
||||
Some((fmt, _)) => fmt,
|
||||
None => return MacResult::dummy_expr()
|
||||
};
|
||||
@@ -776,7 +781,7 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
|
||||
cx.ecx.span_err(efmt.span, m);
|
||||
}
|
||||
}).inside(|| {
|
||||
for piece in parse::Parser::new(fmt) {
|
||||
for piece in parse::Parser::new(fmt.get()) {
|
||||
if !err {
|
||||
cx.verify_piece(&piece);
|
||||
let piece = cx.trans_piece(&piece);
|
||||
|
||||
@@ -31,6 +31,7 @@ use parse;
|
||||
pub mod rt {
|
||||
use ast;
|
||||
use ext::base::ExtCtxt;
|
||||
use parse::token;
|
||||
use parse;
|
||||
use print::pprust;
|
||||
|
||||
@@ -118,7 +119,8 @@ pub mod rt {
|
||||
|
||||
impl<'a> ToSource for &'a str {
|
||||
fn to_source(&self) -> @str {
|
||||
let lit = dummy_spanned(ast::LitStr(self.to_managed(), ast::CookedStr));
|
||||
let lit = dummy_spanned(ast::LitStr(
|
||||
token::intern_and_get_ident(*self), ast::CookedStr));
|
||||
pprust::lit_to_str(&lit).to_managed()
|
||||
}
|
||||
}
|
||||
@@ -349,7 +351,7 @@ fn id_ext(str: &str) -> ast::Ident {
|
||||
|
||||
// Lift an ident to the expr that evaluates to that ident.
|
||||
fn mk_ident(cx: &ExtCtxt, sp: Span, ident: ast::Ident) -> @ast::Expr {
|
||||
let e_str = cx.expr_str(sp, cx.str_of(ident));
|
||||
let e_str = cx.expr_str(sp, token::get_ident(ident.name));
|
||||
cx.expr_method_call(sp,
|
||||
cx.expr_ident(sp, id_ext("ext_cx")),
|
||||
id_ext("ident_of"),
|
||||
|
||||
@@ -16,7 +16,8 @@ use ext::base::*;
|
||||
use ext::base;
|
||||
use ext::build::AstBuilder;
|
||||
use parse;
|
||||
use parse::token::{get_ident_interner};
|
||||
use parse::token::get_ident_interner;
|
||||
use parse::token;
|
||||
use print::pprust;
|
||||
|
||||
use std::io;
|
||||
@@ -57,21 +58,21 @@ pub fn expand_file(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
|
||||
let topmost = topmost_expn_info(cx.backtrace().unwrap());
|
||||
let loc = cx.codemap().lookup_char_pos(topmost.call_site.lo);
|
||||
let filename = loc.file.name;
|
||||
let filename = token::intern_and_get_ident(loc.file.name);
|
||||
base::MRExpr(cx.expr_str(topmost.call_site, filename))
|
||||
}
|
||||
|
||||
pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
-> base::MacResult {
|
||||
let s = pprust::tts_to_str(tts, get_ident_interner());
|
||||
base::MRExpr(cx.expr_str(sp, s.to_managed()))
|
||||
base::MRExpr(cx.expr_str(sp, token::intern_and_get_ident(s)))
|
||||
}
|
||||
|
||||
pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
-> base::MacResult {
|
||||
base::check_zero_tts(cx, sp, tts, "module_path!");
|
||||
base::MRExpr(cx.expr_str(sp,
|
||||
cx.mod_path().map(|x| cx.str_of(*x)).connect("::").to_managed()))
|
||||
let string = cx.mod_path().map(|x| cx.str_of(*x)).connect("::");
|
||||
base::MRExpr(cx.expr_str(sp, token::intern_and_get_ident(string)))
|
||||
}
|
||||
|
||||
// include! : parse the given file as an expr
|
||||
@@ -117,7 +118,7 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
let filename = file.display().to_str().to_managed();
|
||||
cx.parse_sess.cm.new_filemap(filename, src);
|
||||
|
||||
base::MRExpr(cx.expr_str(sp, src))
|
||||
base::MRExpr(cx.expr_str(sp, token::intern_and_get_ident(src)))
|
||||
}
|
||||
None => {
|
||||
cx.span_err(sp, format!("{} wasn't a utf-8 file", file.display()));
|
||||
|
||||
@@ -326,7 +326,9 @@ fn fold_meta_item_<T: Folder>(mi: @MetaItem, fld: &mut T) -> @MetaItem {
|
||||
let fold_meta_item = |x| fold_meta_item_(x, fld);
|
||||
MetaList((*id).clone(), mis.map(|e| fold_meta_item(*e)))
|
||||
}
|
||||
MetaNameValue(ref id, s) => MetaNameValue((*id).clone(), s)
|
||||
MetaNameValue(ref id, ref s) => {
|
||||
MetaNameValue((*id).clone(), (*s).clone())
|
||||
}
|
||||
},
|
||||
span: fld.new_span(mi.span) }
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ impl ParserAttr for Parser {
|
||||
}
|
||||
token::DOC_COMMENT(s) => {
|
||||
let attr = ::attr::mk_sugared_doc_attr(
|
||||
self.id_to_str(s),
|
||||
self.id_to_interned_str(s),
|
||||
self.span.lo,
|
||||
self.span.hi
|
||||
);
|
||||
@@ -133,7 +133,7 @@ impl ParserAttr for Parser {
|
||||
}
|
||||
token::DOC_COMMENT(s) => {
|
||||
self.bump();
|
||||
::attr::mk_sugared_doc_attr(self.id_to_str(s),
|
||||
::attr::mk_sugared_doc_attr(self.id_to_interned_str(s),
|
||||
self.span.lo,
|
||||
self.span.hi)
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@ pub fn doc_comment_style(comment: &str) -> ast::AttrStyle {
|
||||
}
|
||||
|
||||
pub fn strip_doc_comment_decoration(comment: &str) -> ~str {
|
||||
|
||||
/// remove whitespace-only lines from the start/end of lines
|
||||
fn vertical_trim(lines: ~[~str]) -> ~[~str] {
|
||||
let mut i = 0u;
|
||||
|
||||
@@ -345,7 +345,7 @@ pub struct Parser {
|
||||
/// extra detail when the same error is seen twice
|
||||
obsolete_set: HashSet<ObsoleteSyntax>,
|
||||
/// Used to determine the path to externally loaded source files
|
||||
mod_path_stack: ~[@str],
|
||||
mod_path_stack: ~[InternedString],
|
||||
/// Stack of spans of open delimiters. Used for error message.
|
||||
open_braces: ~[Span],
|
||||
/* do not copy the parser; its state is tied to outside state */
|
||||
@@ -1408,8 +1408,12 @@ impl Parser {
|
||||
token::LIT_FLOAT(s, ft) => LitFloat(self.id_to_str(s), ft),
|
||||
token::LIT_FLOAT_UNSUFFIXED(s) =>
|
||||
LitFloatUnsuffixed(self.id_to_str(s)),
|
||||
token::LIT_STR(s) => LitStr(self.id_to_str(s), ast::CookedStr),
|
||||
token::LIT_STR_RAW(s, n) => LitStr(self.id_to_str(s), ast::RawStr(n)),
|
||||
token::LIT_STR(s) => {
|
||||
LitStr(self.id_to_interned_str(s), ast::CookedStr)
|
||||
}
|
||||
token::LIT_STR_RAW(s, n) => {
|
||||
LitStr(self.id_to_interned_str(s), ast::RawStr(n))
|
||||
}
|
||||
token::LPAREN => { self.expect(&token::RPAREN); LitNil },
|
||||
_ => { self.unexpected_last(tok); }
|
||||
}
|
||||
@@ -4146,11 +4150,11 @@ impl Parser {
|
||||
}
|
||||
|
||||
fn push_mod_path(&mut self, id: Ident, attrs: &[Attribute]) {
|
||||
let default_path = token::interner_get(id.name);
|
||||
let default_path = self.id_to_interned_str(id);
|
||||
let file_path = match ::attr::first_attr_value_str_by_name(attrs,
|
||||
"path") {
|
||||
Some(d) => d,
|
||||
None => default_path
|
||||
None => default_path,
|
||||
};
|
||||
self.mod_path_stack.push(file_path)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use std::cast;
|
||||
use std::char;
|
||||
use std::fmt;
|
||||
use std::local_data;
|
||||
use std::path::BytesContainer;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[deriving(Clone, Encodable, Decodable, Eq, IterBytes)]
|
||||
@@ -537,7 +538,7 @@ pub fn get_ident_interner() -> @IdentInterner {
|
||||
/// be fixed in the future by just leaking all strings until task death
|
||||
/// somehow.
|
||||
#[no_send]
|
||||
#[deriving(Clone, Eq, IterBytes, TotalEq, TotalOrd)]
|
||||
#[deriving(Clone, Eq, IterBytes, Ord, TotalEq, TotalOrd)]
|
||||
pub struct InternedString {
|
||||
priv string: @str,
|
||||
}
|
||||
@@ -571,6 +572,17 @@ impl InternedString {
|
||||
}
|
||||
}
|
||||
|
||||
impl BytesContainer for InternedString {
|
||||
fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
|
||||
// XXX(pcwalton): This is a workaround for the incorrect signature of
|
||||
// `BytesContainer`, which is itself a workaround for the lack of DST.
|
||||
unsafe {
|
||||
let this = self.get();
|
||||
cast::transmute(this.container_as_bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Default for InternedString {
|
||||
fn fmt(obj: &InternedString, f: &mut fmt::Formatter) {
|
||||
write!(f.buf, "{}", obj.string);
|
||||
|
||||
@@ -897,7 +897,7 @@ pub fn print_attribute(s: &mut State, attr: &ast::Attribute) {
|
||||
maybe_print_comment(s, attr.span.lo);
|
||||
if attr.node.is_sugared_doc {
|
||||
let comment = attr.value_str().unwrap();
|
||||
word(&mut s.s, comment);
|
||||
word(&mut s.s, comment.get());
|
||||
} else {
|
||||
word(&mut s.s, "#[");
|
||||
print_meta_item(s, attr.meta());
|
||||
@@ -1931,10 +1931,10 @@ pub fn print_meta_item(s: &mut State, item: &ast::MetaItem) {
|
||||
ibox(s, indent_unit);
|
||||
match item.node {
|
||||
ast::MetaWord(ref name) => word(&mut s.s, name.get()),
|
||||
ast::MetaNameValue(ref name, value) => {
|
||||
ast::MetaNameValue(ref name, ref value) => {
|
||||
word_space(s, name.get());
|
||||
word_space(s, "=");
|
||||
print_literal(s, &value);
|
||||
print_literal(s, value);
|
||||
}
|
||||
ast::MetaList(ref name, ref items) => {
|
||||
word(&mut s.s, name.get());
|
||||
@@ -2172,7 +2172,7 @@ pub fn print_literal(s: &mut State, lit: &ast::Lit) {
|
||||
_ => ()
|
||||
}
|
||||
match lit.node {
|
||||
ast::LitStr(st, style) => print_string(s, st, style),
|
||||
ast::LitStr(ref st, style) => print_string(s, st.get(), style),
|
||||
ast::LitChar(ch) => {
|
||||
let mut res = ~"'";
|
||||
char::from_u32(ch).unwrap().escape_default(|c| res.push_char(c));
|
||||
|
||||
Reference in New Issue
Block a user