Skip to content

Commit 7c936de

Browse files
committed
---
yaml --- r: 5055 b: refs/heads/master c: 4c936d7 h: refs/heads/master i: 5053: ea26491 5051: 4b6de60 5047: a7388c2 5039: 62fbe40 5023: 645d1af 4991: 3cc561b v: v3
1 parent b7a1646 commit 7c936de

File tree

7 files changed

+949
-154
lines changed

7 files changed

+949
-154
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
---
2-
refs/heads/master: 9e2c5f77a413bf99fff1d8b4f0629127a5f9bbd3
2+
refs/heads/master: 4c936d7992123e029dedea0380f429b6844a9ab8

trunk/src/comp/rustc.rc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ mod syntax {
6363
mod expand;
6464

6565
mod fmt;
66+
mod ifmt;
6667
mod env;
6768
mod simplext;
6869
mod concat_idents;

trunk/src/comp/syntax/ext/base.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ tag syntax_extension {
2323
fn syntax_expander_table() -> hashmap<istr, syntax_extension> {
2424
let syntax_expanders = new_str_hash::<syntax_extension>();
2525
syntax_expanders.insert(~"fmt", normal(ext::fmt::expand_syntax_ext));
26+
syntax_expanders.insert(~"ifmt", normal(ext::ifmt::expand_syntax_ext));
2627
syntax_expanders.insert(~"env", normal(ext::env::expand_syntax_ext));
2728
syntax_expanders.insert(~"macro",
2829
macro_defining(ext::simplext::add_new_extension));

trunk/src/comp/syntax/ext/ifmt.rs

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
2+
3+
/*
4+
* The compiler code necessary to support the #fmt extension. Eventually this
5+
* should all get sucked into either the standard library extfmt module or the
6+
* compiler syntax extension plugin interface.
7+
*/
8+
import std::vec;
9+
import std::str;
10+
import std::istr;
11+
import std::option;
12+
import std::option::none;
13+
import std::option::some;
14+
import std::extifmt::ct::*;
15+
import base::*;
16+
import codemap::span;
17+
export expand_syntax_ext;
18+
19+
fn expand_syntax_ext(cx: &ext_ctxt, sp: span, arg: @ast::expr,
20+
_body: &option::t<istr>) -> @ast::expr {
21+
let args: [@ast::expr] =
22+
alt arg.node {
23+
ast::expr_vec(elts, _) { elts }
24+
_ {
25+
cx.span_fatal(sp, ~"#fmt requires arguments of the form `[...]`.")
26+
}
27+
};
28+
if vec::len::<@ast::expr>(args) == 0u {
29+
cx.span_fatal(sp, ~"#fmt requires a format string");
30+
}
31+
let fmt =
32+
expr_to_str(cx, args[0],
33+
~"first argument to #fmt must be a "
34+
+ ~"string literal.");
35+
let fmtspan = args[0].span;
36+
log "Format string:";
37+
log fmt;
38+
fn parse_fmt_err_(cx: &ext_ctxt, sp: span, msg: &istr) -> ! {
39+
cx.span_fatal(sp, msg);
40+
}
41+
let parse_fmt_err = bind parse_fmt_err_(cx, fmtspan, _);
42+
let pieces = parse_fmt_string(fmt, parse_fmt_err);
43+
ret pieces_to_expr(cx, sp, pieces, args);
44+
}
45+
46+
// FIXME: A lot of these functions for producing expressions can probably
47+
// be factored out in common with other code that builds expressions.
48+
// FIXME: Cleanup the naming of these functions
49+
fn pieces_to_expr(cx: &ext_ctxt, sp: span, pieces: &[piece],
50+
args: &[@ast::expr]) -> @ast::expr {
51+
fn make_new_lit(cx: &ext_ctxt, sp: span, lit: ast::lit_) -> @ast::expr {
52+
let sp_lit = @{node: lit, span: sp};
53+
ret @{id: cx.next_id(), node: ast::expr_lit(sp_lit), span: sp};
54+
}
55+
fn make_new_str(cx: &ext_ctxt, sp: span, s: &istr) -> @ast::expr {
56+
let lit = ast::lit_str(s, ast::sk_unique);
57+
ret make_new_lit(cx, sp, lit);
58+
}
59+
fn make_new_int(cx: &ext_ctxt, sp: span, i: int) -> @ast::expr {
60+
let lit = ast::lit_int(i);
61+
ret make_new_lit(cx, sp, lit);
62+
}
63+
fn make_new_uint(cx: &ext_ctxt, sp: span, u: uint) -> @ast::expr {
64+
let lit = ast::lit_uint(u);
65+
ret make_new_lit(cx, sp, lit);
66+
}
67+
fn make_add_expr(cx: &ext_ctxt, sp: span, lhs: @ast::expr,
68+
rhs: @ast::expr) -> @ast::expr {
69+
let binexpr = ast::expr_binary(ast::add, lhs, rhs);
70+
ret @{id: cx.next_id(), node: binexpr, span: sp};
71+
}
72+
fn make_path_expr(cx: &ext_ctxt, sp: span, idents: &[ast::ident]) ->
73+
@ast::expr {
74+
let path = {global: false, idents: idents, types: []};
75+
let sp_path = {node: path, span: sp};
76+
let pathexpr = ast::expr_path(sp_path);
77+
ret @{id: cx.next_id(), node: pathexpr, span: sp};
78+
}
79+
fn make_vec_expr(cx: &ext_ctxt, sp: span, exprs: &[@ast::expr]) ->
80+
@ast::expr {
81+
let vecexpr = ast::expr_vec(exprs, ast::imm);
82+
ret @{id: cx.next_id(), node: vecexpr, span: sp};
83+
}
84+
fn make_call(cx: &ext_ctxt, sp: span, fn_path: &[ast::ident],
85+
args: &[@ast::expr]) -> @ast::expr {
86+
let pathexpr = make_path_expr(cx, sp, fn_path);
87+
let callexpr = ast::expr_call(pathexpr, args);
88+
ret @{id: cx.next_id(), node: callexpr, span: sp};
89+
}
90+
fn make_rec_expr(cx: &ext_ctxt, sp: span,
91+
fields: &[{ident: ast::ident, ex: @ast::expr}]) ->
92+
@ast::expr {
93+
let astfields: [ast::field] = [];
94+
for field: {ident: ast::ident, ex: @ast::expr} in fields {
95+
let ident = field.ident;
96+
let val = field.ex;
97+
let astfield =
98+
{node: {mut: ast::imm, ident: ident, expr: val}, span: sp};
99+
astfields += [astfield];
100+
}
101+
let recexpr = ast::expr_rec(astfields, option::none::<@ast::expr>);
102+
ret @{id: cx.next_id(), node: recexpr, span: sp};
103+
}
104+
fn make_path_vec(cx: &ext_ctxt, ident: &ast::ident) -> [ast::ident] {
105+
fn compiling_std(cx: &ext_ctxt) -> bool {
106+
ret istr::find(cx.crate_file_name(), ~"std.rc") >= 0;
107+
}
108+
if compiling_std(cx) {
109+
ret [~"extifmt", ~"rt", ident];
110+
} else { ret [~"std", ~"extifmt", ~"rt", ident]; }
111+
}
112+
fn make_rt_path_expr(cx: &ext_ctxt, sp: span,
113+
ident: &istr) -> @ast::expr {
114+
let path = make_path_vec(cx, ident);
115+
ret make_path_expr(cx, sp, path);
116+
}
117+
// Produces an AST expression that represents a RT::conv record,
118+
// which tells the RT::conv* functions how to perform the conversion
119+
120+
fn make_rt_conv_expr(cx: &ext_ctxt, sp: span, cnv: &conv) -> @ast::expr {
121+
fn make_flags(cx: &ext_ctxt, sp: span, flags: &[flag]) -> @ast::expr {
122+
let flagexprs: [@ast::expr] = [];
123+
for f: flag in flags {
124+
let fstr;
125+
alt f {
126+
flag_left_justify. { fstr = ~"flag_left_justify"; }
127+
flag_left_zero_pad. { fstr = ~"flag_left_zero_pad"; }
128+
flag_space_for_sign. { fstr = ~"flag_space_for_sign"; }
129+
flag_sign_always. { fstr = ~"flag_sign_always"; }
130+
flag_alternate. { fstr = ~"flag_alternate"; }
131+
}
132+
flagexprs += [make_rt_path_expr(cx, sp, fstr)];
133+
}
134+
// FIXME: 0-length vectors can't have their type inferred
135+
// through the rec that these flags are a member of, so
136+
// this is a hack placeholder flag
137+
138+
if vec::len::<@ast::expr>(flagexprs) == 0u {
139+
flagexprs += [make_rt_path_expr(cx, sp, ~"flag_none")];
140+
}
141+
ret make_vec_expr(cx, sp, flagexprs);
142+
}
143+
fn make_count(cx: &ext_ctxt, sp: span, cnt: &count) -> @ast::expr {
144+
alt cnt {
145+
count_implied. {
146+
ret make_rt_path_expr(cx, sp, ~"count_implied");
147+
}
148+
count_is(c) {
149+
let count_lit = make_new_int(cx, sp, c);
150+
let count_is_path = make_path_vec(cx, ~"count_is");
151+
let count_is_args = [count_lit];
152+
ret make_call(cx, sp, count_is_path, count_is_args);
153+
}
154+
_ { cx.span_unimpl(sp, ~"unimplemented #fmt conversion"); }
155+
}
156+
}
157+
fn make_ty(cx: &ext_ctxt, sp: span, t: &ty) -> @ast::expr {
158+
let rt_type;
159+
alt t {
160+
ty_hex(c) {
161+
alt c {
162+
case_upper. { rt_type = ~"ty_hex_upper"; }
163+
case_lower. { rt_type = ~"ty_hex_lower"; }
164+
}
165+
}
166+
ty_bits. { rt_type = ~"ty_bits"; }
167+
ty_octal. { rt_type = ~"ty_octal"; }
168+
_ { rt_type = ~"ty_default"; }
169+
}
170+
ret make_rt_path_expr(cx, sp, rt_type);
171+
}
172+
fn make_conv_rec(cx: &ext_ctxt, sp: span, flags_expr: @ast::expr,
173+
width_expr: @ast::expr, precision_expr: @ast::expr,
174+
ty_expr: @ast::expr) -> @ast::expr {
175+
ret make_rec_expr(cx, sp,
176+
[{ident: ~"flags", ex: flags_expr},
177+
{ident: ~"width", ex: width_expr},
178+
{ident: ~"precision", ex: precision_expr},
179+
{ident: ~"ty", ex: ty_expr}]);
180+
}
181+
let rt_conv_flags = make_flags(cx, sp, cnv.flags);
182+
let rt_conv_width = make_count(cx, sp, cnv.width);
183+
let rt_conv_precision = make_count(cx, sp, cnv.precision);
184+
let rt_conv_ty = make_ty(cx, sp, cnv.ty);
185+
ret make_conv_rec(cx, sp, rt_conv_flags, rt_conv_width,
186+
rt_conv_precision, rt_conv_ty);
187+
}
188+
fn make_conv_call(cx: &ext_ctxt, sp: span, conv_type: &istr,
189+
cnv: &conv, arg: @ast::expr) -> @ast::expr {
190+
let fname = ~"conv_" + conv_type;
191+
let path = make_path_vec(cx, fname);
192+
let cnv_expr = make_rt_conv_expr(cx, sp, cnv);
193+
let args = [cnv_expr, arg];
194+
ret make_call(cx, arg.span, path, args);
195+
}
196+
fn make_new_conv(cx: &ext_ctxt, sp: span, cnv: conv, arg: @ast::expr) ->
197+
@ast::expr {
198+
// FIXME: Extract all this validation into extfmt::ct
199+
200+
fn is_signed_type(cnv: conv) -> bool {
201+
alt cnv.ty {
202+
ty_int(s) {
203+
alt s { signed. { ret true; } unsigned. { ret false; } }
204+
}
205+
_ { ret false; }
206+
}
207+
}
208+
let unsupported = ~"conversion not supported in #fmt string";
209+
alt cnv.param {
210+
option::none. { }
211+
_ { cx.span_unimpl(sp, unsupported); }
212+
}
213+
for f: flag in cnv.flags {
214+
alt f {
215+
flag_left_justify. { }
216+
flag_sign_always. {
217+
if !is_signed_type(cnv) {
218+
cx.span_fatal(sp,
219+
~"+ flag only valid in " +
220+
~"signed #fmt conversion");
221+
}
222+
}
223+
flag_space_for_sign. {
224+
if !is_signed_type(cnv) {
225+
cx.span_fatal(sp,
226+
~"space flag only valid in " +
227+
~"signed #fmt conversions");
228+
}
229+
}
230+
flag_left_zero_pad. { }
231+
_ { cx.span_unimpl(sp, unsupported); }
232+
}
233+
}
234+
alt cnv.width {
235+
count_implied. { }
236+
count_is(_) { }
237+
_ { cx.span_unimpl(sp, unsupported); }
238+
}
239+
alt cnv.precision {
240+
count_implied. { }
241+
count_is(_) { }
242+
_ { cx.span_unimpl(sp, unsupported); }
243+
}
244+
alt cnv.ty {
245+
ty_str. { ret make_conv_call(cx, arg.span, ~"str", cnv, arg); }
246+
ty_int(sign) {
247+
alt sign {
248+
signed. { ret make_conv_call(cx, arg.span, ~"int", cnv, arg); }
249+
unsigned. {
250+
ret make_conv_call(cx, arg.span, ~"uint", cnv, arg);
251+
}
252+
}
253+
}
254+
ty_bool. { ret make_conv_call(cx, arg.span, ~"bool", cnv, arg); }
255+
ty_char. { ret make_conv_call(cx, arg.span, ~"char", cnv, arg); }
256+
ty_hex(_) { ret make_conv_call(cx, arg.span, ~"uint", cnv, arg); }
257+
ty_bits. { ret make_conv_call(cx, arg.span, ~"uint", cnv, arg); }
258+
ty_octal. { ret make_conv_call(cx, arg.span, ~"uint", cnv, arg); }
259+
_ { cx.span_unimpl(sp, unsupported); }
260+
}
261+
}
262+
fn log_conv(c: conv) {
263+
alt c.param {
264+
some(p) {
265+
log "param: "
266+
+ istr::to_estr(std::int::to_str(p, 10u));
267+
}
268+
_ { log "param: none"; }
269+
}
270+
for f: flag in c.flags {
271+
alt f {
272+
flag_left_justify. { log "flag: left justify"; }
273+
flag_left_zero_pad. { log "flag: left zero pad"; }
274+
flag_space_for_sign. { log "flag: left space pad"; }
275+
flag_sign_always. { log "flag: sign always"; }
276+
flag_alternate. { log "flag: alternate"; }
277+
}
278+
}
279+
alt c.width {
280+
count_is(i) { log "width: count is "
281+
+ istr::to_estr(std::int::to_str(i, 10u)); }
282+
count_is_param(i) {
283+
log "width: count is param "
284+
+ istr::to_estr(std::int::to_str(i, 10u));
285+
}
286+
count_is_next_param. { log "width: count is next param"; }
287+
count_implied. { log "width: count is implied"; }
288+
}
289+
alt c.precision {
290+
count_is(i) { log "prec: count is "
291+
+ istr::to_estr(std::int::to_str(i, 10u)); }
292+
count_is_param(i) {
293+
log "prec: count is param "
294+
+ istr::to_estr(std::int::to_str(i, 10u));
295+
}
296+
count_is_next_param. { log "prec: count is next param"; }
297+
count_implied. { log "prec: count is implied"; }
298+
}
299+
alt c.ty {
300+
ty_bool. { log "type: bool"; }
301+
ty_str. { log "type: str"; }
302+
ty_char. { log "type: char"; }
303+
ty_int(s) {
304+
alt s {
305+
signed. { log "type: signed"; }
306+
unsigned. { log "type: unsigned"; }
307+
}
308+
}
309+
ty_bits. { log "type: bits"; }
310+
ty_hex(cs) {
311+
alt cs {
312+
case_upper. { log "type: uhex"; }
313+
case_lower. { log "type: lhex"; }
314+
}
315+
}
316+
ty_octal. { log "type: octal"; }
317+
}
318+
}
319+
let fmt_sp = args[0].span;
320+
let n = 0u;
321+
let tmp_expr = make_new_str(cx, sp, ~"");
322+
let nargs = vec::len::<@ast::expr>(args);
323+
for pc: piece in pieces {
324+
alt pc {
325+
piece_string(s) {
326+
let s_expr = make_new_str(cx, fmt_sp, s);
327+
tmp_expr = make_add_expr(cx, fmt_sp, tmp_expr, s_expr);
328+
}
329+
piece_conv(conv) {
330+
n += 1u;
331+
if n >= nargs {
332+
cx.span_fatal(sp,
333+
~"not enough arguments to #fmt " +
334+
~"for the given format string");
335+
}
336+
log "Building conversion:";
337+
log_conv(conv);
338+
let arg_expr = args[n];
339+
let c_expr = make_new_conv(cx, fmt_sp, conv, arg_expr);
340+
tmp_expr = make_add_expr(cx, fmt_sp, tmp_expr, c_expr);
341+
}
342+
}
343+
}
344+
let expected_nargs = n + 1u; // n conversions + the fmt string
345+
346+
if expected_nargs < nargs {
347+
cx.span_fatal(
348+
sp, istr::from_estr(
349+
#fmt["too many arguments to #fmt. found %u, expected %u",
350+
nargs, expected_nargs]));
351+
}
352+
ret tmp_expr;
353+
}
354+
//
355+
// Local Variables:
356+
// mode: rust
357+
// fill-column: 78;
358+
// indent-tabs-mode: nil
359+
// c-basic-offset: 4
360+
// buffer-file-coding-system: utf-8-unix
361+
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
362+
// End:
363+
//

0 commit comments

Comments
 (0)