diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs index 4ed513a3e06bd..46f5716861abd 100644 --- a/src/comp/front/ast.rs +++ b/src/comp/front/ast.rs @@ -185,7 +185,7 @@ tag expr_ { expr_field(@expr, ident, ann); expr_index(@expr, @expr, ann); expr_path(path, option.t[def], ann); - expr_ext(path, vec[@expr], option.t[@expr], ann); + expr_ext(path, vec[@expr], option.t[@expr], @expr, ann); expr_fail; expr_ret(option.t[@expr]); expr_put(option.t[@expr]); diff --git a/src/comp/front/extfmt.rs b/src/comp/front/extfmt.rs new file mode 100644 index 0000000000000..3f1e5c3039228 --- /dev/null +++ b/src/comp/front/extfmt.rs @@ -0,0 +1,568 @@ +/* The 'fmt' extension is modeled on the posix printf system. + * + * A posix conversion ostensibly looks like this: + * + * %[parameter][flags][width][.precision][length]type + * + * Given the different numeric type bestiary we have, we omit the 'length' + * parameter and support slightly different conversions for 'type': + * + * %[parameter][flags][width][.precision]type + * + * we also only support translating-to-rust a tiny subset of the possible + * combinations at the moment. + */ + +import util.common; + +import std._str; +import std._vec; +import std.option; +import std.option.none; +import std.option.some; + +export expand_syntax_ext; + +tag signedness { + signed; + unsigned; +} + +tag caseness { + case_upper; + case_lower; +} + +tag ty { + ty_bool; + ty_str; + ty_char; + ty_int(signedness); + ty_bits; + ty_hex(caseness); + // FIXME: More types +} + +tag flag { + flag_left_justify; + flag_left_zero_pad; + flag_left_space_pad; + flag_plus_if_positive; + flag_alternate; +} + +tag count { + count_is(int); + count_is_param(int); + count_is_next_param; + count_implied; +} + +// A formatted conversion from an expression to a string +type conv = rec(option.t[int] param, + vec[flag] flags, + count width, + count precision, + ty ty); + +// A fragment of the output sequence +tag piece { + piece_string(str); + piece_conv(conv); +} + +// TODO: Need to thread parser through here to handle errors correctly +fn expand_syntax_ext(vec[@ast.expr] args, + option.t[@ast.expr] body) -> @ast.expr { + + if (_vec.len[@ast.expr](args) == 0u) { + log "malformed #fmt call"; + fail; + } + + auto fmt = expr_to_str(args.(0)); + + log "Format string:"; + log fmt; + + auto pieces = parse_fmt_string(fmt); + auto args_len = _vec.len[@ast.expr](args); + auto fmt_args = _vec.slice[@ast.expr](args, 1u, args_len - 1u); + ret pieces_to_expr(pieces, args); +} + +fn expr_to_str(@ast.expr expr) -> str { + alt (expr.node) { + case (ast.expr_lit(?l, _)) { + alt (l.node) { + case (ast.lit_str(?s)) { + ret s; + } + } + } + } + log "malformed #fmt call"; + fail; +} + +fn parse_fmt_string(str s) -> vec[piece] { + let vec[piece] pieces = vec(); + // FIXME: Should be counting codepoints instead of bytes + auto lim = _str.byte_len(s); + auto buf = ""; + + fn flush_buf(str buf, &vec[piece] pieces) -> str { + if (_str.byte_len(buf) > 0u) { + auto piece = piece_string(buf); + pieces += piece; + } + ret ""; + } + + auto i = 0u; + while (i < lim) { + auto curr = _str.substr(s, i, 1u); + if (_str.eq(curr, "%")) { + i += 1u; + if (i >= lim) { + log "unterminated conversion at end of string"; + fail; + } + auto curr2 = _str.substr(s, i, 1u); + if (_str.eq(curr2, "%")) { + i += 1u; + } else { + buf = flush_buf(buf, pieces); + auto res = parse_conversion(s, i, lim); + pieces += res._0; + i = res._1; + } + } else { + buf += curr; + i += 1u; + } + } + buf = flush_buf(buf, pieces); + ret pieces; +} + +fn peek_num(str s, uint i, uint lim) -> option.t[tup(uint, uint)] { + if (i >= lim) { + ret none[tup(uint, uint)]; + } + + // FIXME: Presumably s.(i) will return char eventually + auto c = s.(i); + if (!('0' as u8 <= c && c <= '9' as u8)) { + ret option.none[tup(uint, uint)]; + } + + auto n = (c - ('0' as u8)) as uint; + alt (peek_num(s, i + 1u, lim)) { + case (none[tup(uint, uint)]) { + ret some[tup(uint, uint)](tup(n, i + 1u)); + } + case (some[tup(uint, uint)](?next)) { + auto m = next._0; + auto j = next._1; + ret some[tup(uint, uint)](tup(n * 10u + m, j)); + } + } + +} + +fn parse_conversion(str s, uint i, uint lim) -> tup(piece, uint) { + auto parm = parse_parameter(s, i, lim); + auto flags = parse_flags(s, parm._1, lim); + auto width = parse_count(s, flags._1, lim); + auto prec = parse_precision(s, width._1, lim); + auto ty = parse_type(s, prec._1, lim); + ret tup(piece_conv(rec(param = parm._0, + flags = flags._0, + width = width._0, + precision = prec._0, + ty = ty._0)), + ty._1); +} + +fn parse_parameter(str s, uint i, uint lim) -> tup(option.t[int], uint) { + if (i >= lim) { + ret tup(none[int], i); + } + + auto num = peek_num(s, i, lim); + alt (num) { + case (none[tup(uint, uint)]) { + ret tup(none[int], i); + } + case (some[tup(uint, uint)](?t)) { + auto n = t._0; + auto j = t._1; + if (j < lim && s.(j) == '$' as u8) { + ret tup(some[int](n as int), j + 1u); + } + else { + ret tup(none[int], i); + } + } + } +} + +fn parse_flags(str s, uint i, uint lim) -> tup(vec[flag], uint) { + let vec[flag] noflags = vec(); + + if (i >= lim) { + ret tup(noflags, i); + } + + fn more_(flag f, str s, uint i, uint lim) -> tup(vec[flag], uint) { + auto next = parse_flags(s, i + 1u, lim); + auto rest = next._0; + auto j = next._1; + let vec[flag] curr = vec(f); + ret tup(curr + rest, j); + } + + auto more = bind more_(_, s, i, lim); + + auto f = s.(i); + if (f == ('-' as u8)) { + ret more(flag_left_justify); + } else if (f == ('0' as u8)) { + ret more(flag_left_zero_pad); + } else if (f == (' ' as u8)) { + ret more(flag_left_space_pad); + } else if (f == ('+' as u8)) { + ret more(flag_plus_if_positive); + } else if (f == ('#' as u8)) { + ret more(flag_alternate); + } else { + ret tup(noflags, i); + } +} + +fn parse_count(str s, uint i, uint lim) -> tup(count, uint) { + if (i >= lim) { + ret tup(count_implied, i); + } + + // FIXME: These inner functions are just to avoid a rustboot + // "Unsatisfied precondition constraint" bug with alts nested in ifs + fn parse_star_count(str s, uint i, uint lim) -> tup(count, uint) { + auto param = parse_parameter(s, i + 1u, lim); + auto j = param._1; + alt (param._0) { + case (none[int]) { + ret tup(count_is_next_param, j); + } + case (some[int](?n)) { + ret tup(count_is_param(n), j); + } + } + } + + fn parse_count_(str s, uint i, uint lim) -> tup(count, uint) { + auto num = peek_num(s, i, lim); + alt (num) { + case (none[tup(uint, uint)]) { + ret tup(count_implied, i); + } + case (some[tup(uint, uint)](?num)) { + ret tup(count_is(num._0 as int), num._1); + } + } + } + + if (s.(i) == ('*' as u8)) { + ret parse_star_count(s, i, lim); + } else { + ret parse_count_(s, i, lim); + } +} + +fn parse_precision(str s, uint i, uint lim) -> tup(count, uint) { + if (i >= lim) { + ret tup(count_implied, i); + } + + if (s.(i) == '.' as u8) { + ret parse_count(s, i + 1u, lim); + } else { + ret tup(count_implied, i); + } +} + +fn parse_type(str s, uint i, uint lim) -> tup(ty, uint) { + if (i >= lim) { + log "missing type in conversion"; + fail; + } + + auto t; + auto tstr = _str.substr(s, i, 1u); + if (_str.eq(tstr, "b")) { + t = ty_bool; + } else if (_str.eq(tstr, "s")) { + t = ty_str; + } else if (_str.eq(tstr, "c")) { + t = ty_char; + } else if (_str.eq(tstr, "d") + || _str.eq(tstr, "i")) { + // TODO: Do we really want two signed types here? + // How important is it to be printf compatible? + t = ty_int(signed); + } else if (_str.eq(tstr, "u")) { + t = ty_int(unsigned); + } else if (_str.eq(tstr, "x")) { + t = ty_hex(case_lower); + } else if (_str.eq(tstr, "X")) { + t = ty_hex(case_upper); + } else if (_str.eq(tstr, "t")) { + t = ty_bits; + } else { + // FIXME: This is a hack to avoid 'unsatisfied precondition + // constraint' on uninitialized variable t below + t = ty_bool; + log "unknown type in conversion"; + fail; + } + + ret tup(t, i + 1u); +} + +fn pieces_to_expr(vec[piece] pieces, vec[@ast.expr] args) -> @ast.expr { + + fn make_new_lit(common.span sp, ast.lit_ lit) -> @ast.expr { + auto sp_lit = @parser.spanned[ast.lit_](sp, sp, lit); + auto expr = ast.expr_lit(sp_lit, ast.ann_none); + ret @parser.spanned[ast.expr_](sp, sp, expr); + } + + fn make_new_str(common.span sp, str s) -> @ast.expr { + auto lit = ast.lit_str(s); + ret make_new_lit(sp, lit); + } + + fn make_new_uint(common.span sp, uint u) -> @ast.expr { + auto lit = ast.lit_uint(u); + ret make_new_lit(sp, lit); + } + + fn make_add_expr(common.span sp, + @ast.expr lhs, @ast.expr rhs) -> @ast.expr { + auto binexpr = ast.expr_binary(ast.add, lhs, rhs, ast.ann_none); + ret @parser.spanned[ast.expr_](sp, sp, binexpr); + } + + fn make_call(common.span sp, vec[ast.ident] fn_path, + vec[@ast.expr] args) -> @ast.expr { + let vec[ast.ident] path_idents = fn_path; + let vec[@ast.ty] path_types = vec(); + auto path = rec(idents = path_idents, types = path_types); + auto sp_path = parser.spanned[ast.path_](sp, sp, path); + auto pathexpr = ast.expr_path(sp_path, none[ast.def], ast.ann_none); + auto sp_pathexpr = @parser.spanned[ast.expr_](sp, sp, pathexpr); + auto callexpr = ast.expr_call(sp_pathexpr, args, ast.ann_none); + auto sp_callexpr = @parser.spanned[ast.expr_](sp, sp, callexpr); + ret sp_callexpr; + } + + fn make_new_conv(conv cnv, @ast.expr arg) -> @ast.expr { + + auto unsupported = "conversion not supported in #fmt string"; + + alt (cnv.param) { + case (option.none[int]) { + } + case (_) { + log unsupported; + fail; + } + } + + if (_vec.len[flag](cnv.flags) != 0u) { + log unsupported; + fail; + } + + alt (cnv.width) { + case (count_implied) { + } + case (_) { + log unsupported; + fail; + } + } + + alt (cnv.precision) { + case (count_implied) { + } + case (_) { + log unsupported; + fail; + } + } + + alt (cnv.ty) { + case (ty_str) { + ret arg; + } + case (ty_int(?sign)) { + alt (sign) { + case (signed) { + let vec[str] path = vec("std", "_int", "to_str"); + auto radix_expr = make_new_uint(arg.span, 10u); + let vec[@ast.expr] args = vec(arg, radix_expr); + ret make_call(arg.span, path, args); + } + case (unsigned) { + let vec[str] path = vec("std", "_uint", "to_str"); + auto radix_expr = make_new_uint(arg.span, 10u); + let vec[@ast.expr] args = vec(arg, radix_expr); + ret make_call(arg.span, path, args); + } + } + } + case (_) { + log unsupported; + fail; + } + } + } + + fn log_conv(conv c) { + alt (c.param) { + case (some[int](?p)) { + log "param: " + std._int.to_str(p, 10u); + } + case (_) { + log "param: none"; + } + } + for (flag f in c.flags) { + alt (f) { + case (flag_left_justify) { + log "flag: left justify"; + } + case (flag_left_zero_pad) { + log "flag: left zero pad"; + } + case (flag_left_space_pad) { + log "flag: left space pad"; + } + case (flag_plus_if_positive) { + log "flag: plus if positive"; + } + case (flag_alternate) { + log "flag: alternate"; + } + } + } + alt (c.width) { + case (count_is(?i)) { + log "width: count is " + std._int.to_str(i, 10u); + } + case (count_is_param(?i)) { + log "width: count is param " + std._int.to_str(i, 10u); + } + case (count_is_next_param) { + log "width: count is next param"; + } + case (count_implied) { + log "width: count is implied"; + } + } + alt (c.precision) { + case (count_is(?i)) { + log "prec: count is " + std._int.to_str(i, 10u); + } + case (count_is_param(?i)) { + log "prec: count is param " + std._int.to_str(i, 10u); + } + case (count_is_next_param) { + log "prec: count is next param"; + } + case (count_implied) { + log "prec: count is implied"; + } + } + alt (c.ty) { + case (ty_bool) { + log "type: bool"; + } + case (ty_str) { + log "type: str"; + } + case (ty_char) { + log "type: char"; + } + case (ty_int(?s)) { + alt (s) { + case (signed) { + log "type: signed"; + } + case (unsigned) { + log "type: unsigned"; + } + } + } + case (ty_bits) { + log "type: bits"; + } + case (ty_hex(?cs)) { + alt (cs) { + case (case_upper) { + log "type: uhex"; + } + case (case_lower) { + log "type: lhex"; + } + } + } + } + } + + auto sp = args.(0).span; + auto n = 0u; + auto tmp_expr = make_new_str(sp, ""); + + for (piece p in pieces) { + alt (p) { + case (piece_string(?s)) { + auto s_expr = make_new_str(sp, s); + tmp_expr = make_add_expr(sp, tmp_expr, s_expr); + } + case (piece_conv(?conv)) { + if (n >= _vec.len[@ast.expr](args)) { + log "too many conversions in #fmt string"; + fail; + } + + // TODO: Remove debug logging + log "Building conversion:"; + log_conv(conv); + + n += 1u; + auto arg_expr = args.(n); + auto c_expr = make_new_conv(conv, arg_expr); + tmp_expr = make_add_expr(sp, tmp_expr, c_expr); + } + } + } + + // TODO: Remove this debug logging + log "dumping expanded ast:"; + log pretty.print_expr(tmp_expr); + ret tmp_expr; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// diff --git a/src/comp/front/parser.rs b/src/comp/front/parser.rs index f747c084b5bdc..50545a2f9b09d 100644 --- a/src/comp/front/parser.rs +++ b/src/comp/front/parser.rs @@ -656,7 +656,8 @@ impure fn parse_bottom_expr(parser p) -> @ast.expr { some(token.COMMA), pf, p); hi = es.span; - ex = ast.expr_ext(pth, es.node, none[@ast.expr], ast.ann_none); + ex = expand_syntax_ext(p, es.span, pth, es.node, + none[@ast.expr]); } case (token.FAIL) { @@ -736,6 +737,34 @@ impure fn parse_bottom_expr(parser p) -> @ast.expr { ret @spanned(lo, hi, ex); } +/* + * FIXME: This is a crude approximation of the syntax-extension system, + * for purposes of prototyping and/or hard-wiring any extensions we + * wish to use while bootstrapping. The eventual aim is to permit + * loading rust crates to process extensions, but this will likely + * require a rust-based frontend, or an ocaml-FFI-based connection to + * rust crates. At the moment we have neither. + */ + +impure fn expand_syntax_ext(parser p, ast.span sp, + &ast.path path, vec[@ast.expr] args, + option.t[@ast.expr] body) -> ast.expr_ { + + check (_vec.len[ast.ident](path.node.idents) > 0u); + auto extname = path.node.idents.(0); + if (_str.eq(extname, "fmt")) { + auto expanded = extfmt.expand_syntax_ext(args, body); + auto newexpr = ast.expr_ext(path, args, body, + expanded, + ast.ann_none); + + ret newexpr; + } else { + p.err("unknown syntax extension"); + fail; + } +} + impure fn extend_expr_by_ident(parser p, span lo, span hi, @ast.expr e, ast.ident i) -> @ast.expr { auto e_ = e.node; diff --git a/src/comp/front/pretty.rs b/src/comp/front/pretty.rs new file mode 100644 index 0000000000000..267763e39972c --- /dev/null +++ b/src/comp/front/pretty.rs @@ -0,0 +1,85 @@ +import std._int; +import std._str; +import std._uint; +import std._vec; + +export print_expr; + +fn unknown() -> str { + ret ""; +} + +fn print_expr(@ast.expr expr) -> str { + alt (expr.node) { + case (ast.expr_lit(?lit, _)) { + ret print_lit(lit); + } + case (ast.expr_binary(?op, ?lhs, ?rhs, _)) { + ret print_expr_binary(op, lhs, rhs); + } + case (ast.expr_call(?path, ?args, _)) { + ret print_expr_call(path, args); + } + case (ast.expr_path(?path, _, _)) { + ret print_path(path); + } + case (_) { + ret unknown(); + } + } +} + +fn print_lit(@ast.lit lit) -> str { + alt (lit.node) { + case (ast.lit_str(?s)) { + ret "\"" + s + "\""; + } + case (ast.lit_int(?i)) { + ret _int.to_str(i, 10u); + } + case (ast.lit_uint(?u)) { + ret _uint.to_str(u, 10u); + } + case (_) { + ret unknown(); + } + } +} + +fn print_expr_binary(ast.binop op, @ast.expr lhs, @ast.expr rhs) -> str { + alt (op) { + case (ast.add) { + auto l = print_expr(lhs); + auto r = print_expr(rhs); + ret l + " + " + r; + } + } +} + +fn print_expr_call(@ast.expr path_expr, vec[@ast.expr] args) -> str { + auto s = print_expr(path_expr); + + s += "("; + fn print_expr_ref(&@ast.expr e) -> str { ret print_expr(e); } + auto mapfn = print_expr_ref; + auto argstrs = _vec.map[@ast.expr, str](mapfn, args); + s += _str.connect(argstrs, ", "); + s += ")"; + + ret s; +} + +fn print_path(ast.path path) -> str { + ret _str.connect(path.node.idents, "."); +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// diff --git a/src/comp/middle/fold.rs b/src/comp/middle/fold.rs index 0978307025294..c7041b263542b 100644 --- a/src/comp/middle/fold.rs +++ b/src/comp/middle/fold.rs @@ -154,6 +154,12 @@ type ast_fold[ENV] = &option.t[def] d, ann a) -> @expr) fold_expr_path, + (fn(&ENV e, &span sp, + &path p, vec[@expr] args, + option.t[@expr] body, + @expr expanded, + ann a) -> @expr) fold_expr_ext, + (fn(&ENV e, &span sp) -> @expr) fold_expr_fail, (fn(&ENV e, &span sp, @@ -644,6 +650,14 @@ fn fold_expr[ENV](&ENV env, ast_fold[ENV] fld, &@expr e) -> @expr { ret fld.fold_expr_path(env_, e.span, p_, r, t); } + case (ast.expr_ext(?p, ?args, ?body, ?expanded, ?t)) { + // Only fold the expanded expression, not the + // expressions involved in syntax extension + auto exp = fold_expr(env_, fld, expanded); + ret fld.fold_expr_ext(env_, e.span, p, args, body, + exp, t); + } + case (ast.expr_fail) { ret fld.fold_expr_fail(env_, e.span); } @@ -1166,6 +1180,14 @@ fn identity_fold_expr_path[ENV](&ENV env, &span sp, ret @respan(sp, ast.expr_path(p, d, a)); } +fn identity_fold_expr_ext[ENV](&ENV env, &span sp, + &path p, vec[@expr] args, + option.t[@expr] body, + @expr expanded, + ann a) -> @expr { + ret @respan(sp, ast.expr_ext(p, args, body, expanded, a)); +} + fn identity_fold_expr_fail[ENV](&ENV env, &span sp) -> @expr { ret @respan(sp, ast.expr_fail); } @@ -1447,6 +1469,7 @@ fn new_identity_fold[ENV]() -> ast_fold[ENV] { fold_expr_field = bind identity_fold_expr_field[ENV](_,_,_,_,_), fold_expr_index = bind identity_fold_expr_index[ENV](_,_,_,_,_), fold_expr_path = bind identity_fold_expr_path[ENV](_,_,_,_,_), + fold_expr_ext = bind identity_fold_expr_ext[ENV](_,_,_,_,_,_,_), fold_expr_fail = bind identity_fold_expr_fail[ENV](_,_), fold_expr_ret = bind identity_fold_expr_ret[ENV](_,_,_), fold_expr_put = bind identity_fold_expr_put[ENV](_,_,_), diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index e961acb4e9191..2594590a4b91a 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -3693,6 +3693,10 @@ fn trans_expr(@block_ctxt cx, @ast.expr e) -> result { ret trans_rec(cx, args, base, ann); } + case (ast.expr_ext(_, _, _, ?expanded, _)) { + ret trans_expr(cx, expanded); + } + case (ast.expr_fail) { ret trans_fail(cx, e.span, "explicit failure"); } diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index 8b9ef6d146f7f..478f54d1ab4f9 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -749,6 +749,7 @@ fn expr_ty(@ast.expr expr) -> @t { case (ast.expr_field(_, _, ?ann)) { ret ann_to_type(ann); } case (ast.expr_index(_, _, ?ann)) { ret ann_to_type(ann); } case (ast.expr_path(_, _, ?ann)) { ret ann_to_type(ann); } + case (ast.expr_ext(_, _, _, _, ?ann)) { ret ann_to_type(ann); } case (ast.expr_fail) { ret plain_ty(ty_nil); } case (ast.expr_log(_)) { ret plain_ty(ty_nil); } diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index ce1c59ec01f78..f8f7fc72719c2 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -1156,6 +1156,11 @@ fn demand_expr_full(&@fn_ctxt fcx, @ty.t expected, @ast.expr e, ann_to_type(ann), adk); e_1 = ast.expr_path(pth, d, ast.ann_type(t)); } + case (ast.expr_ext(?p, ?args, ?body, ?expanded, ?ann)) { + auto t = demand_full(fcx, e.span, expected, + ann_to_type(ann), adk); + e_1 = ast.expr_ext(p, args, body, expanded, ast.ann_type(t)); + } case (ast.expr_fail) { e_1 = e.node; } case (ast.expr_log(_)) { e_1 = e.node; } case (ast.expr_ret(_)) { e_1 = e.node; } @@ -1508,6 +1513,14 @@ fn check_expr(&@fn_ctxt fcx, @ast.expr expr) -> @ast.expr { ast.ann_type(t))); } + case (ast.expr_ext(?p, ?args, ?body, ?expanded, _)) { + auto exp_ = check_expr(fcx, expanded); + auto t = expr_ty(exp_); + ret @fold.respan[ast.expr_](expr.span, + ast.expr_ext(p, args, body, exp_, + ast.ann_type(t))); + } + case (ast.expr_fail) { ret expr; } diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index bc4aaa52c1aef..43a0411768085 100644 --- a/src/comp/rustc.rc +++ b/src/comp/rustc.rc @@ -5,8 +5,10 @@ use std; mod front { mod ast; + mod extfmt; mod lexer; mod parser; + mod pretty; mod token; mod eval; } diff --git a/src/rt/memory_region.cpp b/src/rt/memory_region.cpp index 7ede3761e9a3e..fb19620f8b951 100644 --- a/src/rt/memory_region.cpp +++ b/src/rt/memory_region.cpp @@ -1,7 +1,7 @@ #include "rust_internal.h" #include "memory_region.h" -// #define TRACK_ALLOCATIONS +#define TRACK_ALLOCATIONS memory_region::memory_region(rust_srv *srv, bool synchronized) : _srv(srv), _parent(NULL), _live_allocations(0), diff --git a/src/test/run-pass/syntax-extension-fmt.rs b/src/test/run-pass/syntax-extension-fmt.rs index 65e7647ee8b50..ebb09f96ba2c9 100644 --- a/src/test/run-pass/syntax-extension-fmt.rs +++ b/src/test/run-pass/syntax-extension-fmt.rs @@ -1,5 +1,16 @@ use std; +import std._str; + +fn test(str actual, str expected) { + log actual; + log expected; + check (_str.eq(actual, expected)); +} + fn main() { - auto s = #fmt("hello %d friends and %s things", 10, "formatted"); - log s; + test(#fmt("hello %d friends and %s things", 10, "formatted"), + "hello 10 friends and formatted things"); + test(#fmt("d: %d", 1), "d: 1"); + test(#fmt("i: %i", 2), "i: 2"); + test(#fmt("s: %s", "test"), "s: test"); }